blob: 22e3c62575426089886dfe49befb6b9d77cd6d85 [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/*
133 * Set the previous context mark to the current position and add it to the
134 * jump list.
135 */
136 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100137setpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000138{
Bram Moolenaar071d4272004-06-13 20:20:40 +0000139 int i;
140 xfmark_T *fm;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000141
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100142 // for :global the mark is set only once
Bram Moolenaare1004402020-10-24 20:49:43 +0200143 if (global_busy || listcmd_busy || (cmdmod.cmod_flags & CMOD_KEEPJUMPS))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144 return;
145
146 curwin->w_prev_pcmark = curwin->w_pcmark;
147 curwin->w_pcmark = curwin->w_cursor;
148
Yegappan Lakshmanan87018252023-09-20 20:20:04 +0200149 if (jop_flags & JOP_STACK)
150 {
151 // jumpoptions=stack: if we're somewhere in the middle of the jumplist
152 // discard everything after the current index.
153 if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1)
154 // Discard the rest of the jumplist by cutting the length down to
155 // contain nothing beyond the current index.
156 curwin->w_jumplistlen = curwin->w_jumplistidx + 1;
157 }
158
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100159 // If jumplist is full: remove oldest entry
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160 if (++curwin->w_jumplistlen > JUMPLISTSIZE)
161 {
162 curwin->w_jumplistlen = JUMPLISTSIZE;
163 vim_free(curwin->w_jumplist[0].fname);
164 for (i = 1; i < JUMPLISTSIZE; ++i)
165 curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
166 }
167 curwin->w_jumplistidx = curwin->w_jumplistlen;
168 fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
169
170 fm->fmark.mark = curwin->w_pcmark;
171 fm->fmark.fnum = curbuf->b_fnum;
172 fm->fname = NULL;
Bram Moolenaar739f13a2021-12-13 13:12:53 +0000173#ifdef FEAT_VIMINFO
Bram Moolenaar2d358992016-06-12 21:20:54 +0200174 fm->time_set = vim_time();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000175#endif
176}
177
178/*
179 * To change context, call setpcmark(), then move the current position to
180 * where ever, then call checkpcmark(). This ensures that the previous
181 * context will only be changed if the cursor moved to a different line.
182 * If pcmark was deleted (with "dG") the previous mark is restored.
183 */
184 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100185checkpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000186{
187 if (curwin->w_prev_pcmark.lnum != 0
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100188 && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000189 || curwin->w_pcmark.lnum == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190 curwin->w_pcmark = curwin->w_prev_pcmark;
Bram Moolenaare08aee62021-10-17 21:53:58 +0100191 curwin->w_prev_pcmark.lnum = 0; // it has been checked
Bram Moolenaar071d4272004-06-13 20:20:40 +0000192}
193
Bram Moolenaar071d4272004-06-13 20:20:40 +0000194/*
195 * move "count" positions in the jump list (count may be negative)
196 */
197 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100198movemark(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199{
200 pos_T *pos;
201 xfmark_T *jmp;
202
Bram Moolenaar48679742018-02-13 13:33:29 +0100203 cleanup_jumplist(curwin, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000204
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100205 if (curwin->w_jumplistlen == 0) // nothing to jump to
Bram Moolenaar071d4272004-06-13 20:20:40 +0000206 return (pos_T *)NULL;
207
208 for (;;)
209 {
210 if (curwin->w_jumplistidx + count < 0
211 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
212 return (pos_T *)NULL;
213
214 /*
215 * if first CTRL-O or CTRL-I command after a jump, add cursor position
Bram Moolenaarf711faf2007-05-10 16:48:19 +0000216 * to list. Careful: If there are duplicates (CTRL-O immediately after
Bram Moolenaar071d4272004-06-13 20:20:40 +0000217 * starting Vim on a file), another entry may have been removed.
218 */
219 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
220 {
221 setpcmark();
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100222 --curwin->w_jumplistidx; // skip the new entry
Bram Moolenaar071d4272004-06-13 20:20:40 +0000223 if (curwin->w_jumplistidx + count < 0)
224 return (pos_T *)NULL;
225 }
226
227 curwin->w_jumplistidx += count;
228
229 jmp = curwin->w_jumplist + curwin->w_jumplistidx;
230 if (jmp->fmark.fnum == 0)
231 fname2fnum(jmp);
232 if (jmp->fmark.fnum != curbuf->b_fnum)
233 {
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100234 // Make a copy, an autocommand may make "jmp" invalid.
235 fmark_T fmark = jmp->fmark;
236
237 // jump to the file with the mark
238 if (buflist_findnr(fmark.fnum) == NULL)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100239 { // Skip this one ..
Bram Moolenaar071d4272004-06-13 20:20:40 +0000240 count += count < 0 ? -1 : 1;
241 continue;
242 }
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100243 if (buflist_getfile(fmark.fnum, fmark.mark.lnum, 0, FALSE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000244 return (pos_T *)NULL;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100245 // Set lnum again, autocommands my have changed it
Bram Moolenaar8ecfa2c2022-09-21 13:07:22 +0100246 curwin->w_cursor = fmark.mark;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000247 pos = (pos_T *)-1;
248 }
249 else
250 pos = &(jmp->fmark.mark);
251 return pos;
252 }
253}
254
255/*
256 * Move "count" positions in the changelist (count may be negative).
257 */
258 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100259movechangelist(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000260{
261 int n;
262
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100263 if (curbuf->b_changelistlen == 0) // nothing to jump to
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264 return (pos_T *)NULL;
265
266 n = curwin->w_changelistidx;
267 if (n + count < 0)
268 {
269 if (n == 0)
270 return (pos_T *)NULL;
271 n = 0;
272 }
273 else if (n + count >= curbuf->b_changelistlen)
274 {
275 if (n == curbuf->b_changelistlen - 1)
276 return (pos_T *)NULL;
277 n = curbuf->b_changelistlen - 1;
278 }
279 else
280 n += count;
281 curwin->w_changelistidx = n;
282 return curbuf->b_changelist + n;
283}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284
285/*
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100286 * Find mark "c" in buffer pointed to by "buf".
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000287 * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
288 * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
289 * another file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000290 * Returns:
291 * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
292 * in another file which can't be gotten. (caller needs to check lnum!)
293 * - NULL if there is no mark called 'c'.
294 * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
295 */
296 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100297getmark_buf(buf_T *buf, int c, int changefile)
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100298{
299 return getmark_buf_fnum(buf, c, changefile, NULL);
300}
301
302 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100303getmark(int c, int changefile)
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000304{
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100305 return getmark_buf_fnum(curbuf, c, changefile, NULL);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000306}
307
308 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100309getmark_buf_fnum(
310 buf_T *buf,
311 int c,
312 int changefile,
313 int *fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000314{
315 pos_T *posp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000316 pos_T *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317 static pos_T pos_copy;
318
319 posp = NULL;
320
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100321 // Check for special key, can't be a mark name and might cause islower()
322 // to crash.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000323 if (c < 0)
324 return posp;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100325 if (c > '~') // check for islower()/isupper()
Bram Moolenaar071d4272004-06-13 20:20:40 +0000326 ;
Bram Moolenaar424bcae2022-01-31 14:59:41 +0000327 else if (c == '\'' || c == '`') // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000328 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100329 pos_copy = curwin->w_pcmark; // need to make a copy because
330 posp = &pos_copy; // w_pcmark may be changed soon
Bram Moolenaar071d4272004-06-13 20:20:40 +0000331 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100332 else if (c == '"') // to pos when leaving buffer
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100333 posp = &(buf->b_last_cursor);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100334 else if (c == '^') // to where Insert mode stopped
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100335 posp = &(buf->b_last_insert);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100336 else if (c == '.') // to where last change was made
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100337 posp = &(buf->b_last_change);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100338 else if (c == '[') // to start of previous operator
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100339 posp = &(buf->b_op_start);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100340 else if (c == ']') // to end of previous operator
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100341 posp = &(buf->b_op_end);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100342 else if (c == '{' || c == '}') // to previous/next paragraph
Bram Moolenaar071d4272004-06-13 20:20:40 +0000343 {
344 pos_T pos;
345 oparg_T oa;
346 int slcb = listcmd_busy;
347
348 pos = curwin->w_cursor;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100349 listcmd_busy = TRUE; // avoid that '' is changed
Bram Moolenaar8b96d642005-09-05 22:05:30 +0000350 if (findpar(&oa.inclusive,
351 c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000352 {
353 pos_copy = curwin->w_cursor;
354 posp = &pos_copy;
355 }
356 curwin->w_cursor = pos;
357 listcmd_busy = slcb;
358 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100359 else if (c == '(' || c == ')') // to previous/next sentence
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360 {
361 pos_T pos;
362 int slcb = listcmd_busy;
363
364 pos = curwin->w_cursor;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100365 listcmd_busy = TRUE; // avoid that '' is changed
Bram Moolenaar071d4272004-06-13 20:20:40 +0000366 if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
367 {
368 pos_copy = curwin->w_cursor;
369 posp = &pos_copy;
370 }
371 curwin->w_cursor = pos;
372 listcmd_busy = slcb;
373 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100374 else if (c == '<' || c == '>') // start/end of visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +0000375 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100376 startp = &buf->b_visual.vi_start;
377 endp = &buf->b_visual.vi_end;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100378 if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100379 && startp->lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 posp = startp;
381 else
382 posp = endp;
383 /*
384 * For Visual line mode, set mark at begin or end of line
385 */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100386 if (buf->b_visual.vi_mode == 'V')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387 {
388 pos_copy = *posp;
389 posp = &pos_copy;
390 if (c == '<')
391 pos_copy.col = 0;
392 else
393 pos_copy.col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000394 pos_copy.coladd = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000395 }
396 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100397 else if (ASCII_ISLOWER(c)) // normal named mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000398 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100399 posp = &(buf->b_namedm[c - 'a']);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100401 else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) // named file mark
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402 {
403 if (VIM_ISDIGIT(c))
404 c = c - '0' + NMARKS;
405 else
406 c -= 'A';
407 posp = &(namedfm[c].fmark.mark);
408
409 if (namedfm[c].fmark.fnum == 0)
410 fname2fnum(&namedfm[c]);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000411
412 if (fnum != NULL)
413 *fnum = namedfm[c].fmark.fnum;
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100414 else if (namedfm[c].fmark.fnum != buf->b_fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100416 // mark is in another file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417 posp = &pos_copy;
418
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419 if (namedfm[c].fmark.mark.lnum != 0
420 && changefile && namedfm[c].fmark.fnum)
421 {
422 if (buflist_getfile(namedfm[c].fmark.fnum,
423 (linenr_T)1, GETF_SETMARK, FALSE) == OK)
424 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100425 // Set the lnum now, autocommands could have changed it
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426 curwin->w_cursor = namedfm[c].fmark.mark;
427 return (pos_T *)-1;
428 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100429 pos_copy.lnum = -1; // can't get file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430 }
431 else
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100432 pos_copy.lnum = 0; // mark exists, but is not valid in
433 // current buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434 }
435 }
436
437 return posp;
438}
439
440/*
441 * Search for the next named mark in the current file.
442 *
443 * Returns pointer to pos_T of the next mark or NULL if no mark is found.
444 */
445 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100446getnextmark(
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100447 pos_T *startpos, // where to start
448 int dir, // direction for search
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100449 int begin_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450{
451 int i;
452 pos_T *result = NULL;
453 pos_T pos;
454
455 pos = *startpos;
456
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100457 // When searching backward and leaving the cursor on the first non-blank,
458 // position must be in a previous line.
459 // When searching forward and leaving the cursor on the first non-blank,
460 // position must be in a next line.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000461 if (dir == BACKWARD && begin_line)
462 pos.col = 0;
463 else if (dir == FORWARD && begin_line)
464 pos.col = MAXCOL;
465
466 for (i = 0; i < NMARKS; i++)
467 {
468 if (curbuf->b_namedm[i].lnum > 0)
469 {
470 if (dir == FORWARD)
471 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100472 if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
473 && LT_POS(pos, curbuf->b_namedm[i]))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 result = &curbuf->b_namedm[i];
475 }
476 else
477 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100478 if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
479 && LT_POS(curbuf->b_namedm[i], pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480 result = &curbuf->b_namedm[i];
481 }
482 }
483 }
484
485 return result;
486}
487
488/*
489 * For an xtended filemark: set the fnum from the fname.
490 * This is used for marks obtained from the .viminfo file. It's postponed
491 * until the mark is used to avoid a long startup delay.
492 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +0200493 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100494fname2fnum(xfmark_T *fm)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000495{
496 char_u *p;
497
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000498 if (fm->fname == NULL)
499 return;
500
501 /*
502 * First expand "~/" in the file name to the home directory.
503 * Don't expand the whole name, it may contain other '~' chars.
504 */
505 if (fm->fname[0] == '~' && (fm->fname[1] == '/'
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000506#ifdef BACKSLASH_IN_FILENAME
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000507 || fm->fname[1] == '\\'
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000508#endif
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000509 ))
510 {
511 int len;
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000512
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000513 expand_env((char_u *)"~/", NameBuff, MAXPATHL);
514 len = (int)STRLEN(NameBuff);
515 vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 }
Yegappan Lakshmanane8575982023-01-14 12:32:28 +0000517 else
518 vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
519
520 // Try to shorten the file name.
521 mch_dirname(IObuff, IOSIZE);
522 p = shorten_fname(NameBuff, IObuff);
523
524 // buflist_new() will call fmarks_check_names()
525 (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000526}
527
528/*
529 * Check all file marks for a name that matches the file name in buf.
530 * May replace the name with an fnum.
531 * Used for marks that come from the .viminfo file.
532 */
533 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100534fmarks_check_names(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000535{
536 char_u *name;
537 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000539
540 if (buf->b_ffname == NULL)
541 return;
542
543 name = home_replace_save(buf, buf->b_ffname);
544 if (name == NULL)
545 return;
546
547 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
548 fmarks_check_one(&namedfm[i], name, buf);
549
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550 FOR_ALL_WINDOWS(wp)
551 {
552 for (i = 0; i < wp->w_jumplistlen; ++i)
553 fmarks_check_one(&wp->w_jumplist[i], name, buf);
554 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555
556 vim_free(name);
557}
558
559 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100560fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561{
562 if (fm->fmark.fnum == 0
563 && fm->fname != NULL
564 && fnamecmp(name, fm->fname) == 0)
565 {
566 fm->fmark.fnum = buf->b_fnum;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100567 VIM_CLEAR(fm->fname);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 }
569}
570
571/*
572 * Check a if a position from a mark is valid.
573 * Give and error message and return FAIL if not.
574 */
575 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100576check_mark(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000577{
578 if (pos == NULL)
579 {
Bram Moolenaar40bcec12021-12-05 22:19:27 +0000580 emsg(_(e_unknown_mark));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000581 return FAIL;
582 }
583 if (pos->lnum <= 0)
584 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100585 // lnum is negative if mark is in another file can can't get that
586 // file, error message already give then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000587 if (pos->lnum == 0)
Bram Moolenaar108010a2021-06-27 22:03:33 +0200588 emsg(_(e_mark_not_set));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589 return FAIL;
590 }
591 if (pos->lnum > curbuf->b_ml.ml_line_count)
592 {
Bram Moolenaar108010a2021-06-27 22:03:33 +0200593 emsg(_(e_mark_has_invalid_line_number));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000594 return FAIL;
595 }
596 return OK;
597}
598
599/*
600 * clrallmarks() - clear all marks in the buffer 'buf'
601 *
602 * Used mainly when trashing the entire buffer during ":e" type commands
603 */
604 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100605clrallmarks(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000606{
607 static int i = -1;
608
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100609 if (i == -1) // first call ever: initialize
Bram Moolenaar071d4272004-06-13 20:20:40 +0000610 for (i = 0; i < NMARKS + 1; i++)
611 {
612 namedfm[i].fmark.mark.lnum = 0;
613 namedfm[i].fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200614#ifdef FEAT_VIMINFO
615 namedfm[i].time_set = 0;
616#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000617 }
618
619 for (i = 0; i < NMARKS; i++)
620 buf->b_namedm[i].lnum = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100621 buf->b_op_start.lnum = 0; // start/end op mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 buf->b_op_end.lnum = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100623 buf->b_last_cursor.lnum = 1; // '" mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000624 buf->b_last_cursor.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000625 buf->b_last_cursor.coladd = 0;
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100626 buf->b_last_insert.lnum = 0; // '^ mark cleared
627 buf->b_last_change.lnum = 0; // '. mark cleared
Bram Moolenaar071d4272004-06-13 20:20:40 +0000628 buf->b_changelistlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000629}
630
631/*
632 * Get name of file from a filemark.
633 * When it's in the current buffer, return the text at the mark.
634 * Returns an allocated string.
635 */
636 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100637fm_getname(fmark_T *fmark, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000638{
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100639 if (fmark->fnum == curbuf->b_fnum) // current buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +0000640 return mark_line(&(fmark->mark), lead_len);
641 return buflist_nr2name(fmark->fnum, FALSE, TRUE);
642}
643
644/*
645 * Return the line at mark "mp". Truncate to fit in window.
646 * The returned string has been allocated.
647 */
648 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100649mark_line(pos_T *mp, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000650{
651 char_u *s, *p;
652 int len;
653
654 if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
655 return vim_strsave((char_u *)"-invalid-");
Bram Moolenaar9d5185b2018-07-08 17:57:34 +0200656 // Allow for up to 5 bytes per character.
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200657 s = vim_strnsave(skipwhite(ml_get(mp->lnum)), Columns * 5);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000658 if (s == NULL)
659 return NULL;
Bram Moolenaar9d5185b2018-07-08 17:57:34 +0200660 // Truncate the line to fit it in the window.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661 len = 0;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100662 for (p = s; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000663 {
664 len += ptr2cells(p);
665 if (len >= Columns - lead_len)
666 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000667 }
668 *p = NUL;
669 return s;
670}
671
672/*
673 * print the marks
674 */
675 void
Bram Moolenaar4bd78232019-09-19 23:21:55 +0200676ex_marks(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677{
678 char_u *arg = eap->arg;
679 int i;
680 char_u *name;
Bram Moolenaar54c3fcd2020-07-19 22:09:06 +0200681 pos_T *posp, *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000682
683 if (arg != NULL && *arg == NUL)
684 arg = NULL;
685
686 show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
687 for (i = 0; i < NMARKS; ++i)
688 show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
689 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
690 {
691 if (namedfm[i].fmark.fnum != 0)
692 name = fm_getname(&namedfm[i].fmark, 15);
693 else
694 name = namedfm[i].fname;
695 if (name != NULL)
696 {
697 show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
698 arg, &namedfm[i].fmark.mark, name,
699 namedfm[i].fmark.fnum == curbuf->b_fnum);
700 if (namedfm[i].fmark.fnum != 0)
701 vim_free(name);
702 }
703 }
704 show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
705 show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
706 show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
707 show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
708 show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
Bram Moolenaar54c3fcd2020-07-19 22:09:06 +0200709
710 // Show the marks as where they will jump to.
711 startp = &curbuf->b_visual.vi_start;
712 endp = &curbuf->b_visual.vi_end;
713 if ((LT_POS(*startp, *endp) || endp->lnum == 0) && startp->lnum != 0)
714 posp = startp;
715 else
716 posp = endp;
717 show_one_mark('<', arg, posp, NULL, TRUE);
718 show_one_mark('>', arg, posp == startp ? endp : startp, NULL, TRUE);
719
Bram Moolenaar071d4272004-06-13 20:20:40 +0000720 show_one_mark(-1, arg, NULL, NULL, FALSE);
721}
722
723 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100724show_one_mark(
725 int c,
726 char_u *arg,
727 pos_T *p,
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200728 char_u *name_arg,
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100729 int current) // in current file
Bram Moolenaar071d4272004-06-13 20:20:40 +0000730{
731 static int did_title = FALSE;
732 int mustfree = FALSE;
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200733 char_u *name = name_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000734
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100735 if (c == -1) // finish up
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 {
737 if (did_title)
738 did_title = FALSE;
739 else
740 {
741 if (arg == NULL)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100742 msg(_("No marks set"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000743 else
Bram Moolenaar9a846fb2022-01-01 21:59:18 +0000744 semsg(_(e_no_marks_matching_str), arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000745 }
746 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200747 // don't output anything if 'q' typed at --more-- prompt
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748 else if (!got_int
749 && (arg == NULL || vim_strchr(arg, c) != NULL)
750 && p->lnum != 0)
751 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200752 if (name == NULL && current)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000753 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200754 name = mark_line(p, 15);
755 mustfree = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000756 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200757 if (!message_filtered(name))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200759 if (!did_title)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000760 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200761 // Highlight title
762 msg_puts_title(_("\nmark line col file/text"));
763 did_title = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000764 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200765 msg_putchar('\n');
766 if (!got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000767 {
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200768 sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
769 msg_outtrans(IObuff);
770 if (name != NULL)
771 {
772 msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
773 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000774 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200775 out_flush(); // show one line at a time
Bram Moolenaar071d4272004-06-13 20:20:40 +0000776 }
Bram Moolenaarad6dc492019-04-27 22:40:08 +0200777 if (mustfree)
778 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000779 }
780}
781
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000782/*
783 * ":delmarks[!] [marks]"
784 */
785 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100786ex_delmarks(exarg_T *eap)
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000787{
788 char_u *p;
789 int from, to;
790 int i;
791 int lower;
792 int digit;
793 int n;
794
795 if (*eap->arg == NUL && eap->forceit)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100796 // clear all marks
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000797 clrallmarks(curbuf);
798 else if (eap->forceit)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000799 emsg(_(e_invalid_argument));
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000800 else if (*eap->arg == NUL)
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000801 emsg(_(e_argument_required));
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000802 else
803 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100804 // clear specified marks only
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000805 for (p = eap->arg; *p != NUL; ++p)
806 {
807 lower = ASCII_ISLOWER(*p);
808 digit = VIM_ISDIGIT(*p);
809 if (lower || digit || ASCII_ISUPPER(*p))
810 {
811 if (p[1] == '-')
812 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100813 // clear range of marks
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000814 from = *p;
815 to = p[2];
816 if (!(lower ? ASCII_ISLOWER(p[2])
817 : (digit ? VIM_ISDIGIT(p[2])
818 : ASCII_ISUPPER(p[2])))
819 || to < from)
820 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000821 semsg(_(e_invalid_argument_str), p);
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000822 return;
823 }
824 p += 2;
825 }
826 else
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100827 // clear one lower case mark
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000828 from = to = *p;
829
830 for (i = from; i <= to; ++i)
831 {
832 if (lower)
833 curbuf->b_namedm[i - 'a'].lnum = 0;
834 else
835 {
836 if (digit)
837 n = i - '0' + NMARKS;
838 else
839 n = i - 'A';
840 namedfm[n].fmark.mark.lnum = 0;
Bram Moolenaar8cd6cd82019-12-27 17:33:26 +0100841 namedfm[n].fmark.fnum = 0;
Bram Moolenaard23a8232018-02-10 18:45:26 +0100842 VIM_CLEAR(namedfm[n].fname);
Bram Moolenaar2d358992016-06-12 21:20:54 +0200843#ifdef FEAT_VIMINFO
Bram Moolenaar8cd6cd82019-12-27 17:33:26 +0100844 namedfm[n].time_set = digit ? 0 : vim_time();
Bram Moolenaar2d358992016-06-12 21:20:54 +0200845#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000846 }
847 }
848 }
849 else
850 switch (*p)
851 {
852 case '"': curbuf->b_last_cursor.lnum = 0; break;
853 case '^': curbuf->b_last_insert.lnum = 0; break;
854 case '.': curbuf->b_last_change.lnum = 0; break;
855 case '[': curbuf->b_op_start.lnum = 0; break;
856 case ']': curbuf->b_op_end.lnum = 0; break;
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000857 case '<': curbuf->b_visual.vi_start.lnum = 0; break;
858 case '>': curbuf->b_visual.vi_end.lnum = 0; break;
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000859 case ' ': break;
Bram Moolenaar436b5ad2021-12-31 22:49:24 +0000860 default: semsg(_(e_invalid_argument_str), p);
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000861 return;
862 }
863 }
864 }
865}
866
Bram Moolenaar071d4272004-06-13 20:20:40 +0000867/*
868 * print the jumplist
869 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000870 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100871ex_jumps(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000872{
873 int i;
874 char_u *name;
875
Bram Moolenaar48679742018-02-13 13:33:29 +0100876 cleanup_jumplist(curwin, TRUE);
Bram Moolenaara7e18d22018-02-11 14:29:49 +0100877
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100878 // Highlight title
Bram Moolenaar32526b32019-01-19 17:43:09 +0100879 msg_puts_title(_("\n jump line col file/text"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000880 for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
881 {
882 if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
883 {
Bram Moolenaar071d4272004-06-13 20:20:40 +0000884 name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
Bram Moolenaarf86db782018-10-25 13:31:37 +0200885
Christian Brabandta0f659c2022-04-09 13:35:00 +0100886 // Make sure to output the current indicator, even when on an wiped
887 // out buffer. ":filter" may still skip it.
888 if (name == NULL && i == curwin->w_jumplistidx)
889 name = vim_strsave((char_u *)"-invalid-");
Bram Moolenaarf86db782018-10-25 13:31:37 +0200890 // apply :filter /pat/ or file name not available
891 if (name == NULL || message_filtered(name))
Bram Moolenaard93090f2019-01-27 15:07:39 +0100892 {
893 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000894 continue;
Bram Moolenaard93090f2019-01-27 15:07:39 +0100895 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896
897 msg_putchar('\n');
898 if (got_int)
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000899 {
900 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000901 break;
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000902 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000903 sprintf((char *)IObuff, "%c %2d %5ld %4d ",
904 i == curwin->w_jumplistidx ? '>' : ' ',
905 i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
906 : curwin->w_jumplistidx - i,
907 curwin->w_jumplist[i].fmark.mark.lnum,
908 curwin->w_jumplist[i].fmark.mark.col);
909 msg_outtrans(IObuff);
910 msg_outtrans_attr(name,
911 curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
Bram Moolenaar8820b482017-03-16 17:23:31 +0100912 ? HL_ATTR(HLF_D) : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913 vim_free(name);
914 ui_breakcheck();
915 }
916 out_flush();
917 }
918 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100919 msg_puts("\n>");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920}
921
Bram Moolenaar2d358992016-06-12 21:20:54 +0200922 void
923ex_clearjumps(exarg_T *eap UNUSED)
924{
925 free_jumplist(curwin);
926 curwin->w_jumplistlen = 0;
927 curwin->w_jumplistidx = 0;
928}
929
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930/*
931 * print the changelist
932 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000933 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100934ex_changes(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000935{
936 int i;
937 char_u *name;
938
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100939 // Highlight title
Bram Moolenaar32526b32019-01-19 17:43:09 +0100940 msg_puts_title(_("\nchange line col text"));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000941
942 for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i)
943 {
944 if (curbuf->b_changelist[i].lnum != 0)
945 {
946 msg_putchar('\n');
947 if (got_int)
948 break;
949 sprintf((char *)IObuff, "%c %3d %5ld %4d ",
950 i == curwin->w_changelistidx ? '>' : ' ',
951 i > curwin->w_changelistidx ? i - curwin->w_changelistidx
952 : curwin->w_changelistidx - i,
953 (long)curbuf->b_changelist[i].lnum,
954 curbuf->b_changelist[i].col);
955 msg_outtrans(IObuff);
956 name = mark_line(&curbuf->b_changelist[i], 17);
957 if (name == NULL)
958 break;
Bram Moolenaar8820b482017-03-16 17:23:31 +0100959 msg_outtrans_attr(name, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000960 vim_free(name);
961 ui_breakcheck();
962 }
963 out_flush();
964 }
965 if (curwin->w_changelistidx == curbuf->b_changelistlen)
Bram Moolenaar32526b32019-01-19 17:43:09 +0100966 msg_puts("\n>");
Bram Moolenaar071d4272004-06-13 20:20:40 +0000967}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000968
969#define one_adjust(add) \
970 { \
971 lp = add; \
972 if (*lp >= line1 && *lp <= line2) \
973 { \
974 if (amount == MAXLNUM) \
975 *lp = 0; \
976 else \
977 *lp += amount; \
978 } \
979 else if (amount_after && *lp > line2) \
980 *lp += amount_after; \
981 }
982
Bram Moolenaar4ba37b52019-12-04 21:57:43 +0100983// don't delete the line, just put at first deleted line
Bram Moolenaar071d4272004-06-13 20:20:40 +0000984#define one_adjust_nodel(add) \
985 { \
986 lp = add; \
987 if (*lp >= line1 && *lp <= line2) \
988 { \
989 if (amount == MAXLNUM) \
990 *lp = line1; \
991 else \
992 *lp += amount; \
993 } \
994 else if (amount_after && *lp > line2) \
995 *lp += amount_after; \
996 }
997
998/*
Bram Moolenaare7f05a82023-04-22 15:35:28 +0100999 * Adjust marks between "line1" and "line2" (inclusive) to move "amount" lines.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001000 * Must be called before changed_*(), appended_lines() or deleted_lines().
1001 * May be called before or after changing the text.
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001002 * When deleting lines "line1" to "line2", use an "amount" of MAXLNUM: The
1003 * marks within this range are made invalid.
1004 * If "amount_after" is non-zero adjust marks after "line2".
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005 * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1006 * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1007 * or: mark_adjust(56, 55, MAXLNUM, 2);
1008 */
1009 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001010mark_adjust(
1011 linenr_T line1,
1012 linenr_T line2,
1013 long amount,
1014 long amount_after)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001015{
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001016 mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1017}
1018
1019 void
1020mark_adjust_nofold(
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01001021 linenr_T line1,
1022 linenr_T line2,
1023 long amount,
1024 long amount_after)
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001025{
1026 mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1027}
1028
1029 static void
1030mark_adjust_internal(
Bram Moolenaarb4ad3b02022-03-30 10:57:45 +01001031 linenr_T line1,
1032 linenr_T line2,
1033 long amount,
1034 long amount_after,
1035 int adjust_folds UNUSED)
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001036{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001037 int i;
1038 int fnum = curbuf->b_fnum;
1039 linenr_T *lp;
1040 win_T *win;
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001041 tabpage_T *tab;
Bram Moolenaar29ddebe2019-01-26 17:28:26 +01001042 static pos_T initpos = {1, 0, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +00001043
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001044 if (line2 < line1 && amount_after == 0L) // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001045 return;
1046
Bram Moolenaare1004402020-10-24 20:49:43 +02001047 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001048 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001049 // named marks, lower case and upper case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050 for (i = 0; i < NMARKS; i++)
1051 {
1052 one_adjust(&(curbuf->b_namedm[i].lnum));
1053 if (namedfm[i].fmark.fnum == fnum)
1054 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1055 }
1056 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1057 {
1058 if (namedfm[i].fmark.fnum == fnum)
1059 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1060 }
1061
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001062 // last Insert position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001063 one_adjust(&(curbuf->b_last_insert.lnum));
1064
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001065 // last change position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001066 one_adjust(&(curbuf->b_last_change.lnum));
1067
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001068 // last cursor position, if it was set
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001069 if (!EQUAL_POS(curbuf->b_last_cursor, initpos))
Bram Moolenaarb6a76ff2013-02-06 12:33:21 +01001070 one_adjust(&(curbuf->b_last_cursor.lnum));
1071
1072
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001073 // list of change positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001074 for (i = 0; i < curbuf->b_changelistlen; ++i)
1075 one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001076
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001077 // Visual area
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001078 one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1079 one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001080
1081#ifdef FEAT_QUICKFIX
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001082 // quickfix marks
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001083 qf_mark_adjust(NULL, line1, line2, amount, amount_after);
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001084 // location lists
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001085 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001086 qf_mark_adjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087#endif
1088
1089#ifdef FEAT_SIGNS
1090 sign_mark_adjust(line1, line2, amount, amount_after);
1091#endif
1092 }
1093
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001094 // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001095 one_adjust(&(curwin->w_pcmark.lnum));
1096
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001097 // previous pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001098 one_adjust(&(curwin->w_prev_pcmark.lnum));
1099
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001100 // saved cursor for formatting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001101 if (saved_cursor.lnum != 0)
1102 one_adjust_nodel(&(saved_cursor.lnum));
1103
1104 /*
1105 * Adjust items in all windows related to the current buffer.
1106 */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001107 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001108 {
Bram Moolenaare1004402020-10-24 20:49:43 +02001109 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001110 // Marks in the jumplist. When deleting lines, this may create
1111 // duplicate marks in the jumplist, they will be removed later.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001112 for (i = 0; i < win->w_jumplistlen; ++i)
1113 if (win->w_jumplist[i].fmark.fnum == fnum)
1114 one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001115
1116 if (win->w_buffer == curbuf)
1117 {
Bram Moolenaare1004402020-10-24 20:49:43 +02001118 if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001119 // marks in the tag stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001120 for (i = 0; i < win->w_tagstacklen; i++)
1121 if (win->w_tagstack[i].fmark.fnum == fnum)
1122 one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1123
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001124 // the displayed Visual area
Bram Moolenaar071d4272004-06-13 20:20:40 +00001125 if (win->w_old_cursor_lnum != 0)
1126 {
1127 one_adjust_nodel(&(win->w_old_cursor_lnum));
1128 one_adjust_nodel(&(win->w_old_visual_lnum));
1129 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001130
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001131 // topline and cursor position for windows with the same buffer
1132 // other than the current window
Bram Moolenaar071d4272004-06-13 20:20:40 +00001133 if (win != curwin)
1134 {
1135 if (win->w_topline >= line1 && win->w_topline <= line2)
1136 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001137 if (amount == MAXLNUM) // topline is deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00001138 {
1139 if (line1 <= 1)
1140 win->w_topline = 1;
1141 else
1142 win->w_topline = line1 - 1;
1143 }
Bram Moolenaare7f05a82023-04-22 15:35:28 +01001144 else if (win->w_topline > line1)
1145 // keep topline on the same line, unless inserting just
1146 // above it (we probably want to see that line then)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001147 win->w_topline += amount;
1148#ifdef FEAT_DIFF
1149 win->w_topfill = 0;
1150#endif
1151 }
1152 else if (amount_after && win->w_topline > line2)
1153 {
1154 win->w_topline += amount_after;
1155#ifdef FEAT_DIFF
1156 win->w_topfill = 0;
1157#endif
1158 }
1159 if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1160 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001161 if (amount == MAXLNUM) // line with cursor is deleted
Bram Moolenaar071d4272004-06-13 20:20:40 +00001162 {
1163 if (line1 <= 1)
1164 win->w_cursor.lnum = 1;
1165 else
1166 win->w_cursor.lnum = line1 - 1;
1167 win->w_cursor.col = 0;
1168 }
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001169 else // keep cursor on the same line
Bram Moolenaar071d4272004-06-13 20:20:40 +00001170 win->w_cursor.lnum += amount;
1171 }
1172 else if (amount_after && win->w_cursor.lnum > line2)
1173 win->w_cursor.lnum += amount_after;
1174 }
1175
1176#ifdef FEAT_FOLDING
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001177 // adjust folds
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001178 if (adjust_folds)
1179 foldMarkAdjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001180#endif
1181 }
1182 }
1183
1184#ifdef FEAT_DIFF
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001185 // adjust diffs
Bram Moolenaar071d4272004-06-13 20:20:40 +00001186 diff_mark_adjust(line1, line2, amount, amount_after);
1187#endif
1188}
1189
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001190// This code is used often, needs to be fast.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001191#define col_adjust(pp) \
1192 { \
1193 posp = pp; \
1194 if (posp->lnum == lnum && posp->col >= mincol) \
1195 { \
1196 posp->lnum += lnum_amount; \
1197 if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1198 posp->col = 0; \
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001199 else if (posp->col < spaces_removed) \
1200 posp->col = col_amount + spaces_removed; \
Bram Moolenaar071d4272004-06-13 20:20:40 +00001201 else \
1202 posp->col += col_amount; \
1203 } \
1204 }
1205
1206/*
1207 * Adjust marks in line "lnum" at column "mincol" and further: add
1208 * "lnum_amount" to the line number and add "col_amount" to the column
1209 * position.
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001210 * "spaces_removed" is the number of spaces that were removed, matters when the
1211 * cursor is inside them.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001212 */
1213 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001214mark_col_adjust(
1215 linenr_T lnum,
1216 colnr_T mincol,
1217 long lnum_amount,
Bram Moolenaare1e714e2018-12-31 23:58:24 +01001218 long col_amount,
1219 int spaces_removed)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220{
1221 int i;
1222 int fnum = curbuf->b_fnum;
1223 win_T *win;
1224 pos_T *posp;
1225
Bram Moolenaare1004402020-10-24 20:49:43 +02001226 if ((col_amount == 0L && lnum_amount == 0L)
1227 || (cmdmod.cmod_flags & CMOD_LOCKMARKS))
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001228 return; // nothing to do
Bram Moolenaar071d4272004-06-13 20:20:40 +00001229
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001230 // named marks, lower case and upper case
Bram Moolenaar071d4272004-06-13 20:20:40 +00001231 for (i = 0; i < NMARKS; i++)
1232 {
1233 col_adjust(&(curbuf->b_namedm[i]));
1234 if (namedfm[i].fmark.fnum == fnum)
1235 col_adjust(&(namedfm[i].fmark.mark));
1236 }
1237 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1238 {
1239 if (namedfm[i].fmark.fnum == fnum)
1240 col_adjust(&(namedfm[i].fmark.mark));
1241 }
1242
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001243 // last Insert position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001244 col_adjust(&(curbuf->b_last_insert));
1245
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001246 // last change position
Bram Moolenaar071d4272004-06-13 20:20:40 +00001247 col_adjust(&(curbuf->b_last_change));
1248
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001249 // list of change positions
Bram Moolenaar071d4272004-06-13 20:20:40 +00001250 for (i = 0; i < curbuf->b_changelistlen; ++i)
1251 col_adjust(&(curbuf->b_changelist[i]));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001253 // Visual area
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001254 col_adjust(&(curbuf->b_visual.vi_start));
1255 col_adjust(&(curbuf->b_visual.vi_end));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001256
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001257 // previous context mark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001258 col_adjust(&(curwin->w_pcmark));
1259
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001260 // previous pcmark
Bram Moolenaar071d4272004-06-13 20:20:40 +00001261 col_adjust(&(curwin->w_prev_pcmark));
1262
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001263 // saved cursor for formatting
Bram Moolenaar071d4272004-06-13 20:20:40 +00001264 col_adjust(&saved_cursor);
1265
1266 /*
1267 * Adjust items in all windows related to the current buffer.
1268 */
1269 FOR_ALL_WINDOWS(win)
1270 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001271 // marks in the jumplist
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272 for (i = 0; i < win->w_jumplistlen; ++i)
1273 if (win->w_jumplist[i].fmark.fnum == fnum)
1274 col_adjust(&(win->w_jumplist[i].fmark.mark));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001275
1276 if (win->w_buffer == curbuf)
1277 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001278 // marks in the tag stack
Bram Moolenaar071d4272004-06-13 20:20:40 +00001279 for (i = 0; i < win->w_tagstacklen; i++)
1280 if (win->w_tagstack[i].fmark.fnum == fnum)
1281 col_adjust(&(win->w_tagstack[i].fmark.mark));
1282
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001283 // cursor position for other windows with the same buffer
Bram Moolenaar071d4272004-06-13 20:20:40 +00001284 if (win != curwin)
1285 col_adjust(&win->w_cursor);
1286 }
1287 }
1288}
1289
Bram Moolenaar071d4272004-06-13 20:20:40 +00001290/*
1291 * When deleting lines, this may create duplicate marks in the
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001292 * jumplist. They will be removed here for the specified window.
Bram Moolenaar48679742018-02-13 13:33:29 +01001293 * When "loadfiles" is TRUE first ensure entries have the "fnum" field set
1294 * (this may be a bit slow).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001295 */
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001296 void
Bram Moolenaar48679742018-02-13 13:33:29 +01001297cleanup_jumplist(win_T *wp, int loadfiles)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001298{
1299 int i;
1300 int from, to;
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001301 int mustfree;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302
Bram Moolenaar48679742018-02-13 13:33:29 +01001303 if (loadfiles)
1304 {
Bram Moolenaar4ba37b52019-12-04 21:57:43 +01001305 // If specified, load all the files from the jump list. This is
1306 // needed to properly clean up duplicate entries, but will take some
1307 // time.
Bram Moolenaar48679742018-02-13 13:33:29 +01001308 for (i = 0; i < wp->w_jumplistlen; ++i)
1309 {
1310 if ((wp->w_jumplist[i].fmark.fnum == 0) &&
1311 (wp->w_jumplist[i].fmark.mark.lnum != 0))
1312 fname2fnum(&wp->w_jumplist[i]);
1313 }
1314 }
1315
Bram Moolenaar071d4272004-06-13 20:20:40 +00001316 to = 0;
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001317 for (from = 0; from < wp->w_jumplistlen; ++from)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 {
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001319 if (wp->w_jumplistidx == from)
1320 wp->w_jumplistidx = to;
1321 for (i = from + 1; i < wp->w_jumplistlen; ++i)
1322 if (wp->w_jumplist[i].fmark.fnum
1323 == wp->w_jumplist[from].fmark.fnum
1324 && wp->w_jumplist[from].fmark.fnum != 0
1325 && wp->w_jumplist[i].fmark.mark.lnum
1326 == wp->w_jumplist[from].fmark.mark.lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001327 break;
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001328 if (i >= wp->w_jumplistlen) // not duplicate
1329 mustfree = FALSE;
1330 else if (i > from + 1) // non-adjacent duplicate
1331 // jumpoptions=stack: remove duplicates only when adjacent.
1332 mustfree = !(jop_flags & JOP_STACK);
1333 else // adjacent duplicate
1334 mustfree = TRUE;
1335
1336 if (mustfree)
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001337 vim_free(wp->w_jumplist[from].fname);
Yegappan Lakshmanan87018252023-09-20 20:20:04 +02001338 else
1339 wp->w_jumplist[to++] = wp->w_jumplist[from];
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 }
Bram Moolenaara7e18d22018-02-11 14:29:49 +01001341 if (wp->w_jumplistidx == wp->w_jumplistlen)
1342 wp->w_jumplistidx = to;
1343 wp->w_jumplistlen = to;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344}
1345
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346/*
1347 * Copy the jumplist from window "from" to window "to".
1348 */
1349 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001350copy_jumplist(win_T *from, win_T *to)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351{
1352 int i;
1353
1354 for (i = 0; i < from->w_jumplistlen; ++i)
1355 {
1356 to->w_jumplist[i] = from->w_jumplist[i];
1357 if (from->w_jumplist[i].fname != NULL)
1358 to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1359 }
1360 to->w_jumplistlen = from->w_jumplistlen;
1361 to->w_jumplistidx = from->w_jumplistidx;
1362}
1363
1364/*
1365 * Free items in the jumplist of window "wp".
1366 */
1367 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001368free_jumplist(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001369{
1370 int i;
1371
1372 for (i = 0; i < wp->w_jumplistlen; ++i)
1373 vim_free(wp->w_jumplist[i].fname);
1374}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001375
1376 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001377set_last_cursor(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001378{
Bram Moolenaar9db12932013-11-03 00:20:52 +01001379 if (win->w_buffer != NULL)
1380 win->w_buffer->b_last_cursor = win->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001381}
1382
Bram Moolenaarea408852005-06-25 22:49:46 +00001383#if defined(EXITFREE) || defined(PROTO)
1384 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001385free_all_marks(void)
Bram Moolenaarea408852005-06-25 22:49:46 +00001386{
1387 int i;
1388
1389 for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1390 if (namedfm[i].fmark.mark.lnum != 0)
1391 vim_free(namedfm[i].fname);
1392}
1393#endif
1394
Dominique Pelle748b3082022-01-08 12:41:16 +00001395#if defined(FEAT_VIMINFO) || defined(PROTO)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001396/*
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001397 * Return a pointer to the named file marks.
Bram Moolenaar2d358992016-06-12 21:20:54 +02001398 */
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001399 xfmark_T *
1400get_namedfm(void)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001401{
Bram Moolenaar1e78e692019-07-22 20:18:27 +02001402 return namedfm;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001403}
Dominique Pelle748b3082022-01-08 12:41:16 +00001404#endif
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001405
1406#if defined(FEAT_EVAL) || defined(PROTO)
1407/*
1408 * Add information about mark 'mname' to list 'l'
1409 */
1410 static int
1411add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
1412{
1413 dict_T *d;
1414 list_T *lpos;
1415
1416 if (pos->lnum <= 0)
1417 return OK;
1418
1419 d = dict_alloc();
1420 if (d == NULL)
1421 return FAIL;
1422
1423 if (list_append_dict(l, d) == FAIL)
1424 {
1425 dict_unref(d);
1426 return FAIL;
1427 }
1428
1429 lpos = list_alloc();
1430 if (lpos == NULL)
1431 return FAIL;
1432
1433 list_append_number(lpos, bufnr);
1434 list_append_number(lpos, pos->lnum);
Bram Moolenaarf17e7ea2020-06-01 14:14:44 +02001435 list_append_number(lpos, pos->col + 1);
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001436 list_append_number(lpos, pos->coladd);
1437
1438 if (dict_add_string(d, "mark", mname) == FAIL
1439 || dict_add_list(d, "pos", lpos) == FAIL
1440 || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
1441 return FAIL;
1442
1443 return OK;
1444}
1445
1446/*
1447 * Get information about marks local to a buffer.
1448 */
1449 static void
1450get_buf_local_marks(buf_T *buf, list_T *l)
1451{
1452 char_u mname[3] = "' ";
1453 int i;
1454
1455 // Marks 'a' to 'z'
1456 for (i = 0; i < NMARKS; ++i)
1457 {
1458 mname[1] = 'a' + i;
1459 add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
1460 }
1461
1462 // Mark '' is a window local mark and not a buffer local mark
1463 add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
1464
1465 add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
1466 add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
1467 add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
1468 add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
1469 add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
1470 add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
1471 add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
1472}
1473
1474/*
1475 * Get information about global marks ('A' to 'Z' and '0' to '9')
1476 */
1477 static void
1478get_global_marks(list_T *l)
1479{
1480 char_u mname[3] = "' ";
1481 int i;
1482 char_u *name;
1483
1484 // Marks 'A' to 'Z' and '0' to '9'
1485 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
1486 {
1487 if (namedfm[i].fmark.fnum != 0)
1488 name = buflist_nr2name(namedfm[i].fmark.fnum, TRUE, TRUE);
1489 else
1490 name = namedfm[i].fname;
1491 if (name != NULL)
1492 {
1493 mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
1494 add_mark(l, mname, &namedfm[i].fmark.mark,
1495 namedfm[i].fmark.fnum, name);
1496 if (namedfm[i].fmark.fnum != 0)
1497 vim_free(name);
1498 }
1499 }
1500}
1501
1502/*
1503 * getmarklist() function
1504 */
1505 void
1506f_getmarklist(typval_T *argvars, typval_T *rettv)
1507{
1508 buf_T *buf = NULL;
1509
Bram Moolenaar8088ae92022-06-20 11:38:17 +01001510 if (rettv_list_alloc(rettv) == FAIL)
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001511 return;
1512
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001513 if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
1514 return;
1515
Bram Moolenaarcfb4b472020-05-31 15:41:57 +02001516 if (argvars[0].v_type == VAR_UNKNOWN)
1517 {
1518 get_global_marks(rettv->vval.v_list);
1519 return;
1520 }
1521
1522 buf = tv_get_buf(&argvars[0], FALSE);
1523 if (buf == NULL)
1524 return;
1525
1526 get_buf_local_marks(buf, rettv->vval.v_list);
1527}
1528#endif