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