xref: /vim-8.2.3635/src/mark.c (revision e08aee60)
1 /* vi:set ts=8 sts=4 sw=4 noet:
2  *
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  */
27 static xfmark_T namedfm[NMARKS + EXTRA_MARKS];		// marks with file nr
28 
29 static void fname2fnum(xfmark_T *fm);
30 static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf);
31 static char_u *mark_line(pos_T *mp, int lead_len);
32 static void show_one_mark(int, char_u *, pos_T *, char_u *, int current);
33 static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
34     long amount_after, int adjust_folds);
35 
36 /*
37  * Set named mark "c" at current cursor position.
38  * Returns OK on success, FAIL if bad name given.
39  */
40     int
setmark(int c)41 setmark(int c)
42 {
43     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
setmark_pos(int c,pos_T * pos,int fnum)52 setmark_pos(int c, pos_T *pos, int fnum)
53 {
54     int		i;
55     buf_T	*buf;
56 
57     // Check for a special key (may cause islower() to crash).
58     if (c < 0)
59 	return FAIL;
60 
61     if (c == '\'' || c == '`')
62     {
63 	if (pos == &curwin->w_cursor)
64 	{
65 	    setpcmark();
66 	    // keep it even when the cursor doesn't move
67 	    curwin->w_prev_pcmark = curwin->w_pcmark;
68 	}
69 	else
70 	    curwin->w_pcmark = *pos;
71 	return OK;
72     }
73 
74     buf = buflist_findnr(fnum);
75     if (buf == NULL)
76 	return FAIL;
77 
78     if (c == '"')
79     {
80 	buf->b_last_cursor = *pos;
81 	return OK;
82     }
83 
84     // Allow setting '[ and '] for an autocommand that simulates reading a
85     // file.
86     if (c == '[')
87     {
88 	buf->b_op_start = *pos;
89 	return OK;
90     }
91     if (c == ']')
92     {
93 	buf->b_op_end = *pos;
94 	return OK;
95     }
96 
97     if (c == '<' || c == '>')
98     {
99 	if (c == '<')
100 	    buf->b_visual.vi_start = *pos;
101 	else
102 	    buf->b_visual.vi_end = *pos;
103 	if (buf->b_visual.vi_mode == NUL)
104 	    // Visual_mode has not yet been set, use a sane default.
105 	    buf->b_visual.vi_mode = 'v';
106 	return OK;
107     }
108 
109     if (ASCII_ISLOWER(c))
110     {
111 	i = c - 'a';
112 	buf->b_namedm[i] = *pos;
113 	return OK;
114     }
115     if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))
116     {
117 	if (VIM_ISDIGIT(c))
118 	    i = c - '0' + NMARKS;
119 	else
120 	    i = c - 'A';
121 	namedfm[i].fmark.mark = *pos;
122 	namedfm[i].fmark.fnum = fnum;
123 	VIM_CLEAR(namedfm[i].fname);
124 #ifdef FEAT_VIMINFO
125 	namedfm[i].time_set = vim_time();
126 #endif
127 	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
setpcmark(void)137 setpcmark(void)
138 {
139 #ifdef FEAT_JUMPLIST
140     int		i;
141     xfmark_T	*fm;
142 #endif
143 
144     // for :global the mark is set only once
145     if (global_busy || listcmd_busy || (cmdmod.cmod_flags & CMOD_KEEPJUMPS))
146 	return;
147 
148     curwin->w_prev_pcmark = curwin->w_pcmark;
149     curwin->w_pcmark = curwin->w_cursor;
150 
151 #ifdef FEAT_JUMPLIST
152     // If jumplist is full: remove oldest entry
153     if (++curwin->w_jumplistlen > JUMPLISTSIZE)
154     {
155 	curwin->w_jumplistlen = JUMPLISTSIZE;
156 	vim_free(curwin->w_jumplist[0].fname);
157 	for (i = 1; i < JUMPLISTSIZE; ++i)
158 	    curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
159     }
160     curwin->w_jumplistidx = curwin->w_jumplistlen;
161     fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
162 
163     fm->fmark.mark = curwin->w_pcmark;
164     fm->fmark.fnum = curbuf->b_fnum;
165     fm->fname = NULL;
166 # ifdef FEAT_VIMINFO
167     fm->time_set = vim_time();
168 # endif
169 #endif
170 }
171 
172 /*
173  * To change context, call setpcmark(), then move the current position to
174  * where ever, then call checkpcmark().  This ensures that the previous
175  * context will only be changed if the cursor moved to a different line.
176  * If pcmark was deleted (with "dG") the previous mark is restored.
177  */
178     void
checkpcmark(void)179 checkpcmark(void)
180 {
181     if (curwin->w_prev_pcmark.lnum != 0
182 	    && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
183 		|| curwin->w_pcmark.lnum == 0))
184 	curwin->w_pcmark = curwin->w_prev_pcmark;
185     curwin->w_prev_pcmark.lnum = 0;		// it has been checked
186 }
187 
188 #if defined(FEAT_JUMPLIST) || defined(PROTO)
189 /*
190  * move "count" positions in the jump list (count may be negative)
191  */
192     pos_T *
movemark(int count)193 movemark(int count)
194 {
195     pos_T	*pos;
196     xfmark_T	*jmp;
197 
198     cleanup_jumplist(curwin, TRUE);
199 
200     if (curwin->w_jumplistlen == 0)	    // nothing to jump to
201 	return (pos_T *)NULL;
202 
203     for (;;)
204     {
205 	if (curwin->w_jumplistidx + count < 0
206 		|| curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
207 	    return (pos_T *)NULL;
208 
209 	/*
210 	 * if first CTRL-O or CTRL-I command after a jump, add cursor position
211 	 * to list.  Careful: If there are duplicates (CTRL-O immediately after
212 	 * starting Vim on a file), another entry may have been removed.
213 	 */
214 	if (curwin->w_jumplistidx == curwin->w_jumplistlen)
215 	{
216 	    setpcmark();
217 	    --curwin->w_jumplistidx;	// skip the new entry
218 	    if (curwin->w_jumplistidx + count < 0)
219 		return (pos_T *)NULL;
220 	}
221 
222 	curwin->w_jumplistidx += count;
223 
224 	jmp = curwin->w_jumplist + curwin->w_jumplistidx;
225 	if (jmp->fmark.fnum == 0)
226 	    fname2fnum(jmp);
227 	if (jmp->fmark.fnum != curbuf->b_fnum)
228 	{
229 	    // jump to other file
230 	    if (buflist_findnr(jmp->fmark.fnum) == NULL)
231 	    {					     // Skip this one ..
232 		count += count < 0 ? -1 : 1;
233 		continue;
234 	    }
235 	    if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
236 							    0, FALSE) == FAIL)
237 		return (pos_T *)NULL;
238 	    // Set lnum again, autocommands my have changed it
239 	    curwin->w_cursor = jmp->fmark.mark;
240 	    pos = (pos_T *)-1;
241 	}
242 	else
243 	    pos = &(jmp->fmark.mark);
244 	return pos;
245     }
246 }
247 
248 /*
249  * Move "count" positions in the changelist (count may be negative).
250  */
251     pos_T *
movechangelist(int count)252 movechangelist(int count)
253 {
254     int		n;
255 
256     if (curbuf->b_changelistlen == 0)	    // nothing to jump to
257 	return (pos_T *)NULL;
258 
259     n = curwin->w_changelistidx;
260     if (n + count < 0)
261     {
262 	if (n == 0)
263 	    return (pos_T *)NULL;
264 	n = 0;
265     }
266     else if (n + count >= curbuf->b_changelistlen)
267     {
268 	if (n == curbuf->b_changelistlen - 1)
269 	    return (pos_T *)NULL;
270 	n = curbuf->b_changelistlen - 1;
271     }
272     else
273 	n += count;
274     curwin->w_changelistidx = n;
275     return curbuf->b_changelist + n;
276 }
277 #endif
278 
279 /*
280  * Find mark "c" in buffer pointed to by "buf".
281  * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
282  * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
283  * another file.
284  * Returns:
285  * - pointer to pos_T if found.  lnum is 0 when mark not set, -1 when mark is
286  *   in another file which can't be gotten. (caller needs to check lnum!)
287  * - NULL if there is no mark called 'c'.
288  * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
289  */
290     pos_T *
getmark_buf(buf_T * buf,int c,int changefile)291 getmark_buf(buf_T *buf, int c, int changefile)
292 {
293     return getmark_buf_fnum(buf, c, changefile, NULL);
294 }
295 
296     pos_T *
getmark(int c,int changefile)297 getmark(int c, int changefile)
298 {
299     return getmark_buf_fnum(curbuf, c, changefile, NULL);
300 }
301 
302     pos_T *
getmark_buf_fnum(buf_T * buf,int c,int changefile,int * fnum)303 getmark_buf_fnum(
304     buf_T	*buf,
305     int		c,
306     int		changefile,
307     int		*fnum)
308 {
309     pos_T		*posp;
310     pos_T		*startp, *endp;
311     static pos_T	pos_copy;
312 
313     posp = NULL;
314 
315     // Check for special key, can't be a mark name and might cause islower()
316     // to crash.
317     if (c < 0)
318 	return posp;
319 #ifndef EBCDIC
320     if (c > '~')			// check for islower()/isupper()
321 	;
322     else
323 #endif
324 	if (c == '\'' || c == '`')	// previous context mark
325     {
326 	pos_copy = curwin->w_pcmark;	// need to make a copy because
327 	posp = &pos_copy;		//   w_pcmark may be changed soon
328     }
329     else if (c == '"')			// to pos when leaving buffer
330 	posp = &(buf->b_last_cursor);
331     else if (c == '^')			// to where Insert mode stopped
332 	posp = &(buf->b_last_insert);
333     else if (c == '.')			// to where last change was made
334 	posp = &(buf->b_last_change);
335     else if (c == '[')			// to start of previous operator
336 	posp = &(buf->b_op_start);
337     else if (c == ']')			// to end of previous operator
338 	posp = &(buf->b_op_end);
339     else if (c == '{' || c == '}')	// to previous/next paragraph
340     {
341 	pos_T	pos;
342 	oparg_T	oa;
343 	int	slcb = listcmd_busy;
344 
345 	pos = curwin->w_cursor;
346 	listcmd_busy = TRUE;	    // avoid that '' is changed
347 	if (findpar(&oa.inclusive,
348 			       c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
349 	{
350 	    pos_copy = curwin->w_cursor;
351 	    posp = &pos_copy;
352 	}
353 	curwin->w_cursor = pos;
354 	listcmd_busy = slcb;
355     }
356     else if (c == '(' || c == ')')	// to previous/next sentence
357     {
358 	pos_T	pos;
359 	int	slcb = listcmd_busy;
360 
361 	pos = curwin->w_cursor;
362 	listcmd_busy = TRUE;	    // avoid that '' is changed
363 	if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
364 	{
365 	    pos_copy = curwin->w_cursor;
366 	    posp = &pos_copy;
367 	}
368 	curwin->w_cursor = pos;
369 	listcmd_busy = slcb;
370     }
371     else if (c == '<' || c == '>')	// start/end of visual area
372     {
373 	startp = &buf->b_visual.vi_start;
374 	endp = &buf->b_visual.vi_end;
375 	if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
376 							  && startp->lnum != 0)
377 	    posp = startp;
378 	else
379 	    posp = endp;
380 	/*
381 	 * For Visual line mode, set mark at begin or end of line
382 	 */
383 	if (buf->b_visual.vi_mode == 'V')
384 	{
385 	    pos_copy = *posp;
386 	    posp = &pos_copy;
387 	    if (c == '<')
388 		pos_copy.col = 0;
389 	    else
390 		pos_copy.col = MAXCOL;
391 	    pos_copy.coladd = 0;
392 	}
393     }
394     else if (ASCII_ISLOWER(c))		// normal named mark
395     {
396 	posp = &(buf->b_namedm[c - 'a']);
397     }
398     else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))	// named file mark
399     {
400 	if (VIM_ISDIGIT(c))
401 	    c = c - '0' + NMARKS;
402 	else
403 	    c -= 'A';
404 	posp = &(namedfm[c].fmark.mark);
405 
406 	if (namedfm[c].fmark.fnum == 0)
407 	    fname2fnum(&namedfm[c]);
408 
409 	if (fnum != NULL)
410 	    *fnum = namedfm[c].fmark.fnum;
411 	else if (namedfm[c].fmark.fnum != buf->b_fnum)
412 	{
413 	    // mark is in another file
414 	    posp = &pos_copy;
415 
416 	    if (namedfm[c].fmark.mark.lnum != 0
417 				       && changefile && namedfm[c].fmark.fnum)
418 	    {
419 		if (buflist_getfile(namedfm[c].fmark.fnum,
420 				      (linenr_T)1, GETF_SETMARK, FALSE) == OK)
421 		{
422 		    // Set the lnum now, autocommands could have changed it
423 		    curwin->w_cursor = namedfm[c].fmark.mark;
424 		    return (pos_T *)-1;
425 		}
426 		pos_copy.lnum = -1;	// can't get file
427 	    }
428 	    else
429 		pos_copy.lnum = 0;	// mark exists, but is not valid in
430 					// current buffer
431 	}
432     }
433 
434     return posp;
435 }
436 
437 /*
438  * Search for the next named mark in the current file.
439  *
440  * Returns pointer to pos_T of the next mark or NULL if no mark is found.
441  */
442     pos_T *
getnextmark(pos_T * startpos,int dir,int begin_line)443 getnextmark(
444     pos_T	*startpos,	// where to start
445     int		dir,	// direction for search
446     int		begin_line)
447 {
448     int		i;
449     pos_T	*result = NULL;
450     pos_T	pos;
451 
452     pos = *startpos;
453 
454     // When searching backward and leaving the cursor on the first non-blank,
455     // position must be in a previous line.
456     // When searching forward and leaving the cursor on the first non-blank,
457     // position must be in a next line.
458     if (dir == BACKWARD && begin_line)
459 	pos.col = 0;
460     else if (dir == FORWARD && begin_line)
461 	pos.col = MAXCOL;
462 
463     for (i = 0; i < NMARKS; i++)
464     {
465 	if (curbuf->b_namedm[i].lnum > 0)
466 	{
467 	    if (dir == FORWARD)
468 	    {
469 		if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
470 			&& LT_POS(pos, curbuf->b_namedm[i]))
471 		    result = &curbuf->b_namedm[i];
472 	    }
473 	    else
474 	    {
475 		if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
476 			&& LT_POS(curbuf->b_namedm[i], pos))
477 		    result = &curbuf->b_namedm[i];
478 	    }
479 	}
480     }
481 
482     return result;
483 }
484 
485 /*
486  * For an xtended filemark: set the fnum from the fname.
487  * This is used for marks obtained from the .viminfo file.  It's postponed
488  * until the mark is used to avoid a long startup delay.
489  */
490     static void
fname2fnum(xfmark_T * fm)491 fname2fnum(xfmark_T *fm)
492 {
493     char_u	*p;
494 
495     if (fm->fname != NULL)
496     {
497 	/*
498 	 * First expand "~/" in the file name to the home directory.
499 	 * Don't expand the whole name, it may contain other '~' chars.
500 	 */
501 	if (fm->fname[0] == '~' && (fm->fname[1] == '/'
502 #ifdef BACKSLASH_IN_FILENAME
503 		    || fm->fname[1] == '\\'
504 #endif
505 		    ))
506 	{
507 	    int len;
508 
509 	    expand_env((char_u *)"~/", NameBuff, MAXPATHL);
510 	    len = (int)STRLEN(NameBuff);
511 	    vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
512 	}
513 	else
514 	    vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
515 
516 	// Try to shorten the file name.
517 	mch_dirname(IObuff, IOSIZE);
518 	p = shorten_fname(NameBuff, IObuff);
519 
520 	// buflist_new() will call fmarks_check_names()
521 	(void)buflist_new(NameBuff, p, (linenr_T)1, 0);
522     }
523 }
524 
525 /*
526  * Check all file marks for a name that matches the file name in buf.
527  * May replace the name with an fnum.
528  * Used for marks that come from the .viminfo file.
529  */
530     void
fmarks_check_names(buf_T * buf)531 fmarks_check_names(buf_T *buf)
532 {
533     char_u	*name;
534     int		i;
535 #ifdef FEAT_JUMPLIST
536     win_T	*wp;
537 #endif
538 
539     if (buf->b_ffname == NULL)
540 	return;
541 
542     name = home_replace_save(buf, buf->b_ffname);
543     if (name == NULL)
544 	return;
545 
546     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
547 	fmarks_check_one(&namedfm[i], name, buf);
548 
549 #ifdef FEAT_JUMPLIST
550     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     }
555 #endif
556 
557     vim_free(name);
558 }
559 
560     static void
fmarks_check_one(xfmark_T * fm,char_u * name,buf_T * buf)561 fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
562 {
563     if (fm->fmark.fnum == 0
564 	    && fm->fname != NULL
565 	    && fnamecmp(name, fm->fname) == 0)
566     {
567 	fm->fmark.fnum = buf->b_fnum;
568 	VIM_CLEAR(fm->fname);
569     }
570 }
571 
572 /*
573  * Check a if a position from a mark is valid.
574  * Give and error message and return FAIL if not.
575  */
576     int
check_mark(pos_T * pos)577 check_mark(pos_T *pos)
578 {
579     if (pos == NULL)
580     {
581 	emsg(_(e_umark));
582 	return FAIL;
583     }
584     if (pos->lnum <= 0)
585     {
586 	// lnum is negative if mark is in another file can can't get that
587 	// file, error message already give then.
588 	if (pos->lnum == 0)
589 	    emsg(_(e_mark_not_set));
590 	return FAIL;
591     }
592     if (pos->lnum > curbuf->b_ml.ml_line_count)
593     {
594 	emsg(_(e_mark_has_invalid_line_number));
595 	return FAIL;
596     }
597     return OK;
598 }
599 
600 /*
601  * clrallmarks() - clear all marks in the buffer 'buf'
602  *
603  * Used mainly when trashing the entire buffer during ":e" type commands
604  */
605     void
clrallmarks(buf_T * buf)606 clrallmarks(buf_T *buf)
607 {
608     static int		i = -1;
609 
610     if (i == -1)	// first call ever: initialize
611 	for (i = 0; i < NMARKS + 1; i++)
612 	{
613 	    namedfm[i].fmark.mark.lnum = 0;
614 	    namedfm[i].fname = NULL;
615 #ifdef FEAT_VIMINFO
616 	    namedfm[i].time_set = 0;
617 #endif
618 	}
619 
620     for (i = 0; i < NMARKS; i++)
621 	buf->b_namedm[i].lnum = 0;
622     buf->b_op_start.lnum = 0;		// start/end op mark cleared
623     buf->b_op_end.lnum = 0;
624     buf->b_last_cursor.lnum = 1;	// '" mark cleared
625     buf->b_last_cursor.col = 0;
626     buf->b_last_cursor.coladd = 0;
627     buf->b_last_insert.lnum = 0;	// '^ mark cleared
628     buf->b_last_change.lnum = 0;	// '. mark cleared
629 #ifdef FEAT_JUMPLIST
630     buf->b_changelistlen = 0;
631 #endif
632 }
633 
634 /*
635  * Get name of file from a filemark.
636  * When it's in the current buffer, return the text at the mark.
637  * Returns an allocated string.
638  */
639     char_u *
fm_getname(fmark_T * fmark,int lead_len)640 fm_getname(fmark_T *fmark, int lead_len)
641 {
642     if (fmark->fnum == curbuf->b_fnum)		    // current buffer
643 	return mark_line(&(fmark->mark), lead_len);
644     return buflist_nr2name(fmark->fnum, FALSE, TRUE);
645 }
646 
647 /*
648  * Return the line at mark "mp".  Truncate to fit in window.
649  * The returned string has been allocated.
650  */
651     static char_u *
mark_line(pos_T * mp,int lead_len)652 mark_line(pos_T *mp, int lead_len)
653 {
654     char_u	*s, *p;
655     int		len;
656 
657     if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
658 	return vim_strsave((char_u *)"-invalid-");
659     // Allow for up to 5 bytes per character.
660     s = vim_strnsave(skipwhite(ml_get(mp->lnum)), Columns * 5);
661     if (s == NULL)
662 	return NULL;
663     // Truncate the line to fit it in the window.
664     len = 0;
665     for (p = s; *p != NUL; MB_PTR_ADV(p))
666     {
667 	len += ptr2cells(p);
668 	if (len >= Columns - lead_len)
669 	    break;
670     }
671     *p = NUL;
672     return s;
673 }
674 
675 /*
676  * print the marks
677  */
678     void
ex_marks(exarg_T * eap)679 ex_marks(exarg_T *eap)
680 {
681     char_u	*arg = eap->arg;
682     int		i;
683     char_u	*name;
684     pos_T	*posp, *startp, *endp;
685 
686     if (arg != NULL && *arg == NUL)
687 	arg = NULL;
688 
689     show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
690     for (i = 0; i < NMARKS; ++i)
691 	show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
692     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
693     {
694 	if (namedfm[i].fmark.fnum != 0)
695 	    name = fm_getname(&namedfm[i].fmark, 15);
696 	else
697 	    name = namedfm[i].fname;
698 	if (name != NULL)
699 	{
700 	    show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
701 		    arg, &namedfm[i].fmark.mark, name,
702 		    namedfm[i].fmark.fnum == curbuf->b_fnum);
703 	    if (namedfm[i].fmark.fnum != 0)
704 		vim_free(name);
705 	}
706     }
707     show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
708     show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
709     show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
710     show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
711     show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
712 
713     // Show the marks as where they will jump to.
714     startp = &curbuf->b_visual.vi_start;
715     endp = &curbuf->b_visual.vi_end;
716     if ((LT_POS(*startp, *endp) || endp->lnum == 0) && startp->lnum != 0)
717 	posp = startp;
718     else
719 	posp = endp;
720     show_one_mark('<', arg, posp, NULL, TRUE);
721     show_one_mark('>', arg, posp == startp ? endp : startp, NULL, TRUE);
722 
723     show_one_mark(-1, arg, NULL, NULL, FALSE);
724 }
725 
726     static void
show_one_mark(int c,char_u * arg,pos_T * p,char_u * name_arg,int current)727 show_one_mark(
728     int		c,
729     char_u	*arg,
730     pos_T	*p,
731     char_u	*name_arg,
732     int		current)	// in current file
733 {
734     static int	did_title = FALSE;
735     int		mustfree = FALSE;
736     char_u	*name = name_arg;
737 
738     if (c == -1)			    // finish up
739     {
740 	if (did_title)
741 	    did_title = FALSE;
742 	else
743 	{
744 	    if (arg == NULL)
745 		msg(_("No marks set"));
746 	    else
747 		semsg(_("E283: No marks matching \"%s\""), arg);
748 	}
749     }
750     // don't output anything if 'q' typed at --more-- prompt
751     else if (!got_int
752 	    && (arg == NULL || vim_strchr(arg, c) != NULL)
753 	    && p->lnum != 0)
754     {
755 	if (name == NULL && current)
756 	{
757 	    name = mark_line(p, 15);
758 	    mustfree = TRUE;
759 	}
760 	if (!message_filtered(name))
761 	{
762 	    if (!did_title)
763 	    {
764 		// Highlight title
765 		msg_puts_title(_("\nmark line  col file/text"));
766 		did_title = TRUE;
767 	    }
768 	    msg_putchar('\n');
769 	    if (!got_int)
770 	    {
771 		sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
772 		msg_outtrans(IObuff);
773 		if (name != NULL)
774 		{
775 		    msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
776 		}
777 	    }
778 	    out_flush();		    // show one line at a time
779 	}
780 	if (mustfree)
781 	    vim_free(name);
782     }
783 }
784 
785 /*
786  * ":delmarks[!] [marks]"
787  */
788     void
ex_delmarks(exarg_T * eap)789 ex_delmarks(exarg_T *eap)
790 {
791     char_u	*p;
792     int		from, to;
793     int		i;
794     int		lower;
795     int		digit;
796     int		n;
797 
798     if (*eap->arg == NUL && eap->forceit)
799 	// clear all marks
800 	clrallmarks(curbuf);
801     else if (eap->forceit)
802 	emsg(_(e_invarg));
803     else if (*eap->arg == NUL)
804 	emsg(_(e_argreq));
805     else
806     {
807 	// clear specified marks only
808 	for (p = eap->arg; *p != NUL; ++p)
809 	{
810 	    lower = ASCII_ISLOWER(*p);
811 	    digit = VIM_ISDIGIT(*p);
812 	    if (lower || digit || ASCII_ISUPPER(*p))
813 	    {
814 		if (p[1] == '-')
815 		{
816 		    // clear range of marks
817 		    from = *p;
818 		    to = p[2];
819 		    if (!(lower ? ASCII_ISLOWER(p[2])
820 				: (digit ? VIM_ISDIGIT(p[2])
821 				    : ASCII_ISUPPER(p[2])))
822 			    || to < from)
823 		    {
824 			semsg(_(e_invarg2), p);
825 			return;
826 		    }
827 		    p += 2;
828 		}
829 		else
830 		    // clear one lower case mark
831 		    from = to = *p;
832 
833 		for (i = from; i <= to; ++i)
834 		{
835 		    if (lower)
836 			curbuf->b_namedm[i - 'a'].lnum = 0;
837 		    else
838 		    {
839 			if (digit)
840 			    n = i - '0' + NMARKS;
841 			else
842 			    n = i - 'A';
843 			namedfm[n].fmark.mark.lnum = 0;
844 			namedfm[n].fmark.fnum = 0;
845 			VIM_CLEAR(namedfm[n].fname);
846 #ifdef FEAT_VIMINFO
847 			namedfm[n].time_set = digit ? 0 : vim_time();
848 #endif
849 		    }
850 		}
851 	    }
852 	    else
853 		switch (*p)
854 		{
855 		    case '"': curbuf->b_last_cursor.lnum = 0; break;
856 		    case '^': curbuf->b_last_insert.lnum = 0; break;
857 		    case '.': curbuf->b_last_change.lnum = 0; break;
858 		    case '[': curbuf->b_op_start.lnum    = 0; break;
859 		    case ']': curbuf->b_op_end.lnum      = 0; break;
860 		    case '<': curbuf->b_visual.vi_start.lnum = 0; break;
861 		    case '>': curbuf->b_visual.vi_end.lnum   = 0; break;
862 		    case ' ': break;
863 		    default:  semsg(_(e_invarg2), p);
864 			      return;
865 		}
866 	}
867     }
868 }
869 
870 #if defined(FEAT_JUMPLIST) || defined(PROTO)
871 /*
872  * print the jumplist
873  */
874     void
ex_jumps(exarg_T * eap UNUSED)875 ex_jumps(exarg_T *eap UNUSED)
876 {
877     int		i;
878     char_u	*name;
879 
880     cleanup_jumplist(curwin, TRUE);
881 
882     // Highlight title
883     msg_puts_title(_("\n jump line  col file/text"));
884     for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
885     {
886 	if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
887 	{
888 	    name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
889 
890 	    // apply :filter /pat/ or file name not available
891 	    if (name == NULL || message_filtered(name))
892 	    {
893 		vim_free(name);
894 		continue;
895 	    }
896 
897 	    msg_putchar('\n');
898 	    if (got_int)
899 	    {
900 		vim_free(name);
901 		break;
902 	    }
903 	    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
912 							? HL_ATTR(HLF_D) : 0);
913 	    vim_free(name);
914 	    ui_breakcheck();
915 	}
916 	out_flush();
917     }
918     if (curwin->w_jumplistidx == curwin->w_jumplistlen)
919 	msg_puts("\n>");
920 }
921 
922     void
ex_clearjumps(exarg_T * eap UNUSED)923 ex_clearjumps(exarg_T *eap UNUSED)
924 {
925     free_jumplist(curwin);
926     curwin->w_jumplistlen = 0;
927     curwin->w_jumplistidx = 0;
928 }
929 
930 /*
931  * print the changelist
932  */
933     void
ex_changes(exarg_T * eap UNUSED)934 ex_changes(exarg_T *eap UNUSED)
935 {
936     int		i;
937     char_u	*name;
938 
939     // Highlight title
940     msg_puts_title(_("\nchange line  col text"));
941 
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;
959 	    msg_outtrans_attr(name, HL_ATTR(HLF_D));
960 	    vim_free(name);
961 	    ui_breakcheck();
962 	}
963 	out_flush();
964     }
965     if (curwin->w_changelistidx == curbuf->b_changelistlen)
966 	msg_puts("\n>");
967 }
968 #endif
969 
970 #define one_adjust(add) \
971     { \
972 	lp = add; \
973 	if (*lp >= line1 && *lp <= line2) \
974 	{ \
975 	    if (amount == MAXLNUM) \
976 		*lp = 0; \
977 	    else \
978 		*lp += amount; \
979 	} \
980 	else if (amount_after && *lp > line2) \
981 	    *lp += amount_after; \
982     }
983 
984 // don't delete the line, just put at first deleted line
985 #define one_adjust_nodel(add) \
986     { \
987 	lp = add; \
988 	if (*lp >= line1 && *lp <= line2) \
989 	{ \
990 	    if (amount == MAXLNUM) \
991 		*lp = line1; \
992 	    else \
993 		*lp += amount; \
994 	} \
995 	else if (amount_after && *lp > line2) \
996 	    *lp += amount_after; \
997     }
998 
999 /*
1000  * Adjust marks between line1 and line2 (inclusive) to move 'amount' lines.
1001  * Must be called before changed_*(), appended_lines() or deleted_lines().
1002  * May be called before or after changing the text.
1003  * When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks
1004  * within this range are made invalid.
1005  * If 'amount_after' is non-zero adjust marks after line2.
1006  * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1007  * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1008  *				   or: mark_adjust(56, 55, MAXLNUM, 2);
1009  */
1010     void
mark_adjust(linenr_T line1,linenr_T line2,long amount,long amount_after)1011 mark_adjust(
1012     linenr_T	line1,
1013     linenr_T	line2,
1014     long	amount,
1015     long	amount_after)
1016 {
1017     mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1018 }
1019 
1020     void
mark_adjust_nofold(linenr_T line1,linenr_T line2,long amount,long amount_after)1021 mark_adjust_nofold(
1022     linenr_T line1,
1023     linenr_T line2,
1024     long amount,
1025     long amount_after)
1026 {
1027     mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1028 }
1029 
1030     static void
mark_adjust_internal(linenr_T line1,linenr_T line2,long amount,long amount_after,int adjust_folds UNUSED)1031 mark_adjust_internal(
1032     linenr_T line1,
1033     linenr_T line2,
1034     long amount,
1035     long amount_after,
1036     int adjust_folds UNUSED)
1037 {
1038     int		i;
1039     int		fnum = curbuf->b_fnum;
1040     linenr_T	*lp;
1041     win_T	*win;
1042     tabpage_T	*tab;
1043     static pos_T initpos = {1, 0, 0};
1044 
1045     if (line2 < line1 && amount_after == 0L)	    // nothing to do
1046 	return;
1047 
1048     if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
1049     {
1050 	// named marks, lower case and upper case
1051 	for (i = 0; i < NMARKS; i++)
1052 	{
1053 	    one_adjust(&(curbuf->b_namedm[i].lnum));
1054 	    if (namedfm[i].fmark.fnum == fnum)
1055 		one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1056 	}
1057 	for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1058 	{
1059 	    if (namedfm[i].fmark.fnum == fnum)
1060 		one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1061 	}
1062 
1063 	// last Insert position
1064 	one_adjust(&(curbuf->b_last_insert.lnum));
1065 
1066 	// last change position
1067 	one_adjust(&(curbuf->b_last_change.lnum));
1068 
1069 	// last cursor position, if it was set
1070 	if (!EQUAL_POS(curbuf->b_last_cursor, initpos))
1071 	    one_adjust(&(curbuf->b_last_cursor.lnum));
1072 
1073 
1074 #ifdef FEAT_JUMPLIST
1075 	// list of change positions
1076 	for (i = 0; i < curbuf->b_changelistlen; ++i)
1077 	    one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
1078 #endif
1079 
1080 	// Visual area
1081 	one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1082 	one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
1083 
1084 #ifdef FEAT_QUICKFIX
1085 	// quickfix marks
1086 	qf_mark_adjust(NULL, line1, line2, amount, amount_after);
1087 	// location lists
1088 	FOR_ALL_TAB_WINDOWS(tab, win)
1089 	    qf_mark_adjust(win, line1, line2, amount, amount_after);
1090 #endif
1091 
1092 #ifdef FEAT_SIGNS
1093 	sign_mark_adjust(line1, line2, amount, amount_after);
1094 #endif
1095     }
1096 
1097     // previous context mark
1098     one_adjust(&(curwin->w_pcmark.lnum));
1099 
1100     // previous pcmark
1101     one_adjust(&(curwin->w_prev_pcmark.lnum));
1102 
1103     // saved cursor for formatting
1104     if (saved_cursor.lnum != 0)
1105 	one_adjust_nodel(&(saved_cursor.lnum));
1106 
1107     /*
1108      * Adjust items in all windows related to the current buffer.
1109      */
1110     FOR_ALL_TAB_WINDOWS(tab, win)
1111     {
1112 #ifdef FEAT_JUMPLIST
1113 	if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
1114 	    // Marks in the jumplist.  When deleting lines, this may create
1115 	    // duplicate marks in the jumplist, they will be removed later.
1116 	    for (i = 0; i < win->w_jumplistlen; ++i)
1117 		if (win->w_jumplist[i].fmark.fnum == fnum)
1118 		    one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
1119 #endif
1120 
1121 	if (win->w_buffer == curbuf)
1122 	{
1123 	    if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0)
1124 		// marks in the tag stack
1125 		for (i = 0; i < win->w_tagstacklen; i++)
1126 		    if (win->w_tagstack[i].fmark.fnum == fnum)
1127 			one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1128 
1129 	    // the displayed Visual area
1130 	    if (win->w_old_cursor_lnum != 0)
1131 	    {
1132 		one_adjust_nodel(&(win->w_old_cursor_lnum));
1133 		one_adjust_nodel(&(win->w_old_visual_lnum));
1134 	    }
1135 
1136 	    // topline and cursor position for windows with the same buffer
1137 	    // other than the current window
1138 	    if (win != curwin)
1139 	    {
1140 		if (win->w_topline >= line1 && win->w_topline <= line2)
1141 		{
1142 		    if (amount == MAXLNUM)	    // topline is deleted
1143 		    {
1144 			if (line1 <= 1)
1145 			    win->w_topline = 1;
1146 			else
1147 			    win->w_topline = line1 - 1;
1148 		    }
1149 		    else		// keep topline on the same line
1150 			win->w_topline += amount;
1151 #ifdef FEAT_DIFF
1152 		    win->w_topfill = 0;
1153 #endif
1154 		}
1155 		else if (amount_after && win->w_topline > line2)
1156 		{
1157 		    win->w_topline += amount_after;
1158 #ifdef FEAT_DIFF
1159 		    win->w_topfill = 0;
1160 #endif
1161 		}
1162 		if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1163 		{
1164 		    if (amount == MAXLNUM) // line with cursor is deleted
1165 		    {
1166 			if (line1 <= 1)
1167 			    win->w_cursor.lnum = 1;
1168 			else
1169 			    win->w_cursor.lnum = line1 - 1;
1170 			win->w_cursor.col = 0;
1171 		    }
1172 		    else		// keep cursor on the same line
1173 			win->w_cursor.lnum += amount;
1174 		}
1175 		else if (amount_after && win->w_cursor.lnum > line2)
1176 		    win->w_cursor.lnum += amount_after;
1177 	    }
1178 
1179 #ifdef FEAT_FOLDING
1180 	    // adjust folds
1181 	    if (adjust_folds)
1182 		foldMarkAdjust(win, line1, line2, amount, amount_after);
1183 #endif
1184 	}
1185     }
1186 
1187 #ifdef FEAT_DIFF
1188     // adjust diffs
1189     diff_mark_adjust(line1, line2, amount, amount_after);
1190 #endif
1191 }
1192 
1193 // This code is used often, needs to be fast.
1194 #define col_adjust(pp) \
1195     { \
1196 	posp = pp; \
1197 	if (posp->lnum == lnum && posp->col >= mincol) \
1198 	{ \
1199 	    posp->lnum += lnum_amount; \
1200 	    if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1201 		posp->col = 0; \
1202 	    else if (posp->col < spaces_removed) \
1203 		posp->col = col_amount + spaces_removed; \
1204 	    else \
1205 		posp->col += col_amount; \
1206 	} \
1207     }
1208 
1209 /*
1210  * Adjust marks in line "lnum" at column "mincol" and further: add
1211  * "lnum_amount" to the line number and add "col_amount" to the column
1212  * position.
1213  * "spaces_removed" is the number of spaces that were removed, matters when the
1214  * cursor is inside them.
1215  */
1216     void
mark_col_adjust(linenr_T lnum,colnr_T mincol,long lnum_amount,long col_amount,int spaces_removed)1217 mark_col_adjust(
1218     linenr_T	lnum,
1219     colnr_T	mincol,
1220     long	lnum_amount,
1221     long	col_amount,
1222     int		spaces_removed)
1223 {
1224     int		i;
1225     int		fnum = curbuf->b_fnum;
1226     win_T	*win;
1227     pos_T	*posp;
1228 
1229     if ((col_amount == 0L && lnum_amount == 0L)
1230 				       || (cmdmod.cmod_flags & CMOD_LOCKMARKS))
1231 	return; // nothing to do
1232 
1233     // named marks, lower case and upper case
1234     for (i = 0; i < NMARKS; i++)
1235     {
1236 	col_adjust(&(curbuf->b_namedm[i]));
1237 	if (namedfm[i].fmark.fnum == fnum)
1238 	    col_adjust(&(namedfm[i].fmark.mark));
1239     }
1240     for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1241     {
1242 	if (namedfm[i].fmark.fnum == fnum)
1243 	    col_adjust(&(namedfm[i].fmark.mark));
1244     }
1245 
1246     // last Insert position
1247     col_adjust(&(curbuf->b_last_insert));
1248 
1249     // last change position
1250     col_adjust(&(curbuf->b_last_change));
1251 
1252 #ifdef FEAT_JUMPLIST
1253     // list of change positions
1254     for (i = 0; i < curbuf->b_changelistlen; ++i)
1255 	col_adjust(&(curbuf->b_changelist[i]));
1256 #endif
1257 
1258     // Visual area
1259     col_adjust(&(curbuf->b_visual.vi_start));
1260     col_adjust(&(curbuf->b_visual.vi_end));
1261 
1262     // previous context mark
1263     col_adjust(&(curwin->w_pcmark));
1264 
1265     // previous pcmark
1266     col_adjust(&(curwin->w_prev_pcmark));
1267 
1268     // saved cursor for formatting
1269     col_adjust(&saved_cursor);
1270 
1271     /*
1272      * Adjust items in all windows related to the current buffer.
1273      */
1274     FOR_ALL_WINDOWS(win)
1275     {
1276 #ifdef FEAT_JUMPLIST
1277 	// marks in the jumplist
1278 	for (i = 0; i < win->w_jumplistlen; ++i)
1279 	    if (win->w_jumplist[i].fmark.fnum == fnum)
1280 		col_adjust(&(win->w_jumplist[i].fmark.mark));
1281 #endif
1282 
1283 	if (win->w_buffer == curbuf)
1284 	{
1285 	    // marks in the tag stack
1286 	    for (i = 0; i < win->w_tagstacklen; i++)
1287 		if (win->w_tagstack[i].fmark.fnum == fnum)
1288 		    col_adjust(&(win->w_tagstack[i].fmark.mark));
1289 
1290 	    // cursor position for other windows with the same buffer
1291 	    if (win != curwin)
1292 		col_adjust(&win->w_cursor);
1293 	}
1294     }
1295 }
1296 
1297 #ifdef FEAT_JUMPLIST
1298 /*
1299  * When deleting lines, this may create duplicate marks in the
1300  * jumplist. They will be removed here for the specified window.
1301  * When "loadfiles" is TRUE first ensure entries have the "fnum" field set
1302  * (this may be a bit slow).
1303  */
1304     void
cleanup_jumplist(win_T * wp,int loadfiles)1305 cleanup_jumplist(win_T *wp, int loadfiles)
1306 {
1307     int	    i;
1308     int	    from, to;
1309 
1310     if (loadfiles)
1311     {
1312 	// If specified, load all the files from the jump list. This is
1313 	// needed to properly clean up duplicate entries, but will take some
1314 	// time.
1315 	for (i = 0; i < wp->w_jumplistlen; ++i)
1316 	{
1317 	    if ((wp->w_jumplist[i].fmark.fnum == 0) &&
1318 		    (wp->w_jumplist[i].fmark.mark.lnum != 0))
1319 		fname2fnum(&wp->w_jumplist[i]);
1320 	}
1321     }
1322 
1323     to = 0;
1324     for (from = 0; from < wp->w_jumplistlen; ++from)
1325     {
1326 	if (wp->w_jumplistidx == from)
1327 	    wp->w_jumplistidx = to;
1328 	for (i = from + 1; i < wp->w_jumplistlen; ++i)
1329 	    if (wp->w_jumplist[i].fmark.fnum
1330 					== wp->w_jumplist[from].fmark.fnum
1331 		    && wp->w_jumplist[from].fmark.fnum != 0
1332 		    && wp->w_jumplist[i].fmark.mark.lnum
1333 				  == wp->w_jumplist[from].fmark.mark.lnum)
1334 		break;
1335 	if (i >= wp->w_jumplistlen)	    // no duplicate
1336 	    wp->w_jumplist[to++] = wp->w_jumplist[from];
1337 	else
1338 	    vim_free(wp->w_jumplist[from].fname);
1339     }
1340     if (wp->w_jumplistidx == wp->w_jumplistlen)
1341 	wp->w_jumplistidx = to;
1342     wp->w_jumplistlen = to;
1343 }
1344 
1345 /*
1346  * Copy the jumplist from window "from" to window "to".
1347  */
1348     void
copy_jumplist(win_T * from,win_T * to)1349 copy_jumplist(win_T *from, win_T *to)
1350 {
1351     int		i;
1352 
1353     for (i = 0; i < from->w_jumplistlen; ++i)
1354     {
1355 	to->w_jumplist[i] = from->w_jumplist[i];
1356 	if (from->w_jumplist[i].fname != NULL)
1357 	    to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1358     }
1359     to->w_jumplistlen = from->w_jumplistlen;
1360     to->w_jumplistidx = from->w_jumplistidx;
1361 }
1362 
1363 /*
1364  * Free items in the jumplist of window "wp".
1365  */
1366     void
free_jumplist(win_T * wp)1367 free_jumplist(win_T *wp)
1368 {
1369     int		i;
1370 
1371     for (i = 0; i < wp->w_jumplistlen; ++i)
1372 	vim_free(wp->w_jumplist[i].fname);
1373 }
1374 #endif // FEAT_JUMPLIST
1375 
1376     void
set_last_cursor(win_T * win)1377 set_last_cursor(win_T *win)
1378 {
1379     if (win->w_buffer != NULL)
1380 	win->w_buffer->b_last_cursor = win->w_cursor;
1381 }
1382 
1383 #if defined(EXITFREE) || defined(PROTO)
1384     void
free_all_marks(void)1385 free_all_marks(void)
1386 {
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 
1395 /*
1396  * Return a pointer to the named file marks.
1397  */
1398     xfmark_T *
get_namedfm(void)1399 get_namedfm(void)
1400 {
1401     return namedfm;
1402 }
1403 
1404 #if defined(FEAT_EVAL) || defined(PROTO)
1405 /*
1406  * Add information about mark 'mname' to list 'l'
1407  */
1408     static int
add_mark(list_T * l,char_u * mname,pos_T * pos,int bufnr,char_u * fname)1409 add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
1410 {
1411     dict_T	*d;
1412     list_T	*lpos;
1413 
1414     if (pos->lnum <= 0)
1415 	return OK;
1416 
1417     d = dict_alloc();
1418     if (d == NULL)
1419 	return FAIL;
1420 
1421     if (list_append_dict(l, d) == FAIL)
1422     {
1423 	dict_unref(d);
1424 	return FAIL;
1425     }
1426 
1427     lpos = list_alloc();
1428     if (lpos == NULL)
1429 	return FAIL;
1430 
1431     list_append_number(lpos, bufnr);
1432     list_append_number(lpos, pos->lnum);
1433     list_append_number(lpos, pos->col + 1);
1434     list_append_number(lpos, pos->coladd);
1435 
1436     if (dict_add_string(d, "mark", mname) == FAIL
1437 	    || dict_add_list(d, "pos", lpos) == FAIL
1438 	    || (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
1439 	return FAIL;
1440 
1441     return OK;
1442 }
1443 
1444 /*
1445  * Get information about marks local to a buffer.
1446  */
1447     static void
get_buf_local_marks(buf_T * buf,list_T * l)1448 get_buf_local_marks(buf_T *buf, list_T *l)
1449 {
1450     char_u	mname[3] = "' ";
1451     int		i;
1452 
1453     // Marks 'a' to 'z'
1454     for (i = 0; i < NMARKS; ++i)
1455     {
1456 	mname[1] = 'a' + i;
1457 	add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL);
1458     }
1459 
1460     // Mark '' is a window local mark and not a buffer local mark
1461     add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL);
1462 
1463     add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL);
1464     add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL);
1465     add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL);
1466     add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL);
1467     add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL);
1468     add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL);
1469     add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL);
1470 }
1471 
1472 /*
1473  * Get information about global marks ('A' to 'Z' and '0' to '9')
1474  */
1475     static void
get_global_marks(list_T * l)1476 get_global_marks(list_T *l)
1477 {
1478     char_u	mname[3] = "' ";
1479     int		i;
1480     char_u	*name;
1481 
1482     // Marks 'A' to 'Z' and '0' to '9'
1483     for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
1484     {
1485 	if (namedfm[i].fmark.fnum != 0)
1486 	    name = buflist_nr2name(namedfm[i].fmark.fnum, TRUE, TRUE);
1487 	else
1488 	    name = namedfm[i].fname;
1489 	if (name != NULL)
1490 	{
1491 	    mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A';
1492 	    add_mark(l, mname, &namedfm[i].fmark.mark,
1493 		    namedfm[i].fmark.fnum, name);
1494 	    if (namedfm[i].fmark.fnum != 0)
1495 		vim_free(name);
1496 	}
1497     }
1498 }
1499 
1500 /*
1501  * getmarklist() function
1502  */
1503     void
f_getmarklist(typval_T * argvars,typval_T * rettv)1504 f_getmarklist(typval_T *argvars, typval_T *rettv)
1505 {
1506     buf_T	*buf = NULL;
1507 
1508     if (rettv_list_alloc(rettv) != OK)
1509 	return;
1510 
1511     if (in_vim9script() && check_for_opt_buffer_arg(argvars, 0) == FAIL)
1512 	return;
1513 
1514     if (argvars[0].v_type == VAR_UNKNOWN)
1515     {
1516 	get_global_marks(rettv->vval.v_list);
1517 	return;
1518     }
1519 
1520     buf = tv_get_buf(&argvars[0], FALSE);
1521     if (buf == NULL)
1522 	return;
1523 
1524     get_buf_local_marks(buf, rettv->vval.v_list);
1525 }
1526 #endif
1527