blob: 778110f9192cc387247c176e2c7bc9ea5fa0557e [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
zeertzjq2e7d89b2024-07-10 19:36:36 +0200141 for (i = wp->w_jumplistlen - 1; i >= 0; --i)
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200142 if (wp->w_jumplist[i].fmark.fnum == fnum)
143 {
144 vim_free(wp->w_jumplist[i].fname);
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200145 if (wp->w_jumplistidx > i)
146 --wp->w_jumplistidx;
147 --wp->w_jumplistlen;
zeertzjq2e7d89b2024-07-10 19:36:36 +0200148 mch_memmove(&wp->w_jumplist[i], &wp->w_jumplist[i + 1],
149 (wp->w_jumplistlen - i) * sizeof(wp->w_jumplist[i]));
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200150 }
151
zeertzjq2e7d89b2024-07-10 19:36:36 +0200152 for (i = wp->w_tagstacklen - 1; i >= 0; --i)
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200153 if (wp->w_tagstack[i].fmark.fnum == fnum)
154 {
155 tagstack_clear_entry(&wp->w_tagstack[i]);
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200156 if (wp->w_tagstackidx > i)
157 --wp->w_tagstackidx;
158 --wp->w_tagstacklen;
zeertzjq2e7d89b2024-07-10 19:36:36 +0200159 mch_memmove(&wp->w_tagstack[i], &wp->w_tagstack[i + 1],
160 (wp->w_tagstacklen - i) * sizeof(wp->w_tagstack[i]));
LemonBoy4ff3a9b2024-07-09 20:03:24 +0200161 }
162}
163
164/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000165 * Set the previous context mark to the current position and add it to the
166 * jump list.
167 */
168 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100169setpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000170{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000171 int i;
172 xfmark_T *fm;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100174 // for :global the mark is set only once
Bram Moolenaare1004402020-10-24 20:49:43 +0200175 if (global_busy || listcmd_busy || (cmdmod.cmod_flags & CMOD_KEEPJUMPS))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176 return;
177
178 curwin->w_prev_pcmark = curwin->w_pcmark;
179 curwin->w_pcmark = curwin->w_cursor;
180
Yegappan Lakshmanan87018252023-09-20 20:20:04 +0200181 if (jop_flags & JOP_STACK)
182 {
183 // jumpoptions=stack: if we're somewhere in the middle of the jumplist
184 // discard everything after the current index.
185 if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1)
186 // Discard the rest of the jumplist by cutting the length down to
187 // contain nothing beyond the current index.
188 curwin->w_jumplistlen = curwin->w_jumplistidx + 1;
189 }
190
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100191 // If jumplist is full: remove oldest entry
Bram Moolenaar071d4272004-06-13 20:20:40 +0000192 if (++curwin->w_jumplistlen > JUMPLISTSIZE)
193 {
194 curwin->w_jumplistlen = JUMPLISTSIZE;
195 vim_free(curwin->w_jumplist[0].fname);
196 for (i = 1; i < JUMPLISTSIZE; ++i)
197 curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
198 }
199 curwin->w_jumplistidx = curwin->w_jumplistlen;
200 fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
201
202 fm->fmark.mark = curwin->w_pcmark;
203 fm->fmark.fnum = curbuf->b_fnum;
204 fm->fname = NULL;
Bram Moolenaar739f13a2021-12-13 13:12:53 +0000205#ifdef FEAT_VIMINFO
Bram Moolenaar2d358992016-06-12 21:20:54 +0200206 fm->time_set = vim_time();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207#endif
208}
209
210/*
211 * To change context, call setpcmark(), then move the current position to
212 * where ever, then call checkpcmark(). This ensures that the previous
213 * context will only be changed if the cursor moved to a different line.
214 * If pcmark was deleted (with "dG") the previous mark is restored.
215 */
216 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100217checkpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000218{
219 if (curwin->w_prev_pcmark.lnum != 0
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100220 && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221 || curwin->w_pcmark.lnum == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222 curwin->w_pcmark = curwin->w_prev_pcmark;
Bram Moolenaare08aee62021-10-17 21:53:58 +0100223 curwin->w_prev_pcmark.lnum = 0; // it has been checked
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224}
225
Bram Moolenaar071d4272004-06-13 20:20:40 +0000226/*
227 * move "count" positions in the jump list (count may be negative)
228 */
229 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100230movemark(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000231{
232 pos_T *pos;
233 xfmark_T *jmp;
234
Bram Moolenaar48679742018-02-13 13:33:29 +0100235 cleanup_jumplist(curwin, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100237 if (curwin->w_jumplistlen == 0) // nothing to jump to
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238 return (pos_T *)NULL;
239
240 for (;;)
241 {
242 if (curwin->w_jumplistidx + count < 0
243 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
244 return (pos_T *)NULL;
245
246 /*
247 * if first CTRL-O or CTRL-I command after a jump, add cursor position
Bram Moolenaarf711faf2007-05-10 16:48:19 +0000248 * to list. Careful: If there are duplicates (CTRL-O immediately after
Bram Moolenaar071d4272004-06-13 20:20:40 +0000249 * starting Vim on a file), another entry may have been removed.
250 */
251 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
252 {
253 setpcmark();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100254 --curwin->w_jumplistidx; // skip the new entry
Bram Moolenaar071d4272004-06-13 20:20:40 +0000255 if (curwin->w_jumplistidx + count < 0)
256 return (pos_T *)NULL;
257 }
258
259 curwin->w_jumplistidx += count;
260
261 jmp = curwin->w_jumplist + curwin->w_jumplistidx;
262 if (jmp->fmark.fnum == 0)
263 fname2fnum(jmp);
264 if (jmp->fmark.fnum != curbuf->b_fnum)
265 {
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100266 // Make a copy, an autocommand may make "jmp" invalid.
267 fmark_T fmark = jmp->fmark;
268
269 // jump to the file with the mark
270 if (buflist_findnr(fmark.fnum) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100271 { // Skip this one ..
Bram Moolenaar071d4272004-06-13 20:20:40 +0000272 count += count < 0 ? -1 : 1;
273 continue;
274 }
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100275 if (buflist_getfile(fmark.fnum, fmark.mark.lnum, 0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276 return (pos_T *)NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100277 // Set lnum again, autocommands my have changed it
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100278 curwin->w_cursor = fmark.mark;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000279 pos = (pos_T *)-1;
280 }
281 else
282 pos = &(jmp->fmark.mark);
283 return pos;
284 }
285}
286
287/*
288 * Move "count" positions in the changelist (count may be negative).
289 */
290 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100291movechangelist(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000292{
293 int n;
294
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100295 if (curbuf->b_changelistlen == 0) // nothing to jump to
Bram Moolenaar071d4272004-06-13 20:20:40 +0000296 return (pos_T *)NULL;
297
298 n = curwin->w_changelistidx;
299 if (n + count < 0)
300 {
301 if (n == 0)
302 return (pos_T *)NULL;
303 n = 0;
304 }
305 else if (n + count >= curbuf->b_changelistlen)
306 {
307 if (n == curbuf->b_changelistlen - 1)
308 return (pos_T *)NULL;
309 n = curbuf->b_changelistlen - 1;
310 }
311 else
312 n += count;
313 curwin->w_changelistidx = n;
314 return curbuf->b_changelist + n;
315}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316
317/*
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100318 * Find mark "c" in buffer pointed to by "buf".
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000319 * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
320 * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
321 * another file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000322 * Returns:
323 * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
324 * in another file which can't be gotten. (caller needs to check lnum!)
325 * - NULL if there is no mark called 'c'.
326 * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
327 */
328 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100329getmark_buf(buf_T *buf, int c, int changefile)
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100330{
331 return getmark_buf_fnum(buf, c, changefile, NULL);
332}
333
334 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100335getmark(int c, int changefile)
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000336{
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100337 return getmark_buf_fnum(curbuf, c, changefile, NULL);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000338}
339
340 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100341getmark_buf_fnum(
342 buf_T *buf,
343 int c,
344 int changefile,
345 int *fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000346{
347 pos_T *posp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000348 pos_T *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000349 static pos_T pos_copy;
350
351 posp = NULL;
352
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100353 // Check for special key, can't be a mark name and might cause islower()
354 // to crash.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000355 if (c < 0)
356 return posp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100357 if (c > '~') // check for islower()/isupper()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000358 ;
Bram Moolenaar424bcae2022-01-31 14:59:41 +0000359 else if (c == '\'' || c == '`') // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100361 pos_copy = curwin->w_pcmark; // need to make a copy because
362 posp = &pos_copy; // w_pcmark may be changed soon
Bram Moolenaar071d4272004-06-13 20:20:40 +0000363 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100364 else if (c == '"') // to pos when leaving buffer
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100365 posp = &(buf->b_last_cursor);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100366 else if (c == '^') // to where Insert mode stopped
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100367 posp = &(buf->b_last_insert);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100368 else if (c == '.') // to where last change was made
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100369 posp = &(buf->b_last_change);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100370 else if (c == '[') // to start of previous operator
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100371 posp = &(buf->b_op_start);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100372 else if (c == ']') // to end of previous operator
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100373 posp = &(buf->b_op_end);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100374 else if (c == '{' || c == '}') // to previous/next paragraph
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 {
376 pos_T pos;
377 oparg_T oa;
378 int slcb = listcmd_busy;
379
380 pos = curwin->w_cursor;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100381 listcmd_busy = TRUE; // avoid that '' is changed
Bram Moolenaar8b96d642005-09-05 22:05:30 +0000382 if (findpar(&oa.inclusive,
383 c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000384 {
385 pos_copy = curwin->w_cursor;
386 posp = &pos_copy;
387 }
388 curwin->w_cursor = pos;
389 listcmd_busy = slcb;
390 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100391 else if (c == '(' || c == ')') // to previous/next sentence
Bram Moolenaar071d4272004-06-13 20:20:40 +0000392 {
393 pos_T pos;
394 int slcb = listcmd_busy;
395
396 pos = curwin->w_cursor;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100397 listcmd_busy = TRUE; // avoid that '' is changed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
399 {
400 pos_copy = curwin->w_cursor;
401 posp = &pos_copy;
402 }
403 curwin->w_cursor = pos;
404 listcmd_busy = slcb;
405 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100406 else if (c == '<' || c == '>') // start/end of visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +0000407 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100408 startp = &buf->b_visual.vi_start;
409 endp = &buf->b_visual.vi_end;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100410 if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100411 && startp->lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000412 posp = startp;
413 else
414 posp = endp;
415 /*
416 * For Visual line mode, set mark at begin or end of line
417 */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100418 if (buf->b_visual.vi_mode == 'V')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419 {
420 pos_copy = *posp;
421 posp = &pos_copy;
422 if (c == '<')
423 pos_copy.col = 0;
424 else
425 pos_copy.col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426 pos_copy.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427 }
428 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100429 else if (ASCII_ISLOWER(c)) // normal named mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100431 posp = &(buf->b_namedm[c - 'a']);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000432 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100433 else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) // named file mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434 {
435 if (VIM_ISDIGIT(c))
436 c = c - '0' + NMARKS;
437 else
438 c -= 'A';
439 posp = &(namedfm[c].fmark.mark);
440
441 if (namedfm[c].fmark.fnum == 0)
442 fname2fnum(&namedfm[c]);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000443
444 if (fnum != NULL)
445 *fnum = namedfm[c].fmark.fnum;
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100446 else if (namedfm[c].fmark.fnum != buf->b_fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100448 // mark is in another file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 posp = &pos_copy;
450
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451 if (namedfm[c].fmark.mark.lnum != 0
452 && changefile && namedfm[c].fmark.fnum)
453 {
454 if (buflist_getfile(namedfm[c].fmark.fnum,
455 (linenr_T)1, GETF_SETMARK, FALSE) == OK)
456 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100457 // Set the lnum now, autocommands could have changed it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000458 curwin->w_cursor = namedfm[c].fmark.mark;
459 return (pos_T *)-1;
460 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100461 pos_copy.lnum = -1; // can't get file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000462 }
463 else
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100464 pos_copy.lnum = 0; // mark exists, but is not valid in
465 // current buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000466 }
467 }
468
469 return posp;
470}
471
472/*
473 * Search for the next named mark in the current file.
474 *
475 * Returns pointer to pos_T of the next mark or NULL if no mark is found.
476 */
477 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100478getnextmark(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100479 pos_T *startpos, // where to start
480 int dir, // direction for search
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100481 int begin_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000482{
483 int i;
484 pos_T *result = NULL;
485 pos_T pos;
486
487 pos = *startpos;
488
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100489 // When searching backward and leaving the cursor on the first non-blank,
490 // position must be in a previous line.
491 // When searching forward and leaving the cursor on the first non-blank,
492 // position must be in a next line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000493 if (dir == BACKWARD && begin_line)
494 pos.col = 0;
495 else if (dir == FORWARD && begin_line)
496 pos.col = MAXCOL;
497
498 for (i = 0; i < NMARKS; i++)
499 {
500 if (curbuf->b_namedm[i].lnum > 0)
501 {
502 if (dir == FORWARD)
503 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100504 if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
505 && LT_POS(pos, curbuf->b_namedm[i]))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 result = &curbuf->b_namedm[i];
507 }
508 else
509 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100510 if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
511 && LT_POS(curbuf->b_namedm[i], pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 result = &curbuf->b_namedm[i];
513 }
514 }
515 }
516
517 return result;
518}
519
520/*
521 * For an xtended filemark: set the fnum from the fname.
522 * This is used for marks obtained from the .viminfo file. It's postponed
523 * until the mark is used to avoid a long startup delay.
524 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200525 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100526fname2fnum(xfmark_T *fm)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527{
528 char_u *p;
529
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000530 if (fm->fname == NULL)
531 return;
532
533 /*
534 * First expand "~/" in the file name to the home directory.
535 * Don't expand the whole name, it may contain other '~' chars.
536 */
537 if (fm->fname[0] == '~' && (fm->fname[1] == '/'
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000538#ifdef BACKSLASH_IN_FILENAME
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000539 || fm->fname[1] == '\\'
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000540#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000541 ))
542 {
John Marriottfff01322025-06-18 18:15:31 +0200543 size_t len;
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000544
John Marriottfff01322025-06-18 18:15:31 +0200545 len = expand_env((char_u *)"~/", NameBuff, MAXPATHL);
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000546 vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000547 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000548 else
549 vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
550
551 // Try to shorten the file name.
552 mch_dirname(IObuff, IOSIZE);
553 p = shorten_fname(NameBuff, IObuff);
554
555 // buflist_new() will call fmarks_check_names()
556 (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000557}
558
559/*
560 * Check all file marks for a name that matches the file name in buf.
561 * May replace the name with an fnum.
562 * Used for marks that come from the .viminfo file.
563 */
564 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100565fmarks_check_names(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000566{
567 char_u *name;
568 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000569 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000570
571 if (buf->b_ffname == NULL)
572 return;
573
574 name = home_replace_save(buf, buf->b_ffname);
575 if (name == NULL)
576 return;
577
578 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
579 fmarks_check_one(&namedfm[i], name, buf);
580
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 FOR_ALL_WINDOWS(wp)
582 {
583 for (i = 0; i < wp->w_jumplistlen; ++i)
584 fmarks_check_one(&wp->w_jumplist[i], name, buf);
585 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000586
587 vim_free(name);
588}
589
590 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100591fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592{
593 if (fm->fmark.fnum == 0
594 && fm->fname != NULL
595 && fnamecmp(name, fm->fname) == 0)
596 {
597 fm->fmark.fnum = buf->b_fnum;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100598 VIM_CLEAR(fm->fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000599 }
600}
601
602/*
603 * Check a if a position from a mark is valid.
604 * Give and error message and return FAIL if not.
605 */
606 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100607check_mark(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000608{
609 if (pos == NULL)
610 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +0000611 emsg(_(e_unknown_mark));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612 return FAIL;
613 }
614 if (pos->lnum <= 0)
615 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100616 // lnum is negative if mark is in another file can can't get that
617 // file, error message already give then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 if (pos->lnum == 0)
Bram Moolenaar108010a2021-06-27 22:03:33 +0200619 emsg(_(e_mark_not_set));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000620 return FAIL;
621 }
622 if (pos->lnum > curbuf->b_ml.ml_line_count)
623 {
Bram Moolenaar108010a2021-06-27 22:03:33 +0200624 emsg(_(e_mark_has_invalid_line_number));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 return FAIL;
626 }
627 return OK;
628}
629
630/*
631 * clrallmarks() - clear all marks in the buffer 'buf'
632 *
633 * Used mainly when trashing the entire buffer during ":e" type commands
634 */
635 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100636clrallmarks(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000637{
638 static int i = -1;
639
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100640 if (i == -1) // first call ever: initialize
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641 for (i = 0; i < NMARKS + 1; i++)
642 {
643 namedfm[i].fmark.mark.lnum = 0;
644 namedfm[i].fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200645#ifdef FEAT_VIMINFO
646 namedfm[i].time_set = 0;
647#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 }
649
650 for (i = 0; i < NMARKS; i++)
651 buf->b_namedm[i].lnum = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100652 buf->b_op_start.lnum = 0; // start/end op mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000653 buf->b_op_end.lnum = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100654 buf->b_last_cursor.lnum = 1; // '" mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000655 buf->b_last_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000656 buf->b_last_cursor.coladd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100657 buf->b_last_insert.lnum = 0; // '^ mark cleared
658 buf->b_last_change.lnum = 0; // '. mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000659 buf->b_changelistlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000660}
661
662/*
663 * Get name of file from a filemark.
664 * When it's in the current buffer, return the text at the mark.
665 * Returns an allocated string.
666 */
667 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100668fm_getname(fmark_T *fmark, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000669{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100670 if (fmark->fnum == curbuf->b_fnum) // current buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000671 return mark_line(&(fmark->mark), lead_len);
672 return buflist_nr2name(fmark->fnum, FALSE, TRUE);
673}
674
675/*
676 * Return the line at mark "mp". Truncate to fit in window.
677 * The returned string has been allocated.
678 */
679 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100680mark_line(pos_T *mp, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000681{
682 char_u *s, *p;
683 int len;
684
685 if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
686 return vim_strsave((char_u *)"-invalid-");
Bram Moolenaar9d5185b2018-07-08 17:57:34 +0200687 // Allow for up to 5 bytes per character.
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200688 s = vim_strnsave(skipwhite(ml_get(mp->lnum)), Columns * 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689 if (s == NULL)
690 return NULL;
Bram Moolenaar9d5185b2018-07-08 17:57:34 +0200691 // Truncate the line to fit it in the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000692 len = 0;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100693 for (p = s; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000694 {
695 len += ptr2cells(p);
696 if (len >= Columns - lead_len)
697 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000698 }
699 *p = NUL;
700 return s;
701}
702
703/*
704 * print the marks
705 */
706 void
Bram Moolenaar4bd78232019-09-19 23:21:55 +0200707ex_marks(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000708{
709 char_u *arg = eap->arg;
710 int i;
711 char_u *name;
Bram Moolenaar54c3fcd2020-07-19 22:09:06 +0200712 pos_T *posp, *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713
714 if (arg != NULL && *arg == NUL)
715 arg = NULL;
716
717 show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
718 for (i = 0; i < NMARKS; ++i)
719 show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
720 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
721 {
722 if (namedfm[i].fmark.fnum != 0)
723 name = fm_getname(&namedfm[i].fmark, 15);
724 else
725 name = namedfm[i].fname;
726 if (name != NULL)
727 {
728 show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
729 arg, &namedfm[i].fmark.mark, name,
730 namedfm[i].fmark.fnum == curbuf->b_fnum);
731 if (namedfm[i].fmark.fnum != 0)
732 vim_free(name);
733 }
734 }
735 show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
736 show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
737 show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
738 show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
739 show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
Bram Moolenaar54c3fcd2020-07-19 22:09:06 +0200740
741 // Show the marks as where they will jump to.
742 startp = &curbuf->b_visual.vi_start;
743 endp = &curbuf->b_visual.vi_end;
744 if ((LT_POS(*startp, *endp) || endp->lnum == 0) && startp->lnum != 0)
745 posp = startp;
746 else
747 posp = endp;
748 show_one_mark('<', arg, posp, NULL, TRUE);
749 show_one_mark('>', arg, posp == startp ? endp : startp, NULL, TRUE);
750
Bram Moolenaar071d4272004-06-13 20:20:40 +0000751 show_one_mark(-1, arg, NULL, NULL, FALSE);
752}
753
754 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100755show_one_mark(
756 int c,
757 char_u *arg,
758 pos_T *p,
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200759 char_u *name_arg,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100760 int current) // in current file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000761{
762 static int did_title = FALSE;
763 int mustfree = FALSE;
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200764 char_u *name = name_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000765
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100766 if (c == -1) // finish up
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 {
768 if (did_title)
769 did_title = FALSE;
770 else
771 {
772 if (arg == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100773 msg(_("No marks set"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 else
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000775 semsg(_(e_no_marks_matching_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 }
777 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200778 // don't output anything if 'q' typed at --more-- prompt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 else if (!got_int
780 && (arg == NULL || vim_strchr(arg, c) != NULL)
781 && p->lnum != 0)
782 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200783 if (name == NULL && current)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000784 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200785 name = mark_line(p, 15);
John Marriott46b02602025-06-22 19:44:27 +0200786 if (name == NULL)
787 {
788 emsg(_(e_out_of_memory));
789 return;
790 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200791 mustfree = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000792 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200793 if (!message_filtered(name))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000794 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200795 if (!did_title)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000796 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200797 // Highlight title
798 msg_puts_title(_("\nmark line col file/text"));
799 did_title = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200801 msg_putchar('\n');
802 if (!got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200804 sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
805 msg_outtrans(IObuff);
806 if (name != NULL)
807 {
808 msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
809 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000810 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200811 out_flush(); // show one line at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000812 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200813 if (mustfree)
814 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000815 }
816}
817
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000818/*
819 * ":delmarks[!] [marks]"
820 */
821 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100822ex_delmarks(exarg_T *eap)
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000823{
824 char_u *p;
825 int from, to;
826 int i;
827 int lower;
828 int digit;
829 int n;
830
831 if (*eap->arg == NUL && eap->forceit)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100832 // clear all marks
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000833 clrallmarks(curbuf);
834 else if (eap->forceit)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000835 emsg(_(e_invalid_argument));
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000836 else if (*eap->arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000837 emsg(_(e_argument_required));
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000838 else
839 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100840 // clear specified marks only
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000841 for (p = eap->arg; *p != NUL; ++p)
842 {
843 lower = ASCII_ISLOWER(*p);
844 digit = VIM_ISDIGIT(*p);
845 if (lower || digit || ASCII_ISUPPER(*p))
846 {
847 if (p[1] == '-')
848 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100849 // clear range of marks
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000850 from = *p;
851 to = p[2];
852 if (!(lower ? ASCII_ISLOWER(p[2])
853 : (digit ? VIM_ISDIGIT(p[2])
854 : ASCII_ISUPPER(p[2])))
855 || to < from)
856 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000857 semsg(_(e_invalid_argument_str), p);
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000858 return;
859 }
860 p += 2;
861 }
862 else
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100863 // clear one lower case mark
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000864 from = to = *p;
865
866 for (i = from; i <= to; ++i)
867 {
868 if (lower)
869 curbuf->b_namedm[i - 'a'].lnum = 0;
870 else
871 {
872 if (digit)
873 n = i - '0' + NMARKS;
874 else
875 n = i - 'A';
876 namedfm[n].fmark.mark.lnum = 0;
Bram Moolenaar8cd6cd82019-12-27 17:33:26 +0100877 namedfm[n].fmark.fnum = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100878 VIM_CLEAR(namedfm[n].fname);
Bram Moolenaar2d358992016-06-12 21:20:54 +0200879#ifdef FEAT_VIMINFO
Bram Moolenaar8cd6cd82019-12-27 17:33:26 +0100880 namedfm[n].time_set = digit ? 0 : vim_time();
Bram Moolenaar2d358992016-06-12 21:20:54 +0200881#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000882 }
883 }
884 }
885 else
886 switch (*p)
887 {
888 case '"': curbuf->b_last_cursor.lnum = 0; break;
889 case '^': curbuf->b_last_insert.lnum = 0; break;
890 case '.': curbuf->b_last_change.lnum = 0; break;
891 case '[': curbuf->b_op_start.lnum = 0; break;
892 case ']': curbuf->b_op_end.lnum = 0; break;
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000893 case '<': curbuf->b_visual.vi_start.lnum = 0; break;
894 case '>': curbuf->b_visual.vi_end.lnum = 0; break;
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000895 case ' ': break;
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000896 default: semsg(_(e_invalid_argument_str), p);
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000897 return;
898 }
899 }
900 }
901}
902
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903/*
904 * print the jumplist
905 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100907ex_jumps(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000908{
909 int i;
910 char_u *name;
911
Bram Moolenaar48679742018-02-13 13:33:29 +0100912 cleanup_jumplist(curwin, TRUE);
Bram Moolenaara7e18d22018-02-11 14:29:49 +0100913
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100914 // Highlight title
Bram Moolenaar32526b32019-01-19 17:43:09 +0100915 msg_puts_title(_("\n jump line col file/text"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000916 for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
917 {
918 if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
919 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920 name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
Bram Moolenaarf86db782018-10-25 13:31:37 +0200921
Christian Brabandta0f659c2022-04-09 13:35:00 +0100922 // Make sure to output the current indicator, even when on an wiped
923 // out buffer. ":filter" may still skip it.
924 if (name == NULL && i == curwin->w_jumplistidx)
925 name = vim_strsave((char_u *)"-invalid-");
Bram Moolenaarf86db782018-10-25 13:31:37 +0200926 // apply :filter /pat/ or file name not available
927 if (name == NULL || message_filtered(name))
Bram Moolenaard93090f2019-01-27 15:07:39 +0100928 {
929 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 continue;
Bram Moolenaard93090f2019-01-27 15:07:39 +0100931 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000932
933 msg_putchar('\n');
934 if (got_int)
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000935 {
936 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000937 break;
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000938 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000939 sprintf((char *)IObuff, "%c %2d %5ld %4d ",
940 i == curwin->w_jumplistidx ? '>' : ' ',
941 i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
942 : curwin->w_jumplistidx - i,
943 curwin->w_jumplist[i].fmark.mark.lnum,
944 curwin->w_jumplist[i].fmark.mark.col);
945 msg_outtrans(IObuff);
946 msg_outtrans_attr(name,
947 curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
Bram Moolenaar8820b482017-03-16 17:23:31 +0100948 ? HL_ATTR(HLF_D) : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000949 vim_free(name);
950 ui_breakcheck();
951 }
952 out_flush();
953 }
954 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100955 msg_puts("\n>");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000956}
957
Bram Moolenaar2d358992016-06-12 21:20:54 +0200958 void
959ex_clearjumps(exarg_T *eap UNUSED)
960{
961 free_jumplist(curwin);
962 curwin->w_jumplistlen = 0;
963 curwin->w_jumplistidx = 0;
964}
965
Bram Moolenaar071d4272004-06-13 20:20:40 +0000966/*
967 * print the changelist
968 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000969 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100970ex_changes(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971{
972 int i;
973 char_u *name;
974
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100975 // Highlight title
Bram Moolenaar32526b32019-01-19 17:43:09 +0100976 msg_puts_title(_("\nchange line col text"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977
978 for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i)
979 {
980 if (curbuf->b_changelist[i].lnum != 0)
981 {
982 msg_putchar('\n');
983 if (got_int)
984 break;
985 sprintf((char *)IObuff, "%c %3d %5ld %4d ",
986 i == curwin->w_changelistidx ? '>' : ' ',
987 i > curwin->w_changelistidx ? i - curwin->w_changelistidx
988 : curwin->w_changelistidx - i,
989 (long)curbuf->b_changelist[i].lnum,
990 curbuf->b_changelist[i].col);
991 msg_outtrans(IObuff);
992 name = mark_line(&curbuf->b_changelist[i], 17);
993 if (name == NULL)
994 break;
Bram Moolenaar8820b482017-03-16 17:23:31 +0100995 msg_outtrans_attr(name, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996 vim_free(name);
997 ui_breakcheck();
998 }
999 out_flush();
1000 }
1001 if (curwin->w_changelistidx == curbuf->b_changelistlen)
Bram Moolenaar32526b32019-01-19 17:43:09 +01001002 msg_puts("\n>");
Bram Moolenaar071d4272004-06-13 20:20:40 +00001003}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001004
1005#define one_adjust(add) \
1006 { \
1007 lp = add; \
1008 if (*lp >= line1 && *lp <= line2) \
1009 { \
1010 if (amount == MAXLNUM) \
1011 *lp = 0; \
1012 else \
1013 *lp += amount; \
1014 } \
1015 else if (amount_after && *lp > line2) \
1016 *lp += amount_after; \
1017 }
1018
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001019// don't delete the line, just put at first deleted line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001020#define one_adjust_nodel(add) \
1021 { \
1022 lp = add; \
1023 if (*lp >= line1 && *lp <= line2) \
1024 { \
1025 if (amount == MAXLNUM) \
1026 *lp = line1; \
1027 else \
1028 *lp += amount; \
1029 } \
1030 else if (amount_after && *lp > line2) \
1031 *lp += amount_after; \
1032 }
1033
1034/*
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001035 * Adjust marks between "line1" and "line2" (inclusive) to move "amount" lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001036 * Must be called before changed_*(), appended_lines() or deleted_lines().
1037 * May be called before or after changing the text.
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001038 * When deleting lines "line1" to "line2", use an "amount" of MAXLNUM: The
1039 * marks within this range are made invalid.
1040 * If "amount_after" is non-zero adjust marks after "line2".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001041 * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1042 * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1043 * or: mark_adjust(56, 55, MAXLNUM, 2);
1044 */
1045 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001046mark_adjust(
1047 linenr_T line1,
1048 linenr_T line2,
1049 long amount,
1050 long amount_after)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001051{
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001052 mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1053}
1054
1055 void
1056mark_adjust_nofold(
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01001057 linenr_T line1,
1058 linenr_T line2,
1059 long amount,
1060 long amount_after)
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001061{
1062 mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1063}
1064
1065 static void
1066mark_adjust_internal(
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01001067 linenr_T line1,
1068 linenr_T line2,
1069 long amount,
1070 long amount_after,
1071 int adjust_folds UNUSED)
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001072{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073 int i;
1074 int fnum = curbuf->b_fnum;
1075 linenr_T *lp;
1076 win_T *win;
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001077 tabpage_T *tab;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01001078 static pos_T initpos = {1, 0, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +00001079
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001080 if (line2 < line1 && amount_after == 0L) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001081 return;
1082
Bram Moolenaare1004402020-10-24 20:49:43 +02001083 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001085 // named marks, lower case and upper case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001086 for (i = 0; i < NMARKS; i++)
1087 {
1088 one_adjust(&(curbuf->b_namedm[i].lnum));
1089 if (namedfm[i].fmark.fnum == fnum)
1090 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1091 }
1092 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1093 {
1094 if (namedfm[i].fmark.fnum == fnum)
1095 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1096 }
1097
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001098 // last Insert position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001099 one_adjust(&(curbuf->b_last_insert.lnum));
1100
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001101 // last change position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001102 one_adjust(&(curbuf->b_last_change.lnum));
1103
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001104 // last cursor position, if it was set
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001105 if (!EQUAL_POS(curbuf->b_last_cursor, initpos))
Bram Moolenaarb6a76ff2013-02-06 12:33:21 +01001106 one_adjust(&(curbuf->b_last_cursor.lnum));
1107
1108
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001109 // list of change positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001110 for (i = 0; i < curbuf->b_changelistlen; ++i)
1111 one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001112
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001113 // Visual area
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001114 one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1115 one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001116
1117#ifdef FEAT_QUICKFIX
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001118 // quickfix marks
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001119 qf_mark_adjust(NULL, line1, line2, amount, amount_after);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001120 // location lists
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001121 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001122 qf_mark_adjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001123#endif
1124
1125#ifdef FEAT_SIGNS
1126 sign_mark_adjust(line1, line2, amount, amount_after);
1127#endif
1128 }
1129
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001130 // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001131 one_adjust(&(curwin->w_pcmark.lnum));
1132
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001133 // previous pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134 one_adjust(&(curwin->w_prev_pcmark.lnum));
1135
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001136 // saved cursor for formatting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001137 if (saved_cursor.lnum != 0)
1138 one_adjust_nodel(&(saved_cursor.lnum));
1139
1140 /*
1141 * Adjust items in all windows related to the current buffer.
1142 */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001143 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001144 {
Bram Moolenaare1004402020-10-24 20:49:43 +02001145 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001146 // Marks in the jumplist. When deleting lines, this may create
1147 // duplicate marks in the jumplist, they will be removed later.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001148 for (i = 0; i < win->w_jumplistlen; ++i)
1149 if (win->w_jumplist[i].fmark.fnum == fnum)
1150 one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001151
1152 if (win->w_buffer == curbuf)
1153 {
Bram Moolenaare1004402020-10-24 20:49:43 +02001154 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001155 // marks in the tag stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001156 for (i = 0; i < win->w_tagstacklen; i++)
1157 if (win->w_tagstack[i].fmark.fnum == fnum)
1158 one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1159
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001160 // the displayed Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00001161 if (win->w_old_cursor_lnum != 0)
1162 {
1163 one_adjust_nodel(&(win->w_old_cursor_lnum));
1164 one_adjust_nodel(&(win->w_old_visual_lnum));
1165 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001167 // topline and cursor position for windows with the same buffer
1168 // other than the current window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001169 if (win != curwin)
1170 {
1171 if (win->w_topline >= line1 && win->w_topline <= line2)
1172 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001173 if (amount == MAXLNUM) // topline is deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00001174 {
1175 if (line1 <= 1)
1176 win->w_topline = 1;
1177 else
1178 win->w_topline = line1 - 1;
1179 }
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001180 else if (win->w_topline > line1)
1181 // keep topline on the same line, unless inserting just
1182 // above it (we probably want to see that line then)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001183 win->w_topline += amount;
1184#ifdef FEAT_DIFF
1185 win->w_topfill = 0;
1186#endif
1187 }
1188 else if (amount_after && win->w_topline > line2)
1189 {
1190 win->w_topline += amount_after;
1191#ifdef FEAT_DIFF
1192 win->w_topfill = 0;
1193#endif
1194 }
1195 if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1196 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001197 if (amount == MAXLNUM) // line with cursor is deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00001198 {
1199 if (line1 <= 1)
1200 win->w_cursor.lnum = 1;
1201 else
1202 win->w_cursor.lnum = line1 - 1;
1203 win->w_cursor.col = 0;
1204 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001205 else // keep cursor on the same line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 win->w_cursor.lnum += amount;
1207 }
1208 else if (amount_after && win->w_cursor.lnum > line2)
1209 win->w_cursor.lnum += amount_after;
1210 }
1211
1212#ifdef FEAT_FOLDING
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001213 // adjust folds
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001214 if (adjust_folds)
1215 foldMarkAdjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001216#endif
1217 }
1218 }
1219
1220#ifdef FEAT_DIFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001221 // adjust diffs
Bram Moolenaar071d4272004-06-13 20:20:40 +00001222 diff_mark_adjust(line1, line2, amount, amount_after);
1223#endif
1224}
1225
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001226// This code is used often, needs to be fast.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001227#define col_adjust(pp) \
1228 { \
1229 posp = pp; \
1230 if (posp->lnum == lnum && posp->col >= mincol) \
1231 { \
1232 posp->lnum += lnum_amount; \
1233 if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1234 posp->col = 0; \
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001235 else if (posp->col < spaces_removed) \
1236 posp->col = col_amount + spaces_removed; \
Bram Moolenaar071d4272004-06-13 20:20:40 +00001237 else \
1238 posp->col += col_amount; \
1239 } \
1240 }
1241
1242/*
1243 * Adjust marks in line "lnum" at column "mincol" and further: add
1244 * "lnum_amount" to the line number and add "col_amount" to the column
1245 * position.
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001246 * "spaces_removed" is the number of spaces that were removed, matters when the
1247 * cursor is inside them.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001248 */
1249 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001250mark_col_adjust(
1251 linenr_T lnum,
1252 colnr_T mincol,
1253 long lnum_amount,
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001254 long col_amount,
1255 int spaces_removed)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256{
1257 int i;
1258 int fnum = curbuf->b_fnum;
1259 win_T *win;
1260 pos_T *posp;
1261
Bram Moolenaare1004402020-10-24 20:49:43 +02001262 if ((col_amount == 0L && lnum_amount == 0L)
1263 || (cmdmod.cmod_flags & CMOD_LOCKMARKS))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001264 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001265
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001266 // named marks, lower case and upper case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 for (i = 0; i < NMARKS; i++)
1268 {
1269 col_adjust(&(curbuf->b_namedm[i]));
1270 if (namedfm[i].fmark.fnum == fnum)
1271 col_adjust(&(namedfm[i].fmark.mark));
1272 }
1273 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1274 {
1275 if (namedfm[i].fmark.fnum == fnum)
1276 col_adjust(&(namedfm[i].fmark.mark));
1277 }
1278
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001279 // last Insert position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001280 col_adjust(&(curbuf->b_last_insert));
1281
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001282 // last change position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001283 col_adjust(&(curbuf->b_last_change));
1284
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001285 // list of change positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 for (i = 0; i < curbuf->b_changelistlen; ++i)
1287 col_adjust(&(curbuf->b_changelist[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001289 // Visual area
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001290 col_adjust(&(curbuf->b_visual.vi_start));
1291 col_adjust(&(curbuf->b_visual.vi_end));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001292
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001293 // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001294 col_adjust(&(curwin->w_pcmark));
1295
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001296 // previous pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001297 col_adjust(&(curwin->w_prev_pcmark));
1298
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001299 // saved cursor for formatting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001300 col_adjust(&saved_cursor);
1301
1302 /*
1303 * Adjust items in all windows related to the current buffer.
1304 */
1305 FOR_ALL_WINDOWS(win)
1306 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001307 // marks in the jumplist
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 for (i = 0; i < win->w_jumplistlen; ++i)
1309 if (win->w_jumplist[i].fmark.fnum == fnum)
1310 col_adjust(&(win->w_jumplist[i].fmark.mark));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001311
1312 if (win->w_buffer == curbuf)
1313 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001314 // marks in the tag stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315 for (i = 0; i < win->w_tagstacklen; i++)
1316 if (win->w_tagstack[i].fmark.fnum == fnum)
1317 col_adjust(&(win->w_tagstack[i].fmark.mark));
1318
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001319 // cursor position for other windows with the same buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001320 if (win != curwin)
1321 col_adjust(&win->w_cursor);
1322 }
1323 }
1324}
1325
Bram Moolenaar071d4272004-06-13 20:20:40 +00001326/*
1327 * When deleting lines, this may create duplicate marks in the
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001328 * jumplist. They will be removed here for the specified window.
Bram Moolenaar48679742018-02-13 13:33:29 +01001329 * When "loadfiles" is TRUE first ensure entries have the "fnum" field set
1330 * (this may be a bit slow).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001331 */
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001332 void
Bram Moolenaar48679742018-02-13 13:33:29 +01001333cleanup_jumplist(win_T *wp, int loadfiles)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001334{
1335 int i;
1336 int from, to;
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001337 int mustfree;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001338
Bram Moolenaar48679742018-02-13 13:33:29 +01001339 if (loadfiles)
1340 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001341 // If specified, load all the files from the jump list. This is
1342 // needed to properly clean up duplicate entries, but will take some
1343 // time.
Bram Moolenaar48679742018-02-13 13:33:29 +01001344 for (i = 0; i < wp->w_jumplistlen; ++i)
1345 {
1346 if ((wp->w_jumplist[i].fmark.fnum == 0) &&
1347 (wp->w_jumplist[i].fmark.mark.lnum != 0))
1348 fname2fnum(&wp->w_jumplist[i]);
1349 }
1350 }
1351
Bram Moolenaar071d4272004-06-13 20:20:40 +00001352 to = 0;
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001353 for (from = 0; from < wp->w_jumplistlen; ++from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001354 {
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001355 if (wp->w_jumplistidx == from)
1356 wp->w_jumplistidx = to;
1357 for (i = from + 1; i < wp->w_jumplistlen; ++i)
1358 if (wp->w_jumplist[i].fmark.fnum
1359 == wp->w_jumplist[from].fmark.fnum
1360 && wp->w_jumplist[from].fmark.fnum != 0
1361 && wp->w_jumplist[i].fmark.mark.lnum
1362 == wp->w_jumplist[from].fmark.mark.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001363 break;
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001364 if (i >= wp->w_jumplistlen) // not duplicate
1365 mustfree = FALSE;
1366 else if (i > from + 1) // non-adjacent duplicate
1367 // jumpoptions=stack: remove duplicates only when adjacent.
1368 mustfree = !(jop_flags & JOP_STACK);
1369 else // adjacent duplicate
1370 mustfree = TRUE;
1371
1372 if (mustfree)
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001373 vim_free(wp->w_jumplist[from].fname);
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001374 else
1375 wp->w_jumplist[to++] = wp->w_jumplist[from];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 }
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001377 if (wp->w_jumplistidx == wp->w_jumplistlen)
1378 wp->w_jumplistidx = to;
1379 wp->w_jumplistlen = to;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380}
1381
Bram Moolenaar071d4272004-06-13 20:20:40 +00001382/*
1383 * Copy the jumplist from window "from" to window "to".
1384 */
1385 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001386copy_jumplist(win_T *from, win_T *to)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001387{
1388 int i;
1389
1390 for (i = 0; i < from->w_jumplistlen; ++i)
1391 {
1392 to->w_jumplist[i] = from->w_jumplist[i];
1393 if (from->w_jumplist[i].fname != NULL)
1394 to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1395 }
1396 to->w_jumplistlen = from->w_jumplistlen;
1397 to->w_jumplistidx = from->w_jumplistidx;
1398}
1399
1400/*
1401 * Free items in the jumplist of window "wp".
1402 */
1403 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001404free_jumplist(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001405{
1406 int i;
1407
1408 for (i = 0; i < wp->w_jumplistlen; ++i)
1409 vim_free(wp->w_jumplist[i].fname);
1410}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001411
1412 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001413set_last_cursor(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414{
Bram Moolenaar9db12932013-11-03 00:20:52 +01001415 if (win->w_buffer != NULL)
1416 win->w_buffer->b_last_cursor = win->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001417}
1418
Bram Moolenaarea408852005-06-25 22:49:46 +00001419#if defined(EXITFREE) || defined(PROTO)
1420 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001421free_all_marks(void)
Bram Moolenaarea408852005-06-25 22:49:46 +00001422{
1423 int i;
1424
1425 for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1426 if (namedfm[i].fmark.mark.lnum != 0)
1427 vim_free(namedfm[i].fname);
1428}
1429#endif
1430
Dominique Pelle748b3082022-01-08 12:41:16 +00001431#if defined(FEAT_VIMINFO) || defined(PROTO)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001432/*
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001433 * Return a pointer to the named file marks.
Bram Moolenaar2d358992016-06-12 21:20:54 +02001434 */
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001435 xfmark_T *
1436get_namedfm(void)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001437{
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001438 return namedfm;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001439}
Dominique Pelle748b3082022-01-08 12:41:16 +00001440#endif
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001441
1442#if defined(FEAT_EVAL) || defined(PROTO)
1443/*
1444 * Add information about mark 'mname' to list 'l'
1445 */
1446 static int
1447add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
1448{
1449 dict_T *d;
1450 list_T *lpos;
1451
1452 if (pos->lnum <= 0)
1453 return OK;
1454
1455 d = dict_alloc();
1456 if (d == NULL)
1457 return FAIL;
1458
1459 if (list_append_dict(l, d) == FAIL)
1460 {
1461 dict_unref(d);
1462 return FAIL;
1463 }
1464
1465 lpos = list_alloc();
1466 if (lpos == NULL)
1467 return FAIL;
1468
1469 list_append_number(lpos, bufnr);
1470 list_append_number(lpos, pos->lnum);
zeertzjq93318a92025-06-16 20:10:38 +02001471 list_append_number(lpos, pos->col < MAXCOL ? pos->col + 1 : MAXCOL);
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001472 list_append_number(lpos, pos->coladd);
1473
1474 if (dict_add_string(d, "mark", mname) == FAIL
1475 || dict_add_list(d, "pos", lpos) == FAIL
1476 || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
1477 return FAIL;
1478
1479 return OK;
1480}
1481
1482/*
1483 * Get information about marks local to a buffer.
1484 */
1485 static void
1486get_buf_local_marks(buf_T *buf, list_T *l)
1487{
1488 char_u mname[3] = "' ";
1489 int i;
1490
1491 // Marks 'a' to 'z'
1492 for (i = 0; i < NMARKS; ++i)
1493 {
1494 mname[1] = 'a' + i;
1495 add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
1496 }
1497
1498 // Mark '' is a window local mark and not a buffer local mark
1499 add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
1500
1501 add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
1502 add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
1503 add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
1504 add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
1505 add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
1506 add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
1507 add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
1508}
1509
1510/*
1511 * Get information about global marks ('A' to 'Z' and '0' to '9')
1512 */
1513 static void
1514get_global_marks(list_T *l)
1515{
1516 char_u mname[3] = "' ";
1517 int i;
1518 char_u *name;
1519
1520 // Marks 'A' to 'Z' and '0' to '9'
1521 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
1522 {
1523 if (namedfm[i].fmark.fnum != 0)
1524 name = buflist_nr2name(namedfm[i].fmark.fnum, TRUE, TRUE);
1525 else
1526 name = namedfm[i].fname;
1527 if (name != NULL)
1528 {
1529 mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
1530 add_mark(l, mname, &namedfm[i].fmark.mark,
1531 namedfm[i].fmark.fnum, name);
1532 if (namedfm[i].fmark.fnum != 0)
1533 vim_free(name);
1534 }
1535 }
1536}
1537
1538/*
1539 * getmarklist() function
1540 */
1541 void
1542f_getmarklist(typval_T *argvars, typval_T *rettv)
1543{
1544 buf_T *buf = NULL;
1545
Bram Moolenaar8088ae92022-06-20 11:38:17 +01001546 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001547 return;
1548
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001549 if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
1550 return;
1551
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001552 if (argvars[0].v_type == VAR_UNKNOWN)
1553 {
1554 get_global_marks(rettv->vval.v_list);
1555 return;
1556 }
1557
1558 buf = tv_get_buf(&argvars[0], FALSE);
1559 if (buf == NULL)
1560 return;
1561
1562 get_buf_local_marks(buf, rettv->vval.v_list);
1563}
1564#endif