blob: 540f70b97bb8bd2f94406c9b5e0c124db7154ed9 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
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#define EXTRA_MARKS 10 /* marks 0-9 */
28static xfmark_T namedfm[NMARKS + EXTRA_MARKS]; /* marks with file nr */
29
30static void fname2fnum __ARGS((xfmark_T *fm));
31static void fmarks_check_one __ARGS((xfmark_T *fm, char_u *name, buf_T *buf));
32static char_u *mark_line __ARGS((pos_T *mp, int lead_len));
33static void show_one_mark __ARGS((int, char_u *, pos_T *, char_u *, int current));
34#ifdef FEAT_JUMPLIST
35static void cleanup_jumplist __ARGS((void));
36#endif
37#ifdef FEAT_VIMINFO
38static void write_one_filemark __ARGS((FILE *fp, xfmark_T *fm, int c1, int c2));
39#endif
40
41/*
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000042 * Set named mark "c" at current cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +000043 * Returns OK on success, FAIL if bad name given.
44 */
45 int
46setmark(c)
47 int c;
48{
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000049 return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
50}
51
52/*
53 * Set named mark "c" to position "pos".
54 * When "c" is upper case use file "fnum".
55 * Returns OK on success, FAIL if bad name given.
56 */
57 int
58setmark_pos(c, pos, fnum)
59 int c;
60 pos_T *pos;
61 int fnum;
62{
Bram Moolenaar071d4272004-06-13 20:20:40 +000063 int i;
64
65 /* Check for a special key (may cause islower() to crash). */
66 if (c < 0)
67 return FAIL;
68
69 if (c == '\'' || c == '`')
70 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000071 if (pos == &curwin->w_cursor)
72 {
73 setpcmark();
74 /* keep it even when the cursor doesn't move */
75 curwin->w_prev_pcmark = curwin->w_pcmark;
76 }
77 else
78 curwin->w_pcmark = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000079 return OK;
80 }
81
Bram Moolenaar08250432008-02-13 11:42:46 +000082 if (c == '"')
83 {
84 curbuf->b_last_cursor = *pos;
85 return OK;
86 }
87
Bram Moolenaar071d4272004-06-13 20:20:40 +000088 /* Allow setting '[ and '] for an autocommand that simulates reading a
89 * file. */
90 if (c == '[')
91 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000092 curbuf->b_op_start = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000093 return OK;
94 }
95 if (c == ']')
96 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000097 curbuf->b_op_end = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000098 return OK;
99 }
100
Bram Moolenaar0306ac32012-07-06 17:51:28 +0200101#ifdef FEAT_VISUAL
102 if (c == '<')
103 {
104 curbuf->b_visual.vi_start = *pos;
105 return OK;
106 }
107 if (c == '>')
108 {
109 curbuf->b_visual.vi_end = *pos;
110 return OK;
111 }
112#endif
113
Bram Moolenaar071d4272004-06-13 20:20:40 +0000114#ifndef EBCDIC
115 if (c > 'z') /* some islower() and isupper() cannot handle
116 characters above 127 */
117 return FAIL;
118#endif
119 if (islower(c))
120 {
121 i = c - 'a';
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000122 curbuf->b_namedm[i] = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123 return OK;
124 }
125 if (isupper(c))
126 {
127 i = c - 'A';
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000128 namedfm[i].fmark.mark = *pos;
129 namedfm[i].fmark.fnum = fnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000130 vim_free(namedfm[i].fname);
131 namedfm[i].fname = NULL;
132 return OK;
133 }
134 return FAIL;
135}
136
137/*
138 * Set the previous context mark to the current position and add it to the
139 * jump list.
140 */
141 void
142setpcmark()
143{
144#ifdef FEAT_JUMPLIST
145 int i;
146 xfmark_T *fm;
147#endif
148#ifdef JUMPLIST_ROTATE
149 xfmark_T tempmark;
150#endif
151
152 /* for :global the mark is set only once */
153 if (global_busy || listcmd_busy || cmdmod.keepjumps)
154 return;
155
156 curwin->w_prev_pcmark = curwin->w_pcmark;
157 curwin->w_pcmark = curwin->w_cursor;
158
159#ifdef FEAT_JUMPLIST
160# ifdef JUMPLIST_ROTATE
161 /*
162 * If last used entry is not at the top, put it at the top by rotating
163 * the stack until it is (the newer entries will be at the bottom).
164 * Keep one entry (the last used one) at the top.
165 */
166 if (curwin->w_jumplistidx < curwin->w_jumplistlen)
167 ++curwin->w_jumplistidx;
168 while (curwin->w_jumplistidx < curwin->w_jumplistlen)
169 {
170 tempmark = curwin->w_jumplist[curwin->w_jumplistlen - 1];
171 for (i = curwin->w_jumplistlen - 1; i > 0; --i)
172 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
173 curwin->w_jumplist[0] = tempmark;
174 ++curwin->w_jumplistidx;
175 }
176# endif
177
178 /* If jumplist is full: remove oldest entry */
179 if (++curwin->w_jumplistlen > JUMPLISTSIZE)
180 {
181 curwin->w_jumplistlen = JUMPLISTSIZE;
182 vim_free(curwin->w_jumplist[0].fname);
183 for (i = 1; i < JUMPLISTSIZE; ++i)
184 curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
185 }
186 curwin->w_jumplistidx = curwin->w_jumplistlen;
187 fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
188
189 fm->fmark.mark = curwin->w_pcmark;
190 fm->fmark.fnum = curbuf->b_fnum;
191 fm->fname = NULL;
192#endif
193}
194
195/*
196 * To change context, call setpcmark(), then move the current position to
197 * where ever, then call checkpcmark(). This ensures that the previous
198 * context will only be changed if the cursor moved to a different line.
199 * If pcmark was deleted (with "dG") the previous mark is restored.
200 */
201 void
202checkpcmark()
203{
204 if (curwin->w_prev_pcmark.lnum != 0
205 && (equalpos(curwin->w_pcmark, curwin->w_cursor)
206 || curwin->w_pcmark.lnum == 0))
207 {
208 curwin->w_pcmark = curwin->w_prev_pcmark;
209 curwin->w_prev_pcmark.lnum = 0; /* Show it has been checked */
210 }
211}
212
213#if defined(FEAT_JUMPLIST) || defined(PROTO)
214/*
215 * move "count" positions in the jump list (count may be negative)
216 */
217 pos_T *
218movemark(count)
219 int count;
220{
221 pos_T *pos;
222 xfmark_T *jmp;
223
224 cleanup_jumplist();
225
226 if (curwin->w_jumplistlen == 0) /* nothing to jump to */
227 return (pos_T *)NULL;
228
229 for (;;)
230 {
231 if (curwin->w_jumplistidx + count < 0
232 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
233 return (pos_T *)NULL;
234
235 /*
236 * if first CTRL-O or CTRL-I command after a jump, add cursor position
Bram Moolenaarf711faf2007-05-10 16:48:19 +0000237 * to list. Careful: If there are duplicates (CTRL-O immediately after
Bram Moolenaar071d4272004-06-13 20:20:40 +0000238 * starting Vim on a file), another entry may have been removed.
239 */
240 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
241 {
242 setpcmark();
243 --curwin->w_jumplistidx; /* skip the new entry */
244 if (curwin->w_jumplistidx + count < 0)
245 return (pos_T *)NULL;
246 }
247
248 curwin->w_jumplistidx += count;
249
250 jmp = curwin->w_jumplist + curwin->w_jumplistidx;
251 if (jmp->fmark.fnum == 0)
252 fname2fnum(jmp);
253 if (jmp->fmark.fnum != curbuf->b_fnum)
254 {
255 /* jump to other file */
256 if (buflist_findnr(jmp->fmark.fnum) == NULL)
257 { /* Skip this one .. */
258 count += count < 0 ? -1 : 1;
259 continue;
260 }
261 if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
262 0, FALSE) == FAIL)
263 return (pos_T *)NULL;
264 /* Set lnum again, autocommands my have changed it */
265 curwin->w_cursor = jmp->fmark.mark;
266 pos = (pos_T *)-1;
267 }
268 else
269 pos = &(jmp->fmark.mark);
270 return pos;
271 }
272}
273
274/*
275 * Move "count" positions in the changelist (count may be negative).
276 */
277 pos_T *
278movechangelist(count)
279 int count;
280{
281 int n;
282
283 if (curbuf->b_changelistlen == 0) /* nothing to jump to */
284 return (pos_T *)NULL;
285
286 n = curwin->w_changelistidx;
287 if (n + count < 0)
288 {
289 if (n == 0)
290 return (pos_T *)NULL;
291 n = 0;
292 }
293 else if (n + count >= curbuf->b_changelistlen)
294 {
295 if (n == curbuf->b_changelistlen - 1)
296 return (pos_T *)NULL;
297 n = curbuf->b_changelistlen - 1;
298 }
299 else
300 n += count;
301 curwin->w_changelistidx = n;
302 return curbuf->b_changelist + n;
303}
304#endif
305
306/*
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100307 * Find mark "c" in buffer pointed to by "buf".
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000308 * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
309 * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
310 * another file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000311 * Returns:
312 * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
313 * in another file which can't be gotten. (caller needs to check lnum!)
314 * - NULL if there is no mark called 'c'.
315 * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
316 */
317 pos_T *
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100318getmark_buf(buf, c, changefile)
319 buf_T *buf;
320 int c;
321 int changefile;
322{
323 return getmark_buf_fnum(buf, c, changefile, NULL);
324}
325
326 pos_T *
Bram Moolenaar071d4272004-06-13 20:20:40 +0000327getmark(c, changefile)
328 int c;
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000329 int changefile;
330{
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100331 return getmark_buf_fnum(curbuf, c, changefile, NULL);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000332}
333
334 pos_T *
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100335getmark_buf_fnum(buf, c, changefile, fnum)
336 buf_T *buf;
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000337 int c;
338 int changefile;
339 int *fnum;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000340{
341 pos_T *posp;
342#ifdef FEAT_VISUAL
343 pos_T *startp, *endp;
344#endif
345 static pos_T pos_copy;
346
347 posp = NULL;
348
349 /* Check for special key, can't be a mark name and might cause islower()
350 * to crash. */
351 if (c < 0)
352 return posp;
353#ifndef EBCDIC
354 if (c > '~') /* check for islower()/isupper() */
355 ;
356 else
357#endif
358 if (c == '\'' || c == '`') /* previous context mark */
359 {
360 pos_copy = curwin->w_pcmark; /* need to make a copy because */
361 posp = &pos_copy; /* w_pcmark may be changed soon */
362 }
363 else if (c == '"') /* to pos when leaving buffer */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100364 posp = &(buf->b_last_cursor);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365 else if (c == '^') /* to where Insert mode stopped */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100366 posp = &(buf->b_last_insert);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000367 else if (c == '.') /* to where last change was made */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100368 posp = &(buf->b_last_change);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000369 else if (c == '[') /* to start of previous operator */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100370 posp = &(buf->b_op_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000371 else if (c == ']') /* to end of previous operator */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100372 posp = &(buf->b_op_end);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000373 else if (c == '{' || c == '}') /* to previous/next paragraph */
374 {
375 pos_T pos;
376 oparg_T oa;
377 int slcb = listcmd_busy;
378
379 pos = curwin->w_cursor;
380 listcmd_busy = TRUE; /* avoid that '' is changed */
Bram Moolenaar8b96d642005-09-05 22:05:30 +0000381 if (findpar(&oa.inclusive,
382 c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000383 {
384 pos_copy = curwin->w_cursor;
385 posp = &pos_copy;
386 }
387 curwin->w_cursor = pos;
388 listcmd_busy = slcb;
389 }
390 else if (c == '(' || c == ')') /* to previous/next sentence */
391 {
392 pos_T pos;
393 int slcb = listcmd_busy;
394
395 pos = curwin->w_cursor;
396 listcmd_busy = TRUE; /* avoid that '' is changed */
397 if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
398 {
399 pos_copy = curwin->w_cursor;
400 posp = &pos_copy;
401 }
402 curwin->w_cursor = pos;
403 listcmd_busy = slcb;
404 }
405#ifdef FEAT_VISUAL
406 else if (c == '<' || c == '>') /* start/end of visual area */
407 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100408 startp = &buf->b_visual.vi_start;
409 endp = &buf->b_visual.vi_end;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000410 if ((c == '<') == lt(*startp, *endp))
411 posp = startp;
412 else
413 posp = endp;
414 /*
415 * For Visual line mode, set mark at begin or end of line
416 */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100417 if (buf->b_visual.vi_mode == 'V')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418 {
419 pos_copy = *posp;
420 posp = &pos_copy;
421 if (c == '<')
422 pos_copy.col = 0;
423 else
424 pos_copy.col = MAXCOL;
425#ifdef FEAT_VIRTUALEDIT
426 pos_copy.coladd = 0;
427#endif
428 }
429 }
430#endif
431 else if (ASCII_ISLOWER(c)) /* normal named mark */
432 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100433 posp = &(buf->b_namedm[c - 'a']);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000434 }
435 else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) /* named file mark */
436 {
437 if (VIM_ISDIGIT(c))
438 c = c - '0' + NMARKS;
439 else
440 c -= 'A';
441 posp = &(namedfm[c].fmark.mark);
442
443 if (namedfm[c].fmark.fnum == 0)
444 fname2fnum(&namedfm[c]);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000445
446 if (fnum != NULL)
447 *fnum = namedfm[c].fmark.fnum;
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100448 else if (namedfm[c].fmark.fnum != buf->b_fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000450 /* mark is in another file */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000451 posp = &pos_copy;
452
Bram Moolenaar071d4272004-06-13 20:20:40 +0000453 if (namedfm[c].fmark.mark.lnum != 0
454 && changefile && namedfm[c].fmark.fnum)
455 {
456 if (buflist_getfile(namedfm[c].fmark.fnum,
457 (linenr_T)1, GETF_SETMARK, FALSE) == OK)
458 {
459 /* Set the lnum now, autocommands could have changed it */
460 curwin->w_cursor = namedfm[c].fmark.mark;
461 return (pos_T *)-1;
462 }
463 pos_copy.lnum = -1; /* can't get file */
464 }
465 else
466 pos_copy.lnum = 0; /* mark exists, but is not valid in
467 current buffer */
468 }
469 }
470
471 return posp;
472}
473
474/*
475 * Search for the next named mark in the current file.
476 *
477 * Returns pointer to pos_T of the next mark or NULL if no mark is found.
478 */
479 pos_T *
480getnextmark(startpos, dir, begin_line)
481 pos_T *startpos; /* where to start */
482 int dir; /* direction for search */
483 int begin_line;
484{
485 int i;
486 pos_T *result = NULL;
487 pos_T pos;
488
489 pos = *startpos;
490
491 /* When searching backward and leaving the cursor on the first non-blank,
492 * position must be in a previous line.
493 * When searching forward and leaving the cursor on the first non-blank,
494 * position must be in a next line. */
495 if (dir == BACKWARD && begin_line)
496 pos.col = 0;
497 else if (dir == FORWARD && begin_line)
498 pos.col = MAXCOL;
499
500 for (i = 0; i < NMARKS; i++)
501 {
502 if (curbuf->b_namedm[i].lnum > 0)
503 {
504 if (dir == FORWARD)
505 {
506 if ((result == NULL || lt(curbuf->b_namedm[i], *result))
507 && lt(pos, curbuf->b_namedm[i]))
508 result = &curbuf->b_namedm[i];
509 }
510 else
511 {
512 if ((result == NULL || lt(*result, curbuf->b_namedm[i]))
513 && lt(curbuf->b_namedm[i], pos))
514 result = &curbuf->b_namedm[i];
515 }
516 }
517 }
518
519 return result;
520}
521
522/*
523 * For an xtended filemark: set the fnum from the fname.
524 * This is used for marks obtained from the .viminfo file. It's postponed
525 * until the mark is used to avoid a long startup delay.
526 */
527 static void
528fname2fnum(fm)
529 xfmark_T *fm;
530{
531 char_u *p;
532
533 if (fm->fname != NULL)
534 {
535 /*
536 * First expand "~/" in the file name to the home directory.
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000537 * Don't expand the whole name, it may contain other '~' chars.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538 */
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000539 if (fm->fname[0] == '~' && (fm->fname[1] == '/'
540#ifdef BACKSLASH_IN_FILENAME
541 || fm->fname[1] == '\\'
542#endif
543 ))
544 {
545 int len;
546
547 expand_env((char_u *)"~/", NameBuff, MAXPATHL);
Bram Moolenaarcb4cef22008-03-16 15:04:34 +0000548 len = (int)STRLEN(NameBuff);
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000549 vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
550 }
551 else
552 vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
553
554 /* Try to shorten the file name. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000555 mch_dirname(IObuff, IOSIZE);
556 p = shorten_fname(NameBuff, IObuff);
557
558 /* buflist_new() will call fmarks_check_names() */
559 (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
560 }
561}
562
563/*
564 * Check all file marks for a name that matches the file name in buf.
565 * May replace the name with an fnum.
566 * Used for marks that come from the .viminfo file.
567 */
568 void
569fmarks_check_names(buf)
570 buf_T *buf;
571{
572 char_u *name;
573 int i;
574#ifdef FEAT_JUMPLIST
575 win_T *wp;
576#endif
577
578 if (buf->b_ffname == NULL)
579 return;
580
581 name = home_replace_save(buf, buf->b_ffname);
582 if (name == NULL)
583 return;
584
585 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
586 fmarks_check_one(&namedfm[i], name, buf);
587
588#ifdef FEAT_JUMPLIST
589 FOR_ALL_WINDOWS(wp)
590 {
591 for (i = 0; i < wp->w_jumplistlen; ++i)
592 fmarks_check_one(&wp->w_jumplist[i], name, buf);
593 }
594#endif
595
596 vim_free(name);
597}
598
599 static void
600fmarks_check_one(fm, name, buf)
601 xfmark_T *fm;
602 char_u *name;
603 buf_T *buf;
604{
605 if (fm->fmark.fnum == 0
606 && fm->fname != NULL
607 && fnamecmp(name, fm->fname) == 0)
608 {
609 fm->fmark.fnum = buf->b_fnum;
610 vim_free(fm->fname);
611 fm->fname = NULL;
612 }
613}
614
615/*
616 * Check a if a position from a mark is valid.
617 * Give and error message and return FAIL if not.
618 */
619 int
620check_mark(pos)
621 pos_T *pos;
622{
623 if (pos == NULL)
624 {
625 EMSG(_(e_umark));
626 return FAIL;
627 }
628 if (pos->lnum <= 0)
629 {
630 /* lnum is negative if mark is in another file can can't get that
631 * file, error message already give then. */
632 if (pos->lnum == 0)
633 EMSG(_(e_marknotset));
634 return FAIL;
635 }
636 if (pos->lnum > curbuf->b_ml.ml_line_count)
637 {
638 EMSG(_(e_markinval));
639 return FAIL;
640 }
641 return OK;
642}
643
644/*
645 * clrallmarks() - clear all marks in the buffer 'buf'
646 *
647 * Used mainly when trashing the entire buffer during ":e" type commands
648 */
649 void
650clrallmarks(buf)
651 buf_T *buf;
652{
653 static int i = -1;
654
655 if (i == -1) /* first call ever: initialize */
656 for (i = 0; i < NMARKS + 1; i++)
657 {
658 namedfm[i].fmark.mark.lnum = 0;
659 namedfm[i].fname = NULL;
660 }
661
662 for (i = 0; i < NMARKS; i++)
663 buf->b_namedm[i].lnum = 0;
664 buf->b_op_start.lnum = 0; /* start/end op mark cleared */
665 buf->b_op_end.lnum = 0;
666 buf->b_last_cursor.lnum = 1; /* '" mark cleared */
667 buf->b_last_cursor.col = 0;
668#ifdef FEAT_VIRTUALEDIT
669 buf->b_last_cursor.coladd = 0;
670#endif
671 buf->b_last_insert.lnum = 0; /* '^ mark cleared */
672 buf->b_last_change.lnum = 0; /* '. mark cleared */
673#ifdef FEAT_JUMPLIST
674 buf->b_changelistlen = 0;
675#endif
676}
677
678/*
679 * Get name of file from a filemark.
680 * When it's in the current buffer, return the text at the mark.
681 * Returns an allocated string.
682 */
683 char_u *
684fm_getname(fmark, lead_len)
685 fmark_T *fmark;
686 int lead_len;
687{
688 if (fmark->fnum == curbuf->b_fnum) /* current buffer */
689 return mark_line(&(fmark->mark), lead_len);
690 return buflist_nr2name(fmark->fnum, FALSE, TRUE);
691}
692
693/*
694 * Return the line at mark "mp". Truncate to fit in window.
695 * The returned string has been allocated.
696 */
697 static char_u *
698mark_line(mp, lead_len)
699 pos_T *mp;
700 int lead_len;
701{
702 char_u *s, *p;
703 int len;
704
705 if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
706 return vim_strsave((char_u *)"-invalid-");
707 s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (int)Columns);
708 if (s == NULL)
709 return NULL;
710 /* Truncate the line to fit it in the window */
711 len = 0;
Bram Moolenaar1cd871b2004-12-19 22:46:22 +0000712 for (p = s; *p != NUL; mb_ptr_adv(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000713 {
714 len += ptr2cells(p);
715 if (len >= Columns - lead_len)
716 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000717 }
718 *p = NUL;
719 return s;
720}
721
722/*
723 * print the marks
724 */
725 void
726do_marks(eap)
727 exarg_T *eap;
728{
729 char_u *arg = eap->arg;
730 int i;
731 char_u *name;
732
733 if (arg != NULL && *arg == NUL)
734 arg = NULL;
735
736 show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
737 for (i = 0; i < NMARKS; ++i)
738 show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
739 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
740 {
741 if (namedfm[i].fmark.fnum != 0)
742 name = fm_getname(&namedfm[i].fmark, 15);
743 else
744 name = namedfm[i].fname;
745 if (name != NULL)
746 {
747 show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
748 arg, &namedfm[i].fmark.mark, name,
749 namedfm[i].fmark.fnum == curbuf->b_fnum);
750 if (namedfm[i].fmark.fnum != 0)
751 vim_free(name);
752 }
753 }
754 show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
755 show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
756 show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
757 show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
758 show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
759#ifdef FEAT_VISUAL
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000760 show_one_mark('<', arg, &curbuf->b_visual.vi_start, NULL, TRUE);
761 show_one_mark('>', arg, &curbuf->b_visual.vi_end, NULL, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762#endif
763 show_one_mark(-1, arg, NULL, NULL, FALSE);
764}
765
766 static void
767show_one_mark(c, arg, p, name, current)
768 int c;
769 char_u *arg;
770 pos_T *p;
771 char_u *name;
772 int current; /* in current file */
773{
774 static int did_title = FALSE;
775 int mustfree = FALSE;
776
777 if (c == -1) /* finish up */
778 {
779 if (did_title)
780 did_title = FALSE;
781 else
782 {
783 if (arg == NULL)
784 MSG(_("No marks set"));
785 else
786 EMSG2(_("E283: No marks matching \"%s\""), arg);
787 }
788 }
789 /* don't output anything if 'q' typed at --more-- prompt */
790 else if (!got_int
791 && (arg == NULL || vim_strchr(arg, c) != NULL)
792 && p->lnum != 0)
793 {
794 if (!did_title)
795 {
796 /* Highlight title */
797 MSG_PUTS_TITLE(_("\nmark line col file/text"));
798 did_title = TRUE;
799 }
800 msg_putchar('\n');
801 if (!got_int)
802 {
803 sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
804 msg_outtrans(IObuff);
805 if (name == NULL && current)
806 {
807 name = mark_line(p, 15);
808 mustfree = TRUE;
809 }
810 if (name != NULL)
811 {
812 msg_outtrans_attr(name, current ? hl_attr(HLF_D) : 0);
813 if (mustfree)
814 vim_free(name);
815 }
816 }
817 out_flush(); /* show one line at a time */
818 }
819}
820
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000821/*
822 * ":delmarks[!] [marks]"
823 */
824 void
825ex_delmarks(eap)
826 exarg_T *eap;
827{
828 char_u *p;
829 int from, to;
830 int i;
831 int lower;
832 int digit;
833 int n;
834
835 if (*eap->arg == NUL && eap->forceit)
836 /* clear all marks */
837 clrallmarks(curbuf);
838 else if (eap->forceit)
839 EMSG(_(e_invarg));
840 else if (*eap->arg == NUL)
841 EMSG(_(e_argreq));
842 else
843 {
844 /* clear specified marks only */
845 for (p = eap->arg; *p != NUL; ++p)
846 {
847 lower = ASCII_ISLOWER(*p);
848 digit = VIM_ISDIGIT(*p);
849 if (lower || digit || ASCII_ISUPPER(*p))
850 {
851 if (p[1] == '-')
852 {
853 /* clear range of marks */
854 from = *p;
855 to = p[2];
856 if (!(lower ? ASCII_ISLOWER(p[2])
857 : (digit ? VIM_ISDIGIT(p[2])
858 : ASCII_ISUPPER(p[2])))
859 || to < from)
860 {
861 EMSG2(_(e_invarg2), p);
862 return;
863 }
864 p += 2;
865 }
866 else
867 /* clear one lower case mark */
868 from = to = *p;
869
870 for (i = from; i <= to; ++i)
871 {
872 if (lower)
873 curbuf->b_namedm[i - 'a'].lnum = 0;
874 else
875 {
876 if (digit)
877 n = i - '0' + NMARKS;
878 else
879 n = i - 'A';
880 namedfm[n].fmark.mark.lnum = 0;
881 vim_free(namedfm[n].fname);
882 namedfm[n].fname = NULL;
883 }
884 }
885 }
886 else
887 switch (*p)
888 {
889 case '"': curbuf->b_last_cursor.lnum = 0; break;
890 case '^': curbuf->b_last_insert.lnum = 0; break;
891 case '.': curbuf->b_last_change.lnum = 0; break;
892 case '[': curbuf->b_op_start.lnum = 0; break;
893 case ']': curbuf->b_op_end.lnum = 0; break;
894#ifdef FEAT_VISUAL
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000895 case '<': curbuf->b_visual.vi_start.lnum = 0; break;
896 case '>': curbuf->b_visual.vi_end.lnum = 0; break;
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000897#endif
898 case ' ': break;
899 default: EMSG2(_(e_invarg2), p);
900 return;
901 }
902 }
903 }
904}
905
Bram Moolenaar071d4272004-06-13 20:20:40 +0000906#if defined(FEAT_JUMPLIST) || defined(PROTO)
907/*
908 * print the jumplist
909 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000910 void
911ex_jumps(eap)
Bram Moolenaaraf0167f2009-05-16 15:31:32 +0000912 exarg_T *eap UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000913{
914 int i;
915 char_u *name;
916
917 cleanup_jumplist();
918 /* Highlight title */
919 MSG_PUTS_TITLE(_("\n jump line col file/text"));
920 for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
921 {
922 if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
923 {
924 if (curwin->w_jumplist[i].fmark.fnum == 0)
925 fname2fnum(&curwin->w_jumplist[i]);
926 name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
927 if (name == NULL) /* file name not available */
928 continue;
929
930 msg_putchar('\n');
931 if (got_int)
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000932 {
933 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000934 break;
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000935 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000936 sprintf((char *)IObuff, "%c %2d %5ld %4d ",
937 i == curwin->w_jumplistidx ? '>' : ' ',
938 i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
939 : curwin->w_jumplistidx - i,
940 curwin->w_jumplist[i].fmark.mark.lnum,
941 curwin->w_jumplist[i].fmark.mark.col);
942 msg_outtrans(IObuff);
943 msg_outtrans_attr(name,
944 curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
945 ? hl_attr(HLF_D) : 0);
946 vim_free(name);
947 ui_breakcheck();
948 }
949 out_flush();
950 }
951 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
952 MSG_PUTS("\n>");
953}
954
955/*
956 * print the changelist
957 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000958 void
959ex_changes(eap)
Bram Moolenaaraf0167f2009-05-16 15:31:32 +0000960 exarg_T *eap UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000961{
962 int i;
963 char_u *name;
964
965 /* Highlight title */
966 MSG_PUTS_TITLE(_("\nchange line col text"));
967
968 for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i)
969 {
970 if (curbuf->b_changelist[i].lnum != 0)
971 {
972 msg_putchar('\n');
973 if (got_int)
974 break;
975 sprintf((char *)IObuff, "%c %3d %5ld %4d ",
976 i == curwin->w_changelistidx ? '>' : ' ',
977 i > curwin->w_changelistidx ? i - curwin->w_changelistidx
978 : curwin->w_changelistidx - i,
979 (long)curbuf->b_changelist[i].lnum,
980 curbuf->b_changelist[i].col);
981 msg_outtrans(IObuff);
982 name = mark_line(&curbuf->b_changelist[i], 17);
983 if (name == NULL)
984 break;
985 msg_outtrans_attr(name, hl_attr(HLF_D));
986 vim_free(name);
987 ui_breakcheck();
988 }
989 out_flush();
990 }
991 if (curwin->w_changelistidx == curbuf->b_changelistlen)
992 MSG_PUTS("\n>");
993}
994#endif
995
996#define one_adjust(add) \
997 { \
998 lp = add; \
999 if (*lp >= line1 && *lp <= line2) \
1000 { \
1001 if (amount == MAXLNUM) \
1002 *lp = 0; \
1003 else \
1004 *lp += amount; \
1005 } \
1006 else if (amount_after && *lp > line2) \
1007 *lp += amount_after; \
1008 }
1009
1010/* don't delete the line, just put at first deleted line */
1011#define one_adjust_nodel(add) \
1012 { \
1013 lp = add; \
1014 if (*lp >= line1 && *lp <= line2) \
1015 { \
1016 if (amount == MAXLNUM) \
1017 *lp = line1; \
1018 else \
1019 *lp += amount; \
1020 } \
1021 else if (amount_after && *lp > line2) \
1022 *lp += amount_after; \
1023 }
1024
1025/*
1026 * Adjust marks between line1 and line2 (inclusive) to move 'amount' lines.
1027 * Must be called before changed_*(), appended_lines() or deleted_lines().
1028 * May be called before or after changing the text.
1029 * When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks
1030 * within this range are made invalid.
1031 * If 'amount_after' is non-zero adjust marks after line2.
1032 * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1033 * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1034 * or: mark_adjust(56, 55, MAXLNUM, 2);
1035 */
1036 void
1037mark_adjust(line1, line2, amount, amount_after)
1038 linenr_T line1;
1039 linenr_T line2;
1040 long amount;
1041 long amount_after;
1042{
1043 int i;
1044 int fnum = curbuf->b_fnum;
1045 linenr_T *lp;
1046 win_T *win;
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001047#ifdef FEAT_WINDOWS
1048 tabpage_T *tab;
1049#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001050
1051 if (line2 < line1 && amount_after == 0L) /* nothing to do */
1052 return;
1053
1054 if (!cmdmod.lockmarks)
1055 {
1056 /* named marks, lower case and upper case */
1057 for (i = 0; i < NMARKS; i++)
1058 {
1059 one_adjust(&(curbuf->b_namedm[i].lnum));
1060 if (namedfm[i].fmark.fnum == fnum)
1061 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1062 }
1063 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1064 {
1065 if (namedfm[i].fmark.fnum == fnum)
1066 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1067 }
1068
1069 /* last Insert position */
1070 one_adjust(&(curbuf->b_last_insert.lnum));
1071
1072 /* last change position */
1073 one_adjust(&(curbuf->b_last_change.lnum));
1074
1075#ifdef FEAT_JUMPLIST
1076 /* list of change positions */
1077 for (i = 0; i < curbuf->b_changelistlen; ++i)
1078 one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
1079#endif
1080
1081#ifdef FEAT_VISUAL
1082 /* Visual area */
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001083 one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1084 one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001085#endif
1086
1087#ifdef FEAT_QUICKFIX
1088 /* quickfix marks */
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001089 qf_mark_adjust(NULL, line1, line2, amount, amount_after);
1090 /* location lists */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001091 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001092 qf_mark_adjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001093#endif
1094
1095#ifdef FEAT_SIGNS
1096 sign_mark_adjust(line1, line2, amount, amount_after);
1097#endif
1098 }
1099
1100 /* previous context mark */
1101 one_adjust(&(curwin->w_pcmark.lnum));
1102
1103 /* previous pcmark */
1104 one_adjust(&(curwin->w_prev_pcmark.lnum));
1105
1106 /* saved cursor for formatting */
1107 if (saved_cursor.lnum != 0)
1108 one_adjust_nodel(&(saved_cursor.lnum));
1109
1110 /*
1111 * Adjust items in all windows related to the current buffer.
1112 */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001113 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001114 {
1115#ifdef FEAT_JUMPLIST
1116 if (!cmdmod.lockmarks)
1117 /* Marks in the jumplist. When deleting lines, this may create
1118 * duplicate marks in the jumplist, they will be removed later. */
1119 for (i = 0; i < win->w_jumplistlen; ++i)
1120 if (win->w_jumplist[i].fmark.fnum == fnum)
1121 one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
1122#endif
1123
1124 if (win->w_buffer == curbuf)
1125 {
1126 if (!cmdmod.lockmarks)
1127 /* marks in the tag stack */
1128 for (i = 0; i < win->w_tagstacklen; i++)
1129 if (win->w_tagstack[i].fmark.fnum == fnum)
1130 one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1131
1132#ifdef FEAT_VISUAL
1133 /* the displayed Visual area */
1134 if (win->w_old_cursor_lnum != 0)
1135 {
1136 one_adjust_nodel(&(win->w_old_cursor_lnum));
1137 one_adjust_nodel(&(win->w_old_visual_lnum));
1138 }
1139#endif
1140
1141 /* topline and cursor position for windows with the same buffer
1142 * other than the current window */
1143 if (win != curwin)
1144 {
1145 if (win->w_topline >= line1 && win->w_topline <= line2)
1146 {
1147 if (amount == MAXLNUM) /* topline is deleted */
1148 {
1149 if (line1 <= 1)
1150 win->w_topline = 1;
1151 else
1152 win->w_topline = line1 - 1;
1153 }
1154 else /* keep topline on the same line */
1155 win->w_topline += amount;
1156#ifdef FEAT_DIFF
1157 win->w_topfill = 0;
1158#endif
1159 }
1160 else if (amount_after && win->w_topline > line2)
1161 {
1162 win->w_topline += amount_after;
1163#ifdef FEAT_DIFF
1164 win->w_topfill = 0;
1165#endif
1166 }
1167 if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1168 {
1169 if (amount == MAXLNUM) /* line with cursor is deleted */
1170 {
1171 if (line1 <= 1)
1172 win->w_cursor.lnum = 1;
1173 else
1174 win->w_cursor.lnum = line1 - 1;
1175 win->w_cursor.col = 0;
1176 }
1177 else /* keep cursor on the same line */
1178 win->w_cursor.lnum += amount;
1179 }
1180 else if (amount_after && win->w_cursor.lnum > line2)
1181 win->w_cursor.lnum += amount_after;
1182 }
1183
1184#ifdef FEAT_FOLDING
1185 /* adjust folds */
1186 foldMarkAdjust(win, line1, line2, amount, amount_after);
1187#endif
1188 }
1189 }
1190
1191#ifdef FEAT_DIFF
1192 /* adjust diffs */
1193 diff_mark_adjust(line1, line2, amount, amount_after);
1194#endif
1195}
1196
1197/* This code is used often, needs to be fast. */
1198#define col_adjust(pp) \
1199 { \
1200 posp = pp; \
1201 if (posp->lnum == lnum && posp->col >= mincol) \
1202 { \
1203 posp->lnum += lnum_amount; \
1204 if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1205 posp->col = 0; \
1206 else \
1207 posp->col += col_amount; \
1208 } \
1209 }
1210
1211/*
1212 * Adjust marks in line "lnum" at column "mincol" and further: add
1213 * "lnum_amount" to the line number and add "col_amount" to the column
1214 * position.
1215 */
1216 void
1217mark_col_adjust(lnum, mincol, lnum_amount, col_amount)
1218 linenr_T lnum;
1219 colnr_T mincol;
1220 long lnum_amount;
1221 long col_amount;
1222{
1223 int i;
1224 int fnum = curbuf->b_fnum;
1225 win_T *win;
1226 pos_T *posp;
1227
1228 if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks)
1229 return; /* nothing to do */
1230
1231 /* named marks, lower case and upper case */
1232 for (i = 0; i < NMARKS; i++)
1233 {
1234 col_adjust(&(curbuf->b_namedm[i]));
1235 if (namedfm[i].fmark.fnum == fnum)
1236 col_adjust(&(namedfm[i].fmark.mark));
1237 }
1238 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1239 {
1240 if (namedfm[i].fmark.fnum == fnum)
1241 col_adjust(&(namedfm[i].fmark.mark));
1242 }
1243
1244 /* last Insert position */
1245 col_adjust(&(curbuf->b_last_insert));
1246
1247 /* last change position */
1248 col_adjust(&(curbuf->b_last_change));
1249
1250#ifdef FEAT_JUMPLIST
1251 /* list of change positions */
1252 for (i = 0; i < curbuf->b_changelistlen; ++i)
1253 col_adjust(&(curbuf->b_changelist[i]));
1254#endif
1255
1256#ifdef FEAT_VISUAL
1257 /* Visual area */
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001258 col_adjust(&(curbuf->b_visual.vi_start));
1259 col_adjust(&(curbuf->b_visual.vi_end));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260#endif
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 current window.
1301 */
1302 static void
1303cleanup_jumplist()
1304{
1305 int i;
1306 int from, to;
1307
1308 to = 0;
1309 for (from = 0; from < curwin->w_jumplistlen; ++from)
1310 {
1311 if (curwin->w_jumplistidx == from)
1312 curwin->w_jumplistidx = to;
1313 for (i = from + 1; i < curwin->w_jumplistlen; ++i)
1314 if (curwin->w_jumplist[i].fmark.fnum
1315 == curwin->w_jumplist[from].fmark.fnum
1316 && curwin->w_jumplist[from].fmark.fnum != 0
1317 && curwin->w_jumplist[i].fmark.mark.lnum
1318 == curwin->w_jumplist[from].fmark.mark.lnum)
1319 break;
1320 if (i >= curwin->w_jumplistlen) /* no duplicate */
1321 curwin->w_jumplist[to++] = curwin->w_jumplist[from];
1322 else
1323 vim_free(curwin->w_jumplist[from].fname);
1324 }
1325 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
1326 curwin->w_jumplistidx = to;
1327 curwin->w_jumplistlen = to;
1328}
1329
1330# if defined(FEAT_WINDOWS) || defined(PROTO)
1331/*
1332 * Copy the jumplist from window "from" to window "to".
1333 */
1334 void
1335copy_jumplist(from, to)
1336 win_T *from;
1337 win_T *to;
1338{
1339 int i;
1340
1341 for (i = 0; i < from->w_jumplistlen; ++i)
1342 {
1343 to->w_jumplist[i] = from->w_jumplist[i];
1344 if (from->w_jumplist[i].fname != NULL)
1345 to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1346 }
1347 to->w_jumplistlen = from->w_jumplistlen;
1348 to->w_jumplistidx = from->w_jumplistidx;
1349}
1350
1351/*
1352 * Free items in the jumplist of window "wp".
1353 */
1354 void
1355free_jumplist(wp)
1356 win_T *wp;
1357{
1358 int i;
1359
1360 for (i = 0; i < wp->w_jumplistlen; ++i)
1361 vim_free(wp->w_jumplist[i].fname);
1362}
1363# endif
1364#endif /* FEAT_JUMPLIST */
1365
1366 void
1367set_last_cursor(win)
1368 win_T *win;
1369{
1370 win->w_buffer->b_last_cursor = win->w_cursor;
1371}
1372
Bram Moolenaarea408852005-06-25 22:49:46 +00001373#if defined(EXITFREE) || defined(PROTO)
1374 void
1375free_all_marks()
1376{
1377 int i;
1378
1379 for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1380 if (namedfm[i].fmark.mark.lnum != 0)
1381 vim_free(namedfm[i].fname);
1382}
1383#endif
1384
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385#if defined(FEAT_VIMINFO) || defined(PROTO)
1386 int
1387read_viminfo_filemark(virp, force)
1388 vir_T *virp;
1389 int force;
1390{
1391 char_u *str;
1392 xfmark_T *fm;
1393 int i;
1394
1395 /* We only get here if line[0] == '\'' or '-'.
1396 * Illegal mark names are ignored (for future expansion). */
1397 str = virp->vir_line + 1;
1398 if (
1399#ifndef EBCDIC
1400 *str <= 127 &&
1401#endif
1402 ((*virp->vir_line == '\'' && (VIM_ISDIGIT(*str) || isupper(*str)))
1403 || (*virp->vir_line == '-' && *str == '\'')))
1404 {
1405 if (*str == '\'')
1406 {
1407#ifdef FEAT_JUMPLIST
1408 /* If the jumplist isn't full insert fmark as oldest entry */
1409 if (curwin->w_jumplistlen == JUMPLISTSIZE)
1410 fm = NULL;
1411 else
1412 {
1413 for (i = curwin->w_jumplistlen; i > 0; --i)
1414 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
1415 ++curwin->w_jumplistidx;
1416 ++curwin->w_jumplistlen;
1417 fm = &curwin->w_jumplist[0];
1418 fm->fmark.mark.lnum = 0;
1419 fm->fname = NULL;
1420 }
1421#else
1422 fm = NULL;
1423#endif
1424 }
1425 else if (VIM_ISDIGIT(*str))
1426 fm = &namedfm[*str - '0' + NMARKS];
1427 else
1428 fm = &namedfm[*str - 'A'];
1429 if (fm != NULL && (fm->fmark.mark.lnum == 0 || force))
1430 {
1431 str = skipwhite(str + 1);
1432 fm->fmark.mark.lnum = getdigits(&str);
1433 str = skipwhite(str);
1434 fm->fmark.mark.col = getdigits(&str);
1435#ifdef FEAT_VIRTUALEDIT
1436 fm->fmark.mark.coladd = 0;
1437#endif
1438 fm->fmark.fnum = 0;
1439 str = skipwhite(str);
1440 vim_free(fm->fname);
1441 fm->fname = viminfo_readstring(virp, (int)(str - virp->vir_line),
1442 FALSE);
1443 }
1444 }
1445 return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
1446}
1447
1448 void
1449write_viminfo_filemarks(fp)
1450 FILE *fp;
1451{
1452 int i;
1453 char_u *name;
1454 buf_T *buf;
1455 xfmark_T *fm;
1456
1457 if (get_viminfo_parameter('f') == 0)
1458 return;
1459
Bram Moolenaar2f1e0502010-08-13 11:18:02 +02001460 fputs(_("\n# File marks:\n"), fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461
1462 /*
1463 * Find a mark that is the same file and position as the cursor.
1464 * That one, or else the last one is deleted.
1465 * Move '0 to '1, '1 to '2, etc. until the matching one or '9
1466 * Set '0 mark to current cursor position.
1467 */
1468 if (curbuf->b_ffname != NULL && !removable(curbuf->b_ffname))
1469 {
1470 name = buflist_nr2name(curbuf->b_fnum, TRUE, FALSE);
1471 for (i = NMARKS; i < NMARKS + EXTRA_MARKS - 1; ++i)
1472 if (namedfm[i].fmark.mark.lnum == curwin->w_cursor.lnum
1473 && (namedfm[i].fname == NULL
1474 ? namedfm[i].fmark.fnum == curbuf->b_fnum
1475 : (name != NULL
1476 && STRCMP(name, namedfm[i].fname) == 0)))
1477 break;
1478 vim_free(name);
1479
1480 vim_free(namedfm[i].fname);
1481 for ( ; i > NMARKS; --i)
1482 namedfm[i] = namedfm[i - 1];
1483 namedfm[NMARKS].fmark.mark = curwin->w_cursor;
1484 namedfm[NMARKS].fmark.fnum = curbuf->b_fnum;
1485 namedfm[NMARKS].fname = NULL;
1486 }
1487
1488 /* Write the filemarks '0 - '9 and 'A - 'Z */
1489 for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1490 write_one_filemark(fp, &namedfm[i], '\'',
1491 i < NMARKS ? i + 'A' : i - NMARKS + '0');
1492
1493#ifdef FEAT_JUMPLIST
1494 /* Write the jumplist with -' */
Bram Moolenaar2f1e0502010-08-13 11:18:02 +02001495 fputs(_("\n# Jumplist (newest first):\n"), fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 setpcmark(); /* add current cursor position */
1497 cleanup_jumplist();
1498 for (fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
1499 fm >= &curwin->w_jumplist[0]; --fm)
1500 {
1501 if (fm->fmark.fnum == 0
1502 || ((buf = buflist_findnr(fm->fmark.fnum)) != NULL
1503 && !removable(buf->b_ffname)))
1504 write_one_filemark(fp, fm, '-', '\'');
1505 }
1506#endif
1507}
1508
1509 static void
1510write_one_filemark(fp, fm, c1, c2)
1511 FILE *fp;
1512 xfmark_T *fm;
1513 int c1;
1514 int c2;
1515{
1516 char_u *name;
1517
1518 if (fm->fmark.mark.lnum == 0) /* not set */
1519 return;
1520
1521 if (fm->fmark.fnum != 0) /* there is a buffer */
1522 name = buflist_nr2name(fm->fmark.fnum, TRUE, FALSE);
1523 else
1524 name = fm->fname; /* use name from .viminfo */
1525 if (name != NULL && *name != NUL)
1526 {
1527 fprintf(fp, "%c%c %ld %ld ", c1, c2, (long)fm->fmark.mark.lnum,
1528 (long)fm->fmark.mark.col);
1529 viminfo_writestring(fp, name);
1530 }
1531
1532 if (fm->fmark.fnum != 0)
1533 vim_free(name);
1534}
1535
1536/*
1537 * Return TRUE if "name" is on removable media (depending on 'viminfo').
1538 */
1539 int
1540removable(name)
1541 char_u *name;
1542{
1543 char_u *p;
1544 char_u part[51];
1545 int retval = FALSE;
Bram Moolenaarcfc6c432005-06-06 21:50:35 +00001546 size_t n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001547
1548 name = home_replace_save(NULL, name);
1549 if (name != NULL)
1550 {
1551 for (p = p_viminfo; *p; )
1552 {
1553 copy_option_part(&p, part, 51, ", ");
Bram Moolenaarcfc6c432005-06-06 21:50:35 +00001554 if (part[0] == 'r')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001555 {
Bram Moolenaarcfc6c432005-06-06 21:50:35 +00001556 n = STRLEN(part + 1);
1557 if (MB_STRNICMP(part + 1, name, n) == 0)
1558 {
1559 retval = TRUE;
1560 break;
1561 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001562 }
1563 }
1564 vim_free(name);
1565 }
1566 return retval;
1567}
1568
1569static void write_one_mark __ARGS((FILE *fp_out, int c, pos_T *pos));
1570
1571/*
1572 * Write all the named marks for all buffers.
1573 * Return the number of buffers for which marks have been written.
1574 */
1575 int
1576write_viminfo_marks(fp_out)
1577 FILE *fp_out;
1578{
1579 int count;
1580 buf_T *buf;
1581 int is_mark_set;
1582 int i;
1583#ifdef FEAT_WINDOWS
1584 win_T *win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00001585 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586
1587 /*
1588 * Set b_last_cursor for the all buffers that have a window.
1589 */
Bram Moolenaarf740b292006-02-16 22:11:02 +00001590 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 set_last_cursor(win);
1592#else
1593 set_last_cursor(curwin);
1594#endif
1595
Bram Moolenaar2f1e0502010-08-13 11:18:02 +02001596 fputs(_("\n# History of marks within files (newest to oldest):\n"), fp_out);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001597 count = 0;
1598 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1599 {
1600 /*
1601 * Only write something if buffer has been loaded and at least one
1602 * mark is set.
1603 */
1604 if (buf->b_marks_read)
1605 {
1606 if (buf->b_last_cursor.lnum != 0)
1607 is_mark_set = TRUE;
1608 else
1609 {
1610 is_mark_set = FALSE;
1611 for (i = 0; i < NMARKS; i++)
1612 if (buf->b_namedm[i].lnum != 0)
1613 {
1614 is_mark_set = TRUE;
1615 break;
1616 }
1617 }
1618 if (is_mark_set && buf->b_ffname != NULL
1619 && buf->b_ffname[0] != NUL && !removable(buf->b_ffname))
1620 {
1621 home_replace(NULL, buf->b_ffname, IObuff, IOSIZE, TRUE);
1622 fprintf(fp_out, "\n> ");
1623 viminfo_writestring(fp_out, IObuff);
1624 write_one_mark(fp_out, '"', &buf->b_last_cursor);
1625 write_one_mark(fp_out, '^', &buf->b_last_insert);
1626 write_one_mark(fp_out, '.', &buf->b_last_change);
1627#ifdef FEAT_JUMPLIST
1628 /* changelist positions are stored oldest first */
1629 for (i = 0; i < buf->b_changelistlen; ++i)
1630 write_one_mark(fp_out, '+', &buf->b_changelist[i]);
1631#endif
1632 for (i = 0; i < NMARKS; i++)
1633 write_one_mark(fp_out, 'a' + i, &buf->b_namedm[i]);
1634 count++;
1635 }
1636 }
1637 }
1638
1639 return count;
1640}
1641
1642 static void
1643write_one_mark(fp_out, c, pos)
1644 FILE *fp_out;
1645 int c;
1646 pos_T *pos;
1647{
1648 if (pos->lnum != 0)
1649 fprintf(fp_out, "\t%c\t%ld\t%d\n", c, (long)pos->lnum, (int)pos->col);
1650}
1651
1652/*
1653 * Handle marks in the viminfo file:
Bram Moolenaard812df62008-11-09 12:46:09 +00001654 * fp_out != NULL: copy marks for buffers not in buffer list
1655 * fp_out == NULL && (flags & VIF_WANT_MARKS): read marks for curbuf only
1656 * fp_out == NULL && (flags & VIF_GET_OLDFILES | VIF_FORCEIT): fill v:oldfiles
Bram Moolenaar071d4272004-06-13 20:20:40 +00001657 */
1658 void
Bram Moolenaard812df62008-11-09 12:46:09 +00001659copy_viminfo_marks(virp, fp_out, count, eof, flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001660 vir_T *virp;
1661 FILE *fp_out;
1662 int count;
1663 int eof;
Bram Moolenaard812df62008-11-09 12:46:09 +00001664 int flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001665{
1666 char_u *line = virp->vir_line;
1667 buf_T *buf;
1668 int num_marked_files;
1669 int load_marks;
1670 int copy_marks_out;
1671 char_u *str;
1672 int i;
1673 char_u *p;
1674 char_u *name_buf;
1675 pos_T pos;
Bram Moolenaard812df62008-11-09 12:46:09 +00001676#ifdef FEAT_EVAL
1677 list_T *list = NULL;
1678#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001679
1680 if ((name_buf = alloc(LSIZE)) == NULL)
1681 return;
1682 *name_buf = NUL;
Bram Moolenaard812df62008-11-09 12:46:09 +00001683
1684#ifdef FEAT_EVAL
1685 if (fp_out == NULL && (flags & (VIF_GET_OLDFILES | VIF_FORCEIT)))
1686 {
1687 list = list_alloc();
1688 if (list != NULL)
1689 set_vim_var_list(VV_OLDFILES, list);
1690 }
1691#endif
1692
Bram Moolenaar071d4272004-06-13 20:20:40 +00001693 num_marked_files = get_viminfo_parameter('\'');
1694 while (!eof && (count < num_marked_files || fp_out == NULL))
1695 {
1696 if (line[0] != '>')
1697 {
1698 if (line[0] != '\n' && line[0] != '\r' && line[0] != '#')
1699 {
1700 if (viminfo_error("E576: ", _("Missing '>'"), line))
1701 break; /* too many errors, return now */
1702 }
1703 eof = vim_fgets(line, LSIZE, virp->vir_fd);
1704 continue; /* Skip this dud line */
1705 }
1706
1707 /*
1708 * Handle long line and translate escaped characters.
1709 * Find file name, set str to start.
1710 * Ignore leading and trailing white space.
1711 */
1712 str = skipwhite(line + 1);
1713 str = viminfo_readstring(virp, (int)(str - virp->vir_line), FALSE);
1714 if (str == NULL)
1715 continue;
1716 p = str + STRLEN(str);
1717 while (p != str && (*p == NUL || vim_isspace(*p)))
1718 p--;
1719 if (*p)
1720 p++;
1721 *p = NUL;
1722
Bram Moolenaard812df62008-11-09 12:46:09 +00001723#ifdef FEAT_EVAL
1724 if (list != NULL)
1725 list_append_string(list, str, -1);
1726#endif
1727
Bram Moolenaar071d4272004-06-13 20:20:40 +00001728 /*
1729 * If fp_out == NULL, load marks for current buffer.
1730 * If fp_out != NULL, copy marks for buffers not in buflist.
1731 */
1732 load_marks = copy_marks_out = FALSE;
1733 if (fp_out == NULL)
1734 {
Bram Moolenaard812df62008-11-09 12:46:09 +00001735 if ((flags & VIF_WANT_MARKS) && curbuf->b_ffname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736 {
1737 if (*name_buf == NUL) /* only need to do this once */
1738 home_replace(NULL, curbuf->b_ffname, name_buf, LSIZE, TRUE);
1739 if (fnamecmp(str, name_buf) == 0)
1740 load_marks = TRUE;
1741 }
1742 }
1743 else /* fp_out != NULL */
1744 {
1745 /* This is slow if there are many buffers!! */
1746 for (buf = firstbuf; buf != NULL; buf = buf->b_next)
1747 if (buf->b_ffname != NULL)
1748 {
1749 home_replace(NULL, buf->b_ffname, name_buf, LSIZE, TRUE);
1750 if (fnamecmp(str, name_buf) == 0)
1751 break;
1752 }
1753
1754 /*
1755 * copy marks if the buffer has not been loaded
1756 */
1757 if (buf == NULL || !buf->b_marks_read)
1758 {
1759 copy_marks_out = TRUE;
1760 fputs("\n> ", fp_out);
1761 viminfo_writestring(fp_out, str);
1762 count++;
1763 }
1764 }
1765 vim_free(str);
1766
1767#ifdef FEAT_VIRTUALEDIT
1768 pos.coladd = 0;
1769#endif
1770 while (!(eof = viminfo_readline(virp)) && line[0] == TAB)
1771 {
1772 if (load_marks)
1773 {
1774 if (line[1] != NUL)
1775 {
Bram Moolenaare698add2011-02-25 15:11:22 +01001776 unsigned u;
1777
1778 sscanf((char *)line + 2, "%ld %u", &pos.lnum, &u);
1779 pos.col = u;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001780 switch (line[1])
1781 {
1782 case '"': curbuf->b_last_cursor = pos; break;
1783 case '^': curbuf->b_last_insert = pos; break;
1784 case '.': curbuf->b_last_change = pos; break;
1785 case '+':
1786#ifdef FEAT_JUMPLIST
1787 /* changelist positions are stored oldest
1788 * first */
1789 if (curbuf->b_changelistlen == JUMPLISTSIZE)
1790 /* list is full, remove oldest entry */
1791 mch_memmove(curbuf->b_changelist,
1792 curbuf->b_changelist + 1,
1793 sizeof(pos_T) * (JUMPLISTSIZE - 1));
1794 else
1795 ++curbuf->b_changelistlen;
1796 curbuf->b_changelist[
1797 curbuf->b_changelistlen - 1] = pos;
1798#endif
1799 break;
1800 default: if ((i = line[1] - 'a') >= 0 && i < NMARKS)
1801 curbuf->b_namedm[i] = pos;
1802 }
1803 }
1804 }
1805 else if (copy_marks_out)
1806 fputs((char *)line, fp_out);
1807 }
1808 if (load_marks)
1809 {
1810#ifdef FEAT_JUMPLIST
1811 win_T *wp;
1812
1813 FOR_ALL_WINDOWS(wp)
1814 {
1815 if (wp->w_buffer == curbuf)
1816 wp->w_changelistidx = curbuf->b_changelistlen;
1817 }
1818#endif
1819 break;
1820 }
1821 }
1822 vim_free(name_buf);
1823}
1824#endif /* FEAT_VIMINFO */