blob: 85f7b68e2f82e4565010d445457e192e0b9d7021 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * mark.c: functions for setting marks and jumping to them
12 */
13
14#include "vim.h"
15
16/*
17 * This file contains routines to maintain and manipulate marks.
18 */
19
20/*
21 * If a named file mark's lnum is non-zero, it is valid.
22 * If a named file mark's fnum is non-zero, it is for an existing buffer,
23 * otherwise it is from .viminfo and namedfm[n].fname is the file name.
24 * There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing
25 * viminfo).
26 */
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010027static xfmark_T namedfm[NMARKS + EXTRA_MARKS]; // marks with file nr
Bram Moolenaar071d4272004-06-13 20:20:40 +000028
Bram Moolenaar5843f5f2019-08-20 20:13:45 +020029static void fname2fnum(xfmark_T *fm);
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010030static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf);
31static char_u *mark_line(pos_T *mp, int lead_len);
32static void show_one_mark(int, char_u *, pos_T *, char_u *, int current);
Bram Moolenaar88d298a2017-03-14 21:53:58 +010033static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
34 long amount_after, int adjust_folds);
Bram Moolenaar071d4272004-06-13 20:20:40 +000035
36/*
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000037 * Set named mark "c" at current cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 * Returns OK on success, FAIL if bad name given.
39 */
40 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +010041setmark(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +000042{
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000043 return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
44}
45
46/*
47 * Set named mark "c" to position "pos".
48 * When "c" is upper case use file "fnum".
49 * Returns OK on success, FAIL if bad name given.
50 */
51 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +010052setmark_pos(int c, pos_T *pos, int fnum)
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000053{
Bram Moolenaar071d4272004-06-13 20:20:40 +000054 int i;
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010055 buf_T *buf;
Bram Moolenaar071d4272004-06-13 20:20:40 +000056
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010057 // Check for a special key (may cause islower() to crash).
Bram Moolenaar071d4272004-06-13 20:20:40 +000058 if (c < 0)
59 return FAIL;
60
61 if (c == '\'' || c == '`')
62 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000063 if (pos == &curwin->w_cursor)
64 {
65 setpcmark();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010066 // keep it even when the cursor doesn't move
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000067 curwin->w_prev_pcmark = curwin->w_pcmark;
68 }
69 else
70 curwin->w_pcmark = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000071 return OK;
72 }
73
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010074 buf = buflist_findnr(fnum);
75 if (buf == NULL)
76 return FAIL;
77
Bram Moolenaar08250432008-02-13 11:42:46 +000078 if (c == '"')
79 {
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010080 buf->b_last_cursor = *pos;
Bram Moolenaar08250432008-02-13 11:42:46 +000081 return OK;
82 }
83
Bram Moolenaar4ba37b52019-12-04 21:57:43 +010084 // Allow setting '[ and '] for an autocommand that simulates reading a
85 // file.
Bram Moolenaar071d4272004-06-13 20:20:40 +000086 if (c == '[')
87 {
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010088 buf->b_op_start = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000089 return OK;
90 }
91 if (c == ']')
92 {
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010093 buf->b_op_end = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000094 return OK;
95 }
96
Bram Moolenaarbc88a272013-08-02 17:22:23 +020097 if (c == '<' || c == '>')
Bram Moolenaar0306ac32012-07-06 17:51:28 +020098 {
Bram Moolenaarbc88a272013-08-02 17:22:23 +020099 if (c == '<')
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100100 buf->b_visual.vi_start = *pos;
Bram Moolenaarbc88a272013-08-02 17:22:23 +0200101 else
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100102 buf->b_visual.vi_end = *pos;
103 if (buf->b_visual.vi_mode == NUL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100104 // Visual_mode has not yet been set, use a sane default.
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100105 buf->b_visual.vi_mode = 'v';
Bram Moolenaar0306ac32012-07-06 17:51:28 +0200106 return OK;
107 }
Bram Moolenaar0306ac32012-07-06 17:51:28 +0200108
Bram Moolenaar2d358992016-06-12 21:20:54 +0200109 if (ASCII_ISLOWER(c))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000110 {
111 i = c - 'a';
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100112 buf->b_namedm[i] = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000113 return OK;
114 }
Bram Moolenaar2d358992016-06-12 21:20:54 +0200115 if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000116 {
Bram Moolenaar2d358992016-06-12 21:20:54 +0200117 if (VIM_ISDIGIT(c))
118 i = c - '0' + NMARKS;
119 else
120 i = c - 'A';
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000121 namedfm[i].fmark.mark = *pos;
122 namedfm[i].fmark.fnum = fnum;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100123 VIM_CLEAR(namedfm[i].fname);
Bram Moolenaar2d358992016-06-12 21:20:54 +0200124#ifdef FEAT_VIMINFO
125 namedfm[i].time_set = vim_time();
126#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000127 return OK;
128 }
129 return FAIL;
130}
131
132/*
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200133 * Delete every entry referring to file 'fnum' from both the jumplist and the
134 * tag stack.
135 */
136 void
137mark_forget_file(win_T *wp, int fnum)
138{
139 int i;
140
141 for (i = 0; i < wp->w_jumplistlen; ++i)
142 if (wp->w_jumplist[i].fmark.fnum == fnum)
143 {
144 vim_free(wp->w_jumplist[i].fname);
145 mch_memmove(&wp->w_jumplist[i], &wp->w_jumplist[i + 1],
146 (wp->w_jumplistlen - i - 1) * sizeof(xfmark_T));
147 if (wp->w_jumplistidx > i)
148 --wp->w_jumplistidx;
149 --wp->w_jumplistlen;
150 --i;
151 }
152
153 for (i = 0; i < wp->w_tagstacklen; i++)
154 if (wp->w_tagstack[i].fmark.fnum == fnum)
155 {
156 tagstack_clear_entry(&wp->w_tagstack[i]);
157 mch_memmove(&wp->w_tagstack[i], &wp->w_tagstack[i + 1],
158 (wp->w_tagstacklen - i - 1) * sizeof(taggy_T));
159 if (wp->w_tagstackidx > i)
160 --wp->w_tagstackidx;
161 --wp->w_tagstacklen;
162 --i;
163 }
164}
165
166/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000167 * Set the previous context mark to the current position and add it to the
168 * jump list.
169 */
170 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100171setpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000172{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173 int i;
174 xfmark_T *fm;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100176 // for :global the mark is set only once
Bram Moolenaare1004402020-10-24 20:49:43 +0200177 if (global_busy || listcmd_busy || (cmdmod.cmod_flags & CMOD_KEEPJUMPS))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000178 return;
179
180 curwin->w_prev_pcmark = curwin->w_pcmark;
181 curwin->w_pcmark = curwin->w_cursor;
182
Yegappan Lakshmanan87018252023-09-20 20:20:04 +0200183 if (jop_flags & JOP_STACK)
184 {
185 // jumpoptions=stack: if we're somewhere in the middle of the jumplist
186 // discard everything after the current index.
187 if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1)
188 // Discard the rest of the jumplist by cutting the length down to
189 // contain nothing beyond the current index.
190 curwin->w_jumplistlen = curwin->w_jumplistidx + 1;
191 }
192
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100193 // If jumplist is full: remove oldest entry
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194 if (++curwin->w_jumplistlen > JUMPLISTSIZE)
195 {
196 curwin->w_jumplistlen = JUMPLISTSIZE;
197 vim_free(curwin->w_jumplist[0].fname);
198 for (i = 1; i < JUMPLISTSIZE; ++i)
199 curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
200 }
201 curwin->w_jumplistidx = curwin->w_jumplistlen;
202 fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
203
204 fm->fmark.mark = curwin->w_pcmark;
205 fm->fmark.fnum = curbuf->b_fnum;
206 fm->fname = NULL;
Bram Moolenaar739f13a2021-12-13 13:12:53 +0000207#ifdef FEAT_VIMINFO
Bram Moolenaar2d358992016-06-12 21:20:54 +0200208 fm->time_set = vim_time();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209#endif
210}
211
212/*
213 * To change context, call setpcmark(), then move the current position to
214 * where ever, then call checkpcmark(). This ensures that the previous
215 * context will only be changed if the cursor moved to a different line.
216 * If pcmark was deleted (with "dG") the previous mark is restored.
217 */
218 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100219checkpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000220{
221 if (curwin->w_prev_pcmark.lnum != 0
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100222 && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 || curwin->w_pcmark.lnum == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224 curwin->w_pcmark = curwin->w_prev_pcmark;
Bram Moolenaare08aee62021-10-17 21:53:58 +0100225 curwin->w_prev_pcmark.lnum = 0; // it has been checked
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226}
227
Bram Moolenaar071d4272004-06-13 20:20:40 +0000228/*
229 * move "count" positions in the jump list (count may be negative)
230 */
231 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100232movemark(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000233{
234 pos_T *pos;
235 xfmark_T *jmp;
236
Bram Moolenaar48679742018-02-13 13:33:29 +0100237 cleanup_jumplist(curwin, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100239 if (curwin->w_jumplistlen == 0) // nothing to jump to
Bram Moolenaar071d4272004-06-13 20:20:40 +0000240 return (pos_T *)NULL;
241
242 for (;;)
243 {
244 if (curwin->w_jumplistidx + count < 0
245 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
246 return (pos_T *)NULL;
247
248 /*
249 * if first CTRL-O or CTRL-I command after a jump, add cursor position
Bram Moolenaarf711faf2007-05-10 16:48:19 +0000250 * to list. Careful: If there are duplicates (CTRL-O immediately after
Bram Moolenaar071d4272004-06-13 20:20:40 +0000251 * starting Vim on a file), another entry may have been removed.
252 */
253 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
254 {
255 setpcmark();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100256 --curwin->w_jumplistidx; // skip the new entry
Bram Moolenaar071d4272004-06-13 20:20:40 +0000257 if (curwin->w_jumplistidx + count < 0)
258 return (pos_T *)NULL;
259 }
260
261 curwin->w_jumplistidx += count;
262
263 jmp = curwin->w_jumplist + curwin->w_jumplistidx;
264 if (jmp->fmark.fnum == 0)
265 fname2fnum(jmp);
266 if (jmp->fmark.fnum != curbuf->b_fnum)
267 {
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100268 // Make a copy, an autocommand may make "jmp" invalid.
269 fmark_T fmark = jmp->fmark;
270
271 // jump to the file with the mark
272 if (buflist_findnr(fmark.fnum) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100273 { // Skip this one ..
Bram Moolenaar071d4272004-06-13 20:20:40 +0000274 count += count < 0 ? -1 : 1;
275 continue;
276 }
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100277 if (buflist_getfile(fmark.fnum, fmark.mark.lnum, 0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000278 return (pos_T *)NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100279 // Set lnum again, autocommands my have changed it
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100280 curwin->w_cursor = fmark.mark;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 pos = (pos_T *)-1;
282 }
283 else
284 pos = &(jmp->fmark.mark);
285 return pos;
286 }
287}
288
289/*
290 * Move "count" positions in the changelist (count may be negative).
291 */
292 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100293movechangelist(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000294{
295 int n;
296
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100297 if (curbuf->b_changelistlen == 0) // nothing to jump to
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298 return (pos_T *)NULL;
299
300 n = curwin->w_changelistidx;
301 if (n + count < 0)
302 {
303 if (n == 0)
304 return (pos_T *)NULL;
305 n = 0;
306 }
307 else if (n + count >= curbuf->b_changelistlen)
308 {
309 if (n == curbuf->b_changelistlen - 1)
310 return (pos_T *)NULL;
311 n = curbuf->b_changelistlen - 1;
312 }
313 else
314 n += count;
315 curwin->w_changelistidx = n;
316 return curbuf->b_changelist + n;
317}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318
319/*
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100320 * Find mark "c" in buffer pointed to by "buf".
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000321 * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
322 * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
323 * another file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000324 * Returns:
325 * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
326 * in another file which can't be gotten. (caller needs to check lnum!)
327 * - NULL if there is no mark called 'c'.
328 * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
329 */
330 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100331getmark_buf(buf_T *buf, int c, int changefile)
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100332{
333 return getmark_buf_fnum(buf, c, changefile, NULL);
334}
335
336 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100337getmark(int c, int changefile)
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000338{
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100339 return getmark_buf_fnum(curbuf, c, changefile, NULL);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000340}
341
342 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100343getmark_buf_fnum(
344 buf_T *buf,
345 int c,
346 int changefile,
347 int *fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348{
349 pos_T *posp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000350 pos_T *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000351 static pos_T pos_copy;
352
353 posp = NULL;
354
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100355 // Check for special key, can't be a mark name and might cause islower()
356 // to crash.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000357 if (c < 0)
358 return posp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100359 if (c > '~') // check for islower()/isupper()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 ;
Bram Moolenaar424bcae2022-01-31 14:59:41 +0000361 else if (c == '\'' || c == '`') // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100363 pos_copy = curwin->w_pcmark; // need to make a copy because
364 posp = &pos_copy; // w_pcmark may be changed soon
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100366 else if (c == '"') // to pos when leaving buffer
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100367 posp = &(buf->b_last_cursor);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100368 else if (c == '^') // to where Insert mode stopped
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100369 posp = &(buf->b_last_insert);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100370 else if (c == '.') // to where last change was made
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100371 posp = &(buf->b_last_change);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100372 else if (c == '[') // to start of previous operator
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100373 posp = &(buf->b_op_start);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100374 else if (c == ']') // to end of previous operator
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100375 posp = &(buf->b_op_end);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100376 else if (c == '{' || c == '}') // to previous/next paragraph
Bram Moolenaar071d4272004-06-13 20:20:40 +0000377 {
378 pos_T pos;
379 oparg_T oa;
380 int slcb = listcmd_busy;
381
382 pos = curwin->w_cursor;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100383 listcmd_busy = TRUE; // avoid that '' is changed
Bram Moolenaar8b96d642005-09-05 22:05:30 +0000384 if (findpar(&oa.inclusive,
385 c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000386 {
387 pos_copy = curwin->w_cursor;
388 posp = &pos_copy;
389 }
390 curwin->w_cursor = pos;
391 listcmd_busy = slcb;
392 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100393 else if (c == '(' || c == ')') // to previous/next sentence
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 {
395 pos_T pos;
396 int slcb = listcmd_busy;
397
398 pos = curwin->w_cursor;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100399 listcmd_busy = TRUE; // avoid that '' is changed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400 if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
401 {
402 pos_copy = curwin->w_cursor;
403 posp = &pos_copy;
404 }
405 curwin->w_cursor = pos;
406 listcmd_busy = slcb;
407 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100408 else if (c == '<' || c == '>') // start/end of visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +0000409 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100410 startp = &buf->b_visual.vi_start;
411 endp = &buf->b_visual.vi_end;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100412 if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100413 && startp->lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000414 posp = startp;
415 else
416 posp = endp;
417 /*
418 * For Visual line mode, set mark at begin or end of line
419 */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100420 if (buf->b_visual.vi_mode == 'V')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421 {
422 pos_copy = *posp;
423 posp = &pos_copy;
424 if (c == '<')
425 pos_copy.col = 0;
426 else
427 pos_copy.col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428 pos_copy.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000429 }
430 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100431 else if (ASCII_ISLOWER(c)) // normal named mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000432 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100433 posp = &(buf->b_namedm[c - 'a']);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100435 else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) // named file mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000436 {
437 if (VIM_ISDIGIT(c))
438 c = c - '0' + NMARKS;
439 else
440 c -= 'A';
441 posp = &(namedfm[c].fmark.mark);
442
443 if (namedfm[c].fmark.fnum == 0)
444 fname2fnum(&namedfm[c]);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000445
446 if (fnum != NULL)
447 *fnum = namedfm[c].fmark.fnum;
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100448 else if (namedfm[c].fmark.fnum != buf->b_fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100450 // mark is in another file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451 posp = &pos_copy;
452
Bram Moolenaar071d4272004-06-13 20:20:40 +0000453 if (namedfm[c].fmark.mark.lnum != 0
454 && changefile && namedfm[c].fmark.fnum)
455 {
456 if (buflist_getfile(namedfm[c].fmark.fnum,
457 (linenr_T)1, GETF_SETMARK, FALSE) == OK)
458 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100459 // Set the lnum now, autocommands could have changed it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000460 curwin->w_cursor = namedfm[c].fmark.mark;
461 return (pos_T *)-1;
462 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100463 pos_copy.lnum = -1; // can't get file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000464 }
465 else
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100466 pos_copy.lnum = 0; // mark exists, but is not valid in
467 // current buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000468 }
469 }
470
471 return posp;
472}
473
474/*
475 * Search for the next named mark in the current file.
476 *
477 * Returns pointer to pos_T of the next mark or NULL if no mark is found.
478 */
479 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100480getnextmark(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100481 pos_T *startpos, // where to start
482 int dir, // direction for search
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100483 int begin_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000484{
485 int i;
486 pos_T *result = NULL;
487 pos_T pos;
488
489 pos = *startpos;
490
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100491 // When searching backward and leaving the cursor on the first non-blank,
492 // position must be in a previous line.
493 // When searching forward and leaving the cursor on the first non-blank,
494 // position must be in a next line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000495 if (dir == BACKWARD && begin_line)
496 pos.col = 0;
497 else if (dir == FORWARD && begin_line)
498 pos.col = MAXCOL;
499
500 for (i = 0; i < NMARKS; i++)
501 {
502 if (curbuf->b_namedm[i].lnum > 0)
503 {
504 if (dir == FORWARD)
505 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100506 if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
507 && LT_POS(pos, curbuf->b_namedm[i]))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000508 result = &curbuf->b_namedm[i];
509 }
510 else
511 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100512 if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
513 && LT_POS(curbuf->b_namedm[i], pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000514 result = &curbuf->b_namedm[i];
515 }
516 }
517 }
518
519 return result;
520}
521
522/*
523 * For an xtended filemark: set the fnum from the fname.
524 * This is used for marks obtained from the .viminfo file. It's postponed
525 * until the mark is used to avoid a long startup delay.
526 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200527 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100528fname2fnum(xfmark_T *fm)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000529{
530 char_u *p;
531
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000532 if (fm->fname == NULL)
533 return;
534
535 /*
536 * First expand "~/" in the file name to the home directory.
537 * Don't expand the whole name, it may contain other '~' chars.
538 */
539 if (fm->fname[0] == '~' && (fm->fname[1] == '/'
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000540#ifdef BACKSLASH_IN_FILENAME
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000541 || fm->fname[1] == '\\'
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000542#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000543 ))
544 {
545 int len;
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000546
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000547 expand_env((char_u *)"~/", NameBuff, MAXPATHL);
548 len = (int)STRLEN(NameBuff);
549 vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000551 else
552 vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
553
554 // Try to shorten the file name.
555 mch_dirname(IObuff, IOSIZE);
556 p = shorten_fname(NameBuff, IObuff);
557
558 // buflist_new() will call fmarks_check_names()
559 (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000560}
561
562/*
563 * Check all file marks for a name that matches the file name in buf.
564 * May replace the name with an fnum.
565 * Used for marks that come from the .viminfo file.
566 */
567 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100568fmarks_check_names(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569{
570 char_u *name;
571 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000572 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000573
574 if (buf->b_ffname == NULL)
575 return;
576
577 name = home_replace_save(buf, buf->b_ffname);
578 if (name == NULL)
579 return;
580
581 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
582 fmarks_check_one(&namedfm[i], name, buf);
583
Bram Moolenaar071d4272004-06-13 20:20:40 +0000584 FOR_ALL_WINDOWS(wp)
585 {
586 for (i = 0; i < wp->w_jumplistlen; ++i)
587 fmarks_check_one(&wp->w_jumplist[i], name, buf);
588 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589
590 vim_free(name);
591}
592
593 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100594fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595{
596 if (fm->fmark.fnum == 0
597 && fm->fname != NULL
598 && fnamecmp(name, fm->fname) == 0)
599 {
600 fm->fmark.fnum = buf->b_fnum;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100601 VIM_CLEAR(fm->fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000602 }
603}
604
605/*
606 * Check a if a position from a mark is valid.
607 * Give and error message and return FAIL if not.
608 */
609 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100610check_mark(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000611{
612 if (pos == NULL)
613 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +0000614 emsg(_(e_unknown_mark));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615 return FAIL;
616 }
617 if (pos->lnum <= 0)
618 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100619 // lnum is negative if mark is in another file can can't get that
620 // file, error message already give then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000621 if (pos->lnum == 0)
Bram Moolenaar108010a2021-06-27 22:03:33 +0200622 emsg(_(e_mark_not_set));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000623 return FAIL;
624 }
625 if (pos->lnum > curbuf->b_ml.ml_line_count)
626 {
Bram Moolenaar108010a2021-06-27 22:03:33 +0200627 emsg(_(e_mark_has_invalid_line_number));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 return FAIL;
629 }
630 return OK;
631}
632
633/*
634 * clrallmarks() - clear all marks in the buffer 'buf'
635 *
636 * Used mainly when trashing the entire buffer during ":e" type commands
637 */
638 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100639clrallmarks(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640{
641 static int i = -1;
642
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100643 if (i == -1) // first call ever: initialize
Bram Moolenaar071d4272004-06-13 20:20:40 +0000644 for (i = 0; i < NMARKS + 1; i++)
645 {
646 namedfm[i].fmark.mark.lnum = 0;
647 namedfm[i].fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200648#ifdef FEAT_VIMINFO
649 namedfm[i].time_set = 0;
650#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000651 }
652
653 for (i = 0; i < NMARKS; i++)
654 buf->b_namedm[i].lnum = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100655 buf->b_op_start.lnum = 0; // start/end op mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 buf->b_op_end.lnum = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100657 buf->b_last_cursor.lnum = 1; // '" mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000658 buf->b_last_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 buf->b_last_cursor.coladd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100660 buf->b_last_insert.lnum = 0; // '^ mark cleared
661 buf->b_last_change.lnum = 0; // '. mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000662 buf->b_changelistlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000663}
664
665/*
666 * Get name of file from a filemark.
667 * When it's in the current buffer, return the text at the mark.
668 * Returns an allocated string.
669 */
670 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100671fm_getname(fmark_T *fmark, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000672{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100673 if (fmark->fnum == curbuf->b_fnum) // current buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000674 return mark_line(&(fmark->mark), lead_len);
675 return buflist_nr2name(fmark->fnum, FALSE, TRUE);
676}
677
678/*
679 * Return the line at mark "mp". Truncate to fit in window.
680 * The returned string has been allocated.
681 */
682 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100683mark_line(pos_T *mp, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000684{
685 char_u *s, *p;
686 int len;
687
688 if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
689 return vim_strsave((char_u *)"-invalid-");
Bram Moolenaar9d5185b2018-07-08 17:57:34 +0200690 // Allow for up to 5 bytes per character.
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200691 s = vim_strnsave(skipwhite(ml_get(mp->lnum)), Columns * 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692 if (s == NULL)
693 return NULL;
Bram Moolenaar9d5185b2018-07-08 17:57:34 +0200694 // Truncate the line to fit it in the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000695 len = 0;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100696 for (p = s; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000697 {
698 len += ptr2cells(p);
699 if (len >= Columns - lead_len)
700 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000701 }
702 *p = NUL;
703 return s;
704}
705
706/*
707 * print the marks
708 */
709 void
Bram Moolenaar4bd78232019-09-19 23:21:55 +0200710ex_marks(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000711{
712 char_u *arg = eap->arg;
713 int i;
714 char_u *name;
Bram Moolenaar54c3fcd2020-07-19 22:09:06 +0200715 pos_T *posp, *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000716
717 if (arg != NULL && *arg == NUL)
718 arg = NULL;
719
720 show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
721 for (i = 0; i < NMARKS; ++i)
722 show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
723 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
724 {
725 if (namedfm[i].fmark.fnum != 0)
726 name = fm_getname(&namedfm[i].fmark, 15);
727 else
728 name = namedfm[i].fname;
729 if (name != NULL)
730 {
731 show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
732 arg, &namedfm[i].fmark.mark, name,
733 namedfm[i].fmark.fnum == curbuf->b_fnum);
734 if (namedfm[i].fmark.fnum != 0)
735 vim_free(name);
736 }
737 }
738 show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
739 show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
740 show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
741 show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
742 show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
Bram Moolenaar54c3fcd2020-07-19 22:09:06 +0200743
744 // Show the marks as where they will jump to.
745 startp = &curbuf->b_visual.vi_start;
746 endp = &curbuf->b_visual.vi_end;
747 if ((LT_POS(*startp, *endp) || endp->lnum == 0) && startp->lnum != 0)
748 posp = startp;
749 else
750 posp = endp;
751 show_one_mark('<', arg, posp, NULL, TRUE);
752 show_one_mark('>', arg, posp == startp ? endp : startp, NULL, TRUE);
753
Bram Moolenaar071d4272004-06-13 20:20:40 +0000754 show_one_mark(-1, arg, NULL, NULL, FALSE);
755}
756
757 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100758show_one_mark(
759 int c,
760 char_u *arg,
761 pos_T *p,
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200762 char_u *name_arg,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100763 int current) // in current file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764{
765 static int did_title = FALSE;
766 int mustfree = FALSE;
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200767 char_u *name = name_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000768
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100769 if (c == -1) // finish up
Bram Moolenaar071d4272004-06-13 20:20:40 +0000770 {
771 if (did_title)
772 did_title = FALSE;
773 else
774 {
775 if (arg == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100776 msg(_("No marks set"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 else
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000778 semsg(_(e_no_marks_matching_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 }
780 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200781 // don't output anything if 'q' typed at --more-- prompt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 else if (!got_int
783 && (arg == NULL || vim_strchr(arg, c) != NULL)
784 && p->lnum != 0)
785 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200786 if (name == NULL && current)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000787 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200788 name = mark_line(p, 15);
789 mustfree = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000790 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200791 if (!message_filtered(name))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200793 if (!did_title)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200795 // Highlight title
796 msg_puts_title(_("\nmark line col file/text"));
797 did_title = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200799 msg_putchar('\n');
800 if (!got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000801 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200802 sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
803 msg_outtrans(IObuff);
804 if (name != NULL)
805 {
806 msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
807 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200809 out_flush(); // show one line at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000810 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200811 if (mustfree)
812 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000813 }
814}
815
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000816/*
817 * ":delmarks[!] [marks]"
818 */
819 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100820ex_delmarks(exarg_T *eap)
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000821{
822 char_u *p;
823 int from, to;
824 int i;
825 int lower;
826 int digit;
827 int n;
828
829 if (*eap->arg == NUL && eap->forceit)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100830 // clear all marks
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000831 clrallmarks(curbuf);
832 else if (eap->forceit)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000833 emsg(_(e_invalid_argument));
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000834 else if (*eap->arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000835 emsg(_(e_argument_required));
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000836 else
837 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100838 // clear specified marks only
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000839 for (p = eap->arg; *p != NUL; ++p)
840 {
841 lower = ASCII_ISLOWER(*p);
842 digit = VIM_ISDIGIT(*p);
843 if (lower || digit || ASCII_ISUPPER(*p))
844 {
845 if (p[1] == '-')
846 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100847 // clear range of marks
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000848 from = *p;
849 to = p[2];
850 if (!(lower ? ASCII_ISLOWER(p[2])
851 : (digit ? VIM_ISDIGIT(p[2])
852 : ASCII_ISUPPER(p[2])))
853 || to < from)
854 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000855 semsg(_(e_invalid_argument_str), p);
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000856 return;
857 }
858 p += 2;
859 }
860 else
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100861 // clear one lower case mark
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000862 from = to = *p;
863
864 for (i = from; i <= to; ++i)
865 {
866 if (lower)
867 curbuf->b_namedm[i - 'a'].lnum = 0;
868 else
869 {
870 if (digit)
871 n = i - '0' + NMARKS;
872 else
873 n = i - 'A';
874 namedfm[n].fmark.mark.lnum = 0;
Bram Moolenaar8cd6cd82019-12-27 17:33:26 +0100875 namedfm[n].fmark.fnum = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100876 VIM_CLEAR(namedfm[n].fname);
Bram Moolenaar2d358992016-06-12 21:20:54 +0200877#ifdef FEAT_VIMINFO
Bram Moolenaar8cd6cd82019-12-27 17:33:26 +0100878 namedfm[n].time_set = digit ? 0 : vim_time();
Bram Moolenaar2d358992016-06-12 21:20:54 +0200879#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000880 }
881 }
882 }
883 else
884 switch (*p)
885 {
886 case '"': curbuf->b_last_cursor.lnum = 0; break;
887 case '^': curbuf->b_last_insert.lnum = 0; break;
888 case '.': curbuf->b_last_change.lnum = 0; break;
889 case '[': curbuf->b_op_start.lnum = 0; break;
890 case ']': curbuf->b_op_end.lnum = 0; break;
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000891 case '<': curbuf->b_visual.vi_start.lnum = 0; break;
892 case '>': curbuf->b_visual.vi_end.lnum = 0; break;
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000893 case ' ': break;
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000894 default: semsg(_(e_invalid_argument_str), p);
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000895 return;
896 }
897 }
898 }
899}
900
Bram Moolenaar071d4272004-06-13 20:20:40 +0000901/*
902 * print the jumplist
903 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000904 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100905ex_jumps(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906{
907 int i;
908 char_u *name;
909
Bram Moolenaar48679742018-02-13 13:33:29 +0100910 cleanup_jumplist(curwin, TRUE);
Bram Moolenaara7e18d22018-02-11 14:29:49 +0100911
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100912 // Highlight title
Bram Moolenaar32526b32019-01-19 17:43:09 +0100913 msg_puts_title(_("\n jump line col file/text"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000914 for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
915 {
916 if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
917 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918 name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
Bram Moolenaarf86db782018-10-25 13:31:37 +0200919
Christian Brabandta0f659c2022-04-09 13:35:00 +0100920 // Make sure to output the current indicator, even when on an wiped
921 // out buffer. ":filter" may still skip it.
922 if (name == NULL && i == curwin->w_jumplistidx)
923 name = vim_strsave((char_u *)"-invalid-");
Bram Moolenaarf86db782018-10-25 13:31:37 +0200924 // apply :filter /pat/ or file name not available
925 if (name == NULL || message_filtered(name))
Bram Moolenaard93090f2019-01-27 15:07:39 +0100926 {
927 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000928 continue;
Bram Moolenaard93090f2019-01-27 15:07:39 +0100929 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930
931 msg_putchar('\n');
932 if (got_int)
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000933 {
934 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935 break;
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000936 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 sprintf((char *)IObuff, "%c %2d %5ld %4d ",
938 i == curwin->w_jumplistidx ? '>' : ' ',
939 i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
940 : curwin->w_jumplistidx - i,
941 curwin->w_jumplist[i].fmark.mark.lnum,
942 curwin->w_jumplist[i].fmark.mark.col);
943 msg_outtrans(IObuff);
944 msg_outtrans_attr(name,
945 curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
Bram Moolenaar8820b482017-03-16 17:23:31 +0100946 ? HL_ATTR(HLF_D) : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000947 vim_free(name);
948 ui_breakcheck();
949 }
950 out_flush();
951 }
952 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100953 msg_puts("\n>");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000954}
955
Bram Moolenaar2d358992016-06-12 21:20:54 +0200956 void
957ex_clearjumps(exarg_T *eap UNUSED)
958{
959 free_jumplist(curwin);
960 curwin->w_jumplistlen = 0;
961 curwin->w_jumplistidx = 0;
962}
963
Bram Moolenaar071d4272004-06-13 20:20:40 +0000964/*
965 * print the changelist
966 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100968ex_changes(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969{
970 int i;
971 char_u *name;
972
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100973 // Highlight title
Bram Moolenaar32526b32019-01-19 17:43:09 +0100974 msg_puts_title(_("\nchange line col text"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000975
976 for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i)
977 {
978 if (curbuf->b_changelist[i].lnum != 0)
979 {
980 msg_putchar('\n');
981 if (got_int)
982 break;
983 sprintf((char *)IObuff, "%c %3d %5ld %4d ",
984 i == curwin->w_changelistidx ? '>' : ' ',
985 i > curwin->w_changelistidx ? i - curwin->w_changelistidx
986 : curwin->w_changelistidx - i,
987 (long)curbuf->b_changelist[i].lnum,
988 curbuf->b_changelist[i].col);
989 msg_outtrans(IObuff);
990 name = mark_line(&curbuf->b_changelist[i], 17);
991 if (name == NULL)
992 break;
Bram Moolenaar8820b482017-03-16 17:23:31 +0100993 msg_outtrans_attr(name, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000994 vim_free(name);
995 ui_breakcheck();
996 }
997 out_flush();
998 }
999 if (curwin->w_changelistidx == curbuf->b_changelistlen)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001000 msg_puts("\n>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001001}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002
1003#define one_adjust(add) \
1004 { \
1005 lp = add; \
1006 if (*lp >= line1 && *lp <= line2) \
1007 { \
1008 if (amount == MAXLNUM) \
1009 *lp = 0; \
1010 else \
1011 *lp += amount; \
1012 } \
1013 else if (amount_after && *lp > line2) \
1014 *lp += amount_after; \
1015 }
1016
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001017// don't delete the line, just put at first deleted line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001018#define one_adjust_nodel(add) \
1019 { \
1020 lp = add; \
1021 if (*lp >= line1 && *lp <= line2) \
1022 { \
1023 if (amount == MAXLNUM) \
1024 *lp = line1; \
1025 else \
1026 *lp += amount; \
1027 } \
1028 else if (amount_after && *lp > line2) \
1029 *lp += amount_after; \
1030 }
1031
1032/*
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001033 * Adjust marks between "line1" and "line2" (inclusive) to move "amount" lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034 * Must be called before changed_*(), appended_lines() or deleted_lines().
1035 * May be called before or after changing the text.
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001036 * When deleting lines "line1" to "line2", use an "amount" of MAXLNUM: The
1037 * marks within this range are made invalid.
1038 * If "amount_after" is non-zero adjust marks after "line2".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001039 * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1040 * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1041 * or: mark_adjust(56, 55, MAXLNUM, 2);
1042 */
1043 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001044mark_adjust(
1045 linenr_T line1,
1046 linenr_T line2,
1047 long amount,
1048 long amount_after)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049{
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001050 mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1051}
1052
1053 void
1054mark_adjust_nofold(
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01001055 linenr_T line1,
1056 linenr_T line2,
1057 long amount,
1058 long amount_after)
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001059{
1060 mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1061}
1062
1063 static void
1064mark_adjust_internal(
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01001065 linenr_T line1,
1066 linenr_T line2,
1067 long amount,
1068 long amount_after,
1069 int adjust_folds UNUSED)
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001070{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001071 int i;
1072 int fnum = curbuf->b_fnum;
1073 linenr_T *lp;
1074 win_T *win;
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001075 tabpage_T *tab;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01001076 static pos_T initpos = {1, 0, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +00001077
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001078 if (line2 < line1 && amount_after == 0L) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001079 return;
1080
Bram Moolenaare1004402020-10-24 20:49:43 +02001081 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001083 // named marks, lower case and upper case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 for (i = 0; i < NMARKS; i++)
1085 {
1086 one_adjust(&(curbuf->b_namedm[i].lnum));
1087 if (namedfm[i].fmark.fnum == fnum)
1088 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1089 }
1090 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1091 {
1092 if (namedfm[i].fmark.fnum == fnum)
1093 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1094 }
1095
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001096 // last Insert position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097 one_adjust(&(curbuf->b_last_insert.lnum));
1098
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001099 // last change position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100 one_adjust(&(curbuf->b_last_change.lnum));
1101
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001102 // last cursor position, if it was set
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001103 if (!EQUAL_POS(curbuf->b_last_cursor, initpos))
Bram Moolenaarb6a76ff2013-02-06 12:33:21 +01001104 one_adjust(&(curbuf->b_last_cursor.lnum));
1105
1106
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001107 // list of change positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 for (i = 0; i < curbuf->b_changelistlen; ++i)
1109 one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001111 // Visual area
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001112 one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1113 one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114
1115#ifdef FEAT_QUICKFIX
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001116 // quickfix marks
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001117 qf_mark_adjust(NULL, line1, line2, amount, amount_after);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001118 // location lists
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001119 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001120 qf_mark_adjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121#endif
1122
1123#ifdef FEAT_SIGNS
1124 sign_mark_adjust(line1, line2, amount, amount_after);
1125#endif
1126 }
1127
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001128 // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 one_adjust(&(curwin->w_pcmark.lnum));
1130
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001131 // previous pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001132 one_adjust(&(curwin->w_prev_pcmark.lnum));
1133
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001134 // saved cursor for formatting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001135 if (saved_cursor.lnum != 0)
1136 one_adjust_nodel(&(saved_cursor.lnum));
1137
1138 /*
1139 * Adjust items in all windows related to the current buffer.
1140 */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001141 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001142 {
Bram Moolenaare1004402020-10-24 20:49:43 +02001143 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001144 // Marks in the jumplist. When deleting lines, this may create
1145 // duplicate marks in the jumplist, they will be removed later.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001146 for (i = 0; i < win->w_jumplistlen; ++i)
1147 if (win->w_jumplist[i].fmark.fnum == fnum)
1148 one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001149
1150 if (win->w_buffer == curbuf)
1151 {
Bram Moolenaare1004402020-10-24 20:49:43 +02001152 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001153 // marks in the tag stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001154 for (i = 0; i < win->w_tagstacklen; i++)
1155 if (win->w_tagstack[i].fmark.fnum == fnum)
1156 one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1157
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001158 // the displayed Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00001159 if (win->w_old_cursor_lnum != 0)
1160 {
1161 one_adjust_nodel(&(win->w_old_cursor_lnum));
1162 one_adjust_nodel(&(win->w_old_visual_lnum));
1163 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001164
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001165 // topline and cursor position for windows with the same buffer
1166 // other than the current window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001167 if (win != curwin)
1168 {
1169 if (win->w_topline >= line1 && win->w_topline <= line2)
1170 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001171 if (amount == MAXLNUM) // topline is deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00001172 {
1173 if (line1 <= 1)
1174 win->w_topline = 1;
1175 else
1176 win->w_topline = line1 - 1;
1177 }
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001178 else if (win->w_topline > line1)
1179 // keep topline on the same line, unless inserting just
1180 // above it (we probably want to see that line then)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 win->w_topline += amount;
1182#ifdef FEAT_DIFF
1183 win->w_topfill = 0;
1184#endif
1185 }
1186 else if (amount_after && win->w_topline > line2)
1187 {
1188 win->w_topline += amount_after;
1189#ifdef FEAT_DIFF
1190 win->w_topfill = 0;
1191#endif
1192 }
1193 if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1194 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001195 if (amount == MAXLNUM) // line with cursor is deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00001196 {
1197 if (line1 <= 1)
1198 win->w_cursor.lnum = 1;
1199 else
1200 win->w_cursor.lnum = line1 - 1;
1201 win->w_cursor.col = 0;
1202 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001203 else // keep cursor on the same line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 win->w_cursor.lnum += amount;
1205 }
1206 else if (amount_after && win->w_cursor.lnum > line2)
1207 win->w_cursor.lnum += amount_after;
1208 }
1209
1210#ifdef FEAT_FOLDING
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001211 // adjust folds
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001212 if (adjust_folds)
1213 foldMarkAdjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001214#endif
1215 }
1216 }
1217
1218#ifdef FEAT_DIFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001219 // adjust diffs
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 diff_mark_adjust(line1, line2, amount, amount_after);
1221#endif
1222}
1223
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001224// This code is used often, needs to be fast.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001225#define col_adjust(pp) \
1226 { \
1227 posp = pp; \
1228 if (posp->lnum == lnum && posp->col >= mincol) \
1229 { \
1230 posp->lnum += lnum_amount; \
1231 if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1232 posp->col = 0; \
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001233 else if (posp->col < spaces_removed) \
1234 posp->col = col_amount + spaces_removed; \
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235 else \
1236 posp->col += col_amount; \
1237 } \
1238 }
1239
1240/*
1241 * Adjust marks in line "lnum" at column "mincol" and further: add
1242 * "lnum_amount" to the line number and add "col_amount" to the column
1243 * position.
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001244 * "spaces_removed" is the number of spaces that were removed, matters when the
1245 * cursor is inside them.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001246 */
1247 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001248mark_col_adjust(
1249 linenr_T lnum,
1250 colnr_T mincol,
1251 long lnum_amount,
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001252 long col_amount,
1253 int spaces_removed)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001254{
1255 int i;
1256 int fnum = curbuf->b_fnum;
1257 win_T *win;
1258 pos_T *posp;
1259
Bram Moolenaare1004402020-10-24 20:49:43 +02001260 if ((col_amount == 0L && lnum_amount == 0L)
1261 || (cmdmod.cmod_flags & CMOD_LOCKMARKS))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001262 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001263
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001264 // named marks, lower case and upper case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265 for (i = 0; i < NMARKS; i++)
1266 {
1267 col_adjust(&(curbuf->b_namedm[i]));
1268 if (namedfm[i].fmark.fnum == fnum)
1269 col_adjust(&(namedfm[i].fmark.mark));
1270 }
1271 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1272 {
1273 if (namedfm[i].fmark.fnum == fnum)
1274 col_adjust(&(namedfm[i].fmark.mark));
1275 }
1276
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001277 // last Insert position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 col_adjust(&(curbuf->b_last_insert));
1279
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001280 // last change position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 col_adjust(&(curbuf->b_last_change));
1282
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001283 // list of change positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 for (i = 0; i < curbuf->b_changelistlen; ++i)
1285 col_adjust(&(curbuf->b_changelist[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001287 // Visual area
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001288 col_adjust(&(curbuf->b_visual.vi_start));
1289 col_adjust(&(curbuf->b_visual.vi_end));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001291 // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292 col_adjust(&(curwin->w_pcmark));
1293
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001294 // previous pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 col_adjust(&(curwin->w_prev_pcmark));
1296
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001297 // saved cursor for formatting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298 col_adjust(&saved_cursor);
1299
1300 /*
1301 * Adjust items in all windows related to the current buffer.
1302 */
1303 FOR_ALL_WINDOWS(win)
1304 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001305 // marks in the jumplist
Bram Moolenaar071d4272004-06-13 20:20:40 +00001306 for (i = 0; i < win->w_jumplistlen; ++i)
1307 if (win->w_jumplist[i].fmark.fnum == fnum)
1308 col_adjust(&(win->w_jumplist[i].fmark.mark));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001309
1310 if (win->w_buffer == curbuf)
1311 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001312 // marks in the tag stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001313 for (i = 0; i < win->w_tagstacklen; i++)
1314 if (win->w_tagstack[i].fmark.fnum == fnum)
1315 col_adjust(&(win->w_tagstack[i].fmark.mark));
1316
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001317 // cursor position for other windows with the same buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 if (win != curwin)
1319 col_adjust(&win->w_cursor);
1320 }
1321 }
1322}
1323
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324/*
1325 * When deleting lines, this may create duplicate marks in the
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001326 * jumplist. They will be removed here for the specified window.
Bram Moolenaar48679742018-02-13 13:33:29 +01001327 * When "loadfiles" is TRUE first ensure entries have the "fnum" field set
1328 * (this may be a bit slow).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 */
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001330 void
Bram Moolenaar48679742018-02-13 13:33:29 +01001331cleanup_jumplist(win_T *wp, int loadfiles)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001332{
1333 int i;
1334 int from, to;
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001335 int mustfree;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001336
Bram Moolenaar48679742018-02-13 13:33:29 +01001337 if (loadfiles)
1338 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001339 // If specified, load all the files from the jump list. This is
1340 // needed to properly clean up duplicate entries, but will take some
1341 // time.
Bram Moolenaar48679742018-02-13 13:33:29 +01001342 for (i = 0; i < wp->w_jumplistlen; ++i)
1343 {
1344 if ((wp->w_jumplist[i].fmark.fnum == 0) &&
1345 (wp->w_jumplist[i].fmark.mark.lnum != 0))
1346 fname2fnum(&wp->w_jumplist[i]);
1347 }
1348 }
1349
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 to = 0;
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001351 for (from = 0; from < wp->w_jumplistlen; ++from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352 {
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001353 if (wp->w_jumplistidx == from)
1354 wp->w_jumplistidx = to;
1355 for (i = from + 1; i < wp->w_jumplistlen; ++i)
1356 if (wp->w_jumplist[i].fmark.fnum
1357 == wp->w_jumplist[from].fmark.fnum
1358 && wp->w_jumplist[from].fmark.fnum != 0
1359 && wp->w_jumplist[i].fmark.mark.lnum
1360 == wp->w_jumplist[from].fmark.mark.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 break;
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001362 if (i >= wp->w_jumplistlen) // not duplicate
1363 mustfree = FALSE;
1364 else if (i > from + 1) // non-adjacent duplicate
1365 // jumpoptions=stack: remove duplicates only when adjacent.
1366 mustfree = !(jop_flags & JOP_STACK);
1367 else // adjacent duplicate
1368 mustfree = TRUE;
1369
1370 if (mustfree)
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001371 vim_free(wp->w_jumplist[from].fname);
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001372 else
1373 wp->w_jumplist[to++] = wp->w_jumplist[from];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374 }
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001375 if (wp->w_jumplistidx == wp->w_jumplistlen)
1376 wp->w_jumplistidx = to;
1377 wp->w_jumplistlen = to;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001378}
1379
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380/*
1381 * Copy the jumplist from window "from" to window "to".
1382 */
1383 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001384copy_jumplist(win_T *from, win_T *to)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385{
1386 int i;
1387
1388 for (i = 0; i < from->w_jumplistlen; ++i)
1389 {
1390 to->w_jumplist[i] = from->w_jumplist[i];
1391 if (from->w_jumplist[i].fname != NULL)
1392 to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1393 }
1394 to->w_jumplistlen = from->w_jumplistlen;
1395 to->w_jumplistidx = from->w_jumplistidx;
1396}
1397
1398/*
1399 * Free items in the jumplist of window "wp".
1400 */
1401 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001402free_jumplist(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403{
1404 int i;
1405
1406 for (i = 0; i < wp->w_jumplistlen; ++i)
1407 vim_free(wp->w_jumplist[i].fname);
1408}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001409
1410 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001411set_last_cursor(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001412{
Bram Moolenaar9db12932013-11-03 00:20:52 +01001413 if (win->w_buffer != NULL)
1414 win->w_buffer->b_last_cursor = win->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001415}
1416
Bram Moolenaarea408852005-06-25 22:49:46 +00001417#if defined(EXITFREE) || defined(PROTO)
1418 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001419free_all_marks(void)
Bram Moolenaarea408852005-06-25 22:49:46 +00001420{
1421 int i;
1422
1423 for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1424 if (namedfm[i].fmark.mark.lnum != 0)
1425 vim_free(namedfm[i].fname);
1426}
1427#endif
1428
Dominique Pelle748b3082022-01-08 12:41:16 +00001429#if defined(FEAT_VIMINFO) || defined(PROTO)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001430/*
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001431 * Return a pointer to the named file marks.
Bram Moolenaar2d358992016-06-12 21:20:54 +02001432 */
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001433 xfmark_T *
1434get_namedfm(void)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001435{
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001436 return namedfm;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001437}
Dominique Pelle748b3082022-01-08 12:41:16 +00001438#endif
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001439
1440#if defined(FEAT_EVAL) || defined(PROTO)
1441/*
1442 * Add information about mark 'mname' to list 'l'
1443 */
1444 static int
1445add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
1446{
1447 dict_T *d;
1448 list_T *lpos;
1449
1450 if (pos->lnum <= 0)
1451 return OK;
1452
1453 d = dict_alloc();
1454 if (d == NULL)
1455 return FAIL;
1456
1457 if (list_append_dict(l, d) == FAIL)
1458 {
1459 dict_unref(d);
1460 return FAIL;
1461 }
1462
1463 lpos = list_alloc();
1464 if (lpos == NULL)
1465 return FAIL;
1466
1467 list_append_number(lpos, bufnr);
1468 list_append_number(lpos, pos->lnum);
Bram Moolenaarf17e7ea2020-06-01 14:14:44 +02001469 list_append_number(lpos, pos->col + 1);
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001470 list_append_number(lpos, pos->coladd);
1471
1472 if (dict_add_string(d, "mark", mname) == FAIL
1473 || dict_add_list(d, "pos", lpos) == FAIL
1474 || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
1475 return FAIL;
1476
1477 return OK;
1478}
1479
1480/*
1481 * Get information about marks local to a buffer.
1482 */
1483 static void
1484get_buf_local_marks(buf_T *buf, list_T *l)
1485{
1486 char_u mname[3] = "' ";
1487 int i;
1488
1489 // Marks 'a' to 'z'
1490 for (i = 0; i < NMARKS; ++i)
1491 {
1492 mname[1] = 'a' + i;
1493 add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
1494 }
1495
1496 // Mark '' is a window local mark and not a buffer local mark
1497 add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
1498
1499 add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
1500 add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
1501 add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
1502 add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
1503 add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
1504 add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
1505 add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
1506}
1507
1508/*
1509 * Get information about global marks ('A' to 'Z' and '0' to '9')
1510 */
1511 static void
1512get_global_marks(list_T *l)
1513{
1514 char_u mname[3] = "' ";
1515 int i;
1516 char_u *name;
1517
1518 // Marks 'A' to 'Z' and '0' to '9'
1519 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
1520 {
1521 if (namedfm[i].fmark.fnum != 0)
1522 name = buflist_nr2name(namedfm[i].fmark.fnum, TRUE, TRUE);
1523 else
1524 name = namedfm[i].fname;
1525 if (name != NULL)
1526 {
1527 mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
1528 add_mark(l, mname, &namedfm[i].fmark.mark,
1529 namedfm[i].fmark.fnum, name);
1530 if (namedfm[i].fmark.fnum != 0)
1531 vim_free(name);
1532 }
1533 }
1534}
1535
1536/*
1537 * getmarklist() function
1538 */
1539 void
1540f_getmarklist(typval_T *argvars, typval_T *rettv)
1541{
1542 buf_T *buf = NULL;
1543
Bram Moolenaar8088ae92022-06-20 11:38:17 +01001544 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001545 return;
1546
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001547 if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
1548 return;
1549
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001550 if (argvars[0].v_type == VAR_UNKNOWN)
1551 {
1552 get_global_marks(rettv->vval.v_list);
1553 return;
1554 }
1555
1556 buf = tv_get_buf(&argvars[0], FALSE);
1557 if (buf == NULL)
1558 return;
1559
1560 get_buf_local_marks(buf, rettv->vval.v_list);
1561}
1562#endif