blob: 342bfcd9f7ae874d81af460ff5ba0726b87aeb8a [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * mark.c: functions for setting marks and jumping to them
12 */
13
14#include "vim.h"
15
16/*
17 * This file contains routines to maintain and manipulate marks.
18 */
19
20/*
21 * If a named file mark's lnum is non-zero, it is valid.
22 * If a named file mark's fnum is non-zero, it is for an existing buffer,
23 * otherwise it is from .viminfo and namedfm[n].fname is the file name.
24 * There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing
25 * viminfo).
26 */
27#define EXTRA_MARKS 10 /* marks 0-9 */
28static xfmark_T namedfm[NMARKS + EXTRA_MARKS]; /* marks with file nr */
29
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010030static void fname2fnum(xfmark_T *fm);
31static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf);
32static char_u *mark_line(pos_T *mp, int lead_len);
33static void show_one_mark(int, char_u *, pos_T *, char_u *, int current);
Bram Moolenaar071d4272004-06-13 20:20:40 +000034#ifdef FEAT_JUMPLIST
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010035static void cleanup_jumplist(void);
Bram Moolenaar071d4272004-06-13 20:20:40 +000036#endif
37#ifdef FEAT_VIMINFO
Bram Moolenaar92b8b2d2016-01-29 22:36:45 +010038static void write_one_filemark(FILE *fp, xfmark_T *fm, int c1, int c2);
Bram Moolenaar071d4272004-06-13 20:20:40 +000039#endif
Bram Moolenaar88d298a2017-03-14 21:53:58 +010040static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount,
41 long amount_after, int adjust_folds);
Bram Moolenaar071d4272004-06-13 20:20:40 +000042
43/*
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000044 * Set named mark "c" at current cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 * Returns OK on success, FAIL if bad name given.
46 */
47 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +010048setmark(int c)
Bram Moolenaar071d4272004-06-13 20:20:40 +000049{
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000050 return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum);
51}
52
53/*
54 * Set named mark "c" to position "pos".
55 * When "c" is upper case use file "fnum".
56 * Returns OK on success, FAIL if bad name given.
57 */
58 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +010059setmark_pos(int c, pos_T *pos, int fnum)
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000060{
Bram Moolenaar071d4272004-06-13 20:20:40 +000061 int i;
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010062 buf_T *buf;
Bram Moolenaar071d4272004-06-13 20:20:40 +000063
64 /* Check for a special key (may cause islower() to crash). */
65 if (c < 0)
66 return FAIL;
67
68 if (c == '\'' || c == '`')
69 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +000070 if (pos == &curwin->w_cursor)
71 {
72 setpcmark();
73 /* keep it even when the cursor doesn't move */
74 curwin->w_prev_pcmark = curwin->w_pcmark;
75 }
76 else
77 curwin->w_pcmark = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000078 return OK;
79 }
80
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010081 buf = buflist_findnr(fnum);
82 if (buf == NULL)
83 return FAIL;
84
Bram Moolenaar08250432008-02-13 11:42:46 +000085 if (c == '"')
86 {
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010087 buf->b_last_cursor = *pos;
Bram Moolenaar08250432008-02-13 11:42:46 +000088 return OK;
89 }
90
Bram Moolenaar071d4272004-06-13 20:20:40 +000091 /* Allow setting '[ and '] for an autocommand that simulates reading a
92 * file. */
93 if (c == '[')
94 {
Bram Moolenaarf13e00b2017-01-28 18:23:54 +010095 buf->b_op_start = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +000096 return OK;
97 }
98 if (c == ']')
99 {
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100100 buf->b_op_end = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000101 return OK;
102 }
103
Bram Moolenaarbc88a272013-08-02 17:22:23 +0200104 if (c == '<' || c == '>')
Bram Moolenaar0306ac32012-07-06 17:51:28 +0200105 {
Bram Moolenaarbc88a272013-08-02 17:22:23 +0200106 if (c == '<')
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100107 buf->b_visual.vi_start = *pos;
Bram Moolenaarbc88a272013-08-02 17:22:23 +0200108 else
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100109 buf->b_visual.vi_end = *pos;
110 if (buf->b_visual.vi_mode == NUL)
Bram Moolenaarbc88a272013-08-02 17:22:23 +0200111 /* Visual_mode has not yet been set, use a sane default. */
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100112 buf->b_visual.vi_mode = 'v';
Bram Moolenaar0306ac32012-07-06 17:51:28 +0200113 return OK;
114 }
Bram Moolenaar0306ac32012-07-06 17:51:28 +0200115
Bram Moolenaar2d358992016-06-12 21:20:54 +0200116 if (ASCII_ISLOWER(c))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000117 {
118 i = c - 'a';
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100119 buf->b_namedm[i] = *pos;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000120 return OK;
121 }
Bram Moolenaar2d358992016-06-12 21:20:54 +0200122 if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000123 {
Bram Moolenaar2d358992016-06-12 21:20:54 +0200124 if (VIM_ISDIGIT(c))
125 i = c - '0' + NMARKS;
126 else
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;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200132#ifdef FEAT_VIMINFO
133 namedfm[i].time_set = vim_time();
134#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000135 return OK;
136 }
137 return FAIL;
138}
139
140/*
141 * Set the previous context mark to the current position and add it to the
142 * jump list.
143 */
144 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100145setpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000146{
147#ifdef FEAT_JUMPLIST
148 int i;
149 xfmark_T *fm;
150#endif
151#ifdef JUMPLIST_ROTATE
152 xfmark_T tempmark;
153#endif
154
155 /* for :global the mark is set only once */
156 if (global_busy || listcmd_busy || cmdmod.keepjumps)
157 return;
158
159 curwin->w_prev_pcmark = curwin->w_pcmark;
160 curwin->w_pcmark = curwin->w_cursor;
161
162#ifdef FEAT_JUMPLIST
163# ifdef JUMPLIST_ROTATE
164 /*
165 * If last used entry is not at the top, put it at the top by rotating
166 * the stack until it is (the newer entries will be at the bottom).
167 * Keep one entry (the last used one) at the top.
168 */
169 if (curwin->w_jumplistidx < curwin->w_jumplistlen)
170 ++curwin->w_jumplistidx;
171 while (curwin->w_jumplistidx < curwin->w_jumplistlen)
172 {
173 tempmark = curwin->w_jumplist[curwin->w_jumplistlen - 1];
174 for (i = curwin->w_jumplistlen - 1; i > 0; --i)
175 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
176 curwin->w_jumplist[0] = tempmark;
177 ++curwin->w_jumplistidx;
178 }
179# endif
180
181 /* If jumplist is full: remove oldest entry */
182 if (++curwin->w_jumplistlen > JUMPLISTSIZE)
183 {
184 curwin->w_jumplistlen = JUMPLISTSIZE;
185 vim_free(curwin->w_jumplist[0].fname);
186 for (i = 1; i < JUMPLISTSIZE; ++i)
187 curwin->w_jumplist[i - 1] = curwin->w_jumplist[i];
188 }
189 curwin->w_jumplistidx = curwin->w_jumplistlen;
190 fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1];
191
192 fm->fmark.mark = curwin->w_pcmark;
193 fm->fmark.fnum = curbuf->b_fnum;
194 fm->fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200195# ifdef FEAT_VIMINFO
196 fm->time_set = vim_time();
197# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000198#endif
199}
200
201/*
202 * To change context, call setpcmark(), then move the current position to
203 * where ever, then call checkpcmark(). This ensures that the previous
204 * context will only be changed if the cursor moved to a different line.
205 * If pcmark was deleted (with "dG") the previous mark is restored.
206 */
207 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100208checkpcmark(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000209{
210 if (curwin->w_prev_pcmark.lnum != 0
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100211 && (EQUAL_POS(curwin->w_pcmark, curwin->w_cursor)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212 || curwin->w_pcmark.lnum == 0))
213 {
214 curwin->w_pcmark = curwin->w_prev_pcmark;
215 curwin->w_prev_pcmark.lnum = 0; /* Show it has been checked */
216 }
217}
218
219#if defined(FEAT_JUMPLIST) || defined(PROTO)
220/*
221 * move "count" positions in the jump list (count may be negative)
222 */
223 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100224movemark(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000225{
226 pos_T *pos;
227 xfmark_T *jmp;
228
229 cleanup_jumplist();
230
231 if (curwin->w_jumplistlen == 0) /* nothing to jump to */
232 return (pos_T *)NULL;
233
234 for (;;)
235 {
236 if (curwin->w_jumplistidx + count < 0
237 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen)
238 return (pos_T *)NULL;
239
240 /*
241 * if first CTRL-O or CTRL-I command after a jump, add cursor position
Bram Moolenaarf711faf2007-05-10 16:48:19 +0000242 * to list. Careful: If there are duplicates (CTRL-O immediately after
Bram Moolenaar071d4272004-06-13 20:20:40 +0000243 * starting Vim on a file), another entry may have been removed.
244 */
245 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
246 {
247 setpcmark();
248 --curwin->w_jumplistidx; /* skip the new entry */
249 if (curwin->w_jumplistidx + count < 0)
250 return (pos_T *)NULL;
251 }
252
253 curwin->w_jumplistidx += count;
254
255 jmp = curwin->w_jumplist + curwin->w_jumplistidx;
256 if (jmp->fmark.fnum == 0)
257 fname2fnum(jmp);
258 if (jmp->fmark.fnum != curbuf->b_fnum)
259 {
260 /* jump to other file */
261 if (buflist_findnr(jmp->fmark.fnum) == NULL)
262 { /* Skip this one .. */
263 count += count < 0 ? -1 : 1;
264 continue;
265 }
266 if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum,
267 0, FALSE) == FAIL)
268 return (pos_T *)NULL;
269 /* Set lnum again, autocommands my have changed it */
270 curwin->w_cursor = jmp->fmark.mark;
271 pos = (pos_T *)-1;
272 }
273 else
274 pos = &(jmp->fmark.mark);
275 return pos;
276 }
277}
278
279/*
280 * Move "count" positions in the changelist (count may be negative).
281 */
282 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100283movechangelist(int count)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000284{
285 int n;
286
287 if (curbuf->b_changelistlen == 0) /* nothing to jump to */
288 return (pos_T *)NULL;
289
290 n = curwin->w_changelistidx;
291 if (n + count < 0)
292 {
293 if (n == 0)
294 return (pos_T *)NULL;
295 n = 0;
296 }
297 else if (n + count >= curbuf->b_changelistlen)
298 {
299 if (n == curbuf->b_changelistlen - 1)
300 return (pos_T *)NULL;
301 n = curbuf->b_changelistlen - 1;
302 }
303 else
304 n += count;
305 curwin->w_changelistidx = n;
306 return curbuf->b_changelist + n;
307}
308#endif
309
310/*
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100311 * Find mark "c" in buffer pointed to by "buf".
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000312 * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc.
313 * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit
314 * another file.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000315 * Returns:
316 * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is
317 * in another file which can't be gotten. (caller needs to check lnum!)
318 * - NULL if there is no mark called 'c'.
319 * - -1 if mark is in other file and jumped there (only if changefile is TRUE)
320 */
321 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100322getmark_buf(buf_T *buf, int c, int changefile)
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100323{
324 return getmark_buf_fnum(buf, c, changefile, NULL);
325}
326
327 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100328getmark(int c, int changefile)
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000329{
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100330 return getmark_buf_fnum(curbuf, c, changefile, NULL);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000331}
332
333 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100334getmark_buf_fnum(
335 buf_T *buf,
336 int c,
337 int changefile,
338 int *fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000339{
340 pos_T *posp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000341 pos_T *startp, *endp;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000342 static pos_T pos_copy;
343
344 posp = NULL;
345
346 /* Check for special key, can't be a mark name and might cause islower()
347 * to crash. */
348 if (c < 0)
349 return posp;
350#ifndef EBCDIC
351 if (c > '~') /* check for islower()/isupper() */
352 ;
353 else
354#endif
355 if (c == '\'' || c == '`') /* previous context mark */
356 {
357 pos_copy = curwin->w_pcmark; /* need to make a copy because */
358 posp = &pos_copy; /* w_pcmark may be changed soon */
359 }
360 else if (c == '"') /* to pos when leaving buffer */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100361 posp = &(buf->b_last_cursor);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000362 else if (c == '^') /* to where Insert mode stopped */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100363 posp = &(buf->b_last_insert);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000364 else if (c == '.') /* to where last change was made */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100365 posp = &(buf->b_last_change);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000366 else if (c == '[') /* to start of previous operator */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100367 posp = &(buf->b_op_start);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000368 else if (c == ']') /* to end of previous operator */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100369 posp = &(buf->b_op_end);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000370 else if (c == '{' || c == '}') /* to previous/next paragraph */
371 {
372 pos_T pos;
373 oparg_T oa;
374 int slcb = listcmd_busy;
375
376 pos = curwin->w_cursor;
377 listcmd_busy = TRUE; /* avoid that '' is changed */
Bram Moolenaar8b96d642005-09-05 22:05:30 +0000378 if (findpar(&oa.inclusive,
379 c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380 {
381 pos_copy = curwin->w_cursor;
382 posp = &pos_copy;
383 }
384 curwin->w_cursor = pos;
385 listcmd_busy = slcb;
386 }
387 else if (c == '(' || c == ')') /* to previous/next sentence */
388 {
389 pos_T pos;
390 int slcb = listcmd_busy;
391
392 pos = curwin->w_cursor;
393 listcmd_busy = TRUE; /* avoid that '' is changed */
394 if (findsent(c == ')' ? FORWARD : BACKWARD, 1L))
395 {
396 pos_copy = curwin->w_cursor;
397 posp = &pos_copy;
398 }
399 curwin->w_cursor = pos;
400 listcmd_busy = slcb;
401 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000402 else if (c == '<' || c == '>') /* start/end of visual area */
403 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100404 startp = &buf->b_visual.vi_start;
405 endp = &buf->b_visual.vi_end;
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100406 if (((c == '<') == LT_POS(*startp, *endp) || endp->lnum == 0)
Bram Moolenaarf13e00b2017-01-28 18:23:54 +0100407 && startp->lnum != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000408 posp = startp;
409 else
410 posp = endp;
411 /*
412 * For Visual line mode, set mark at begin or end of line
413 */
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100414 if (buf->b_visual.vi_mode == 'V')
Bram Moolenaar071d4272004-06-13 20:20:40 +0000415 {
416 pos_copy = *posp;
417 posp = &pos_copy;
418 if (c == '<')
419 pos_copy.col = 0;
420 else
421 pos_copy.col = MAXCOL;
422#ifdef FEAT_VIRTUALEDIT
423 pos_copy.coladd = 0;
424#endif
425 }
426 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000427 else if (ASCII_ISLOWER(c)) /* normal named mark */
428 {
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100429 posp = &(buf->b_namedm[c - 'a']);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430 }
431 else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) /* named file mark */
432 {
433 if (VIM_ISDIGIT(c))
434 c = c - '0' + NMARKS;
435 else
436 c -= 'A';
437 posp = &(namedfm[c].fmark.mark);
438
439 if (namedfm[c].fmark.fnum == 0)
440 fname2fnum(&namedfm[c]);
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000441
442 if (fnum != NULL)
443 *fnum = namedfm[c].fmark.fnum;
Bram Moolenaar9d182dd2013-01-23 15:53:15 +0100444 else if (namedfm[c].fmark.fnum != buf->b_fnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000445 {
Bram Moolenaarbfb2d402006-03-03 22:50:42 +0000446 /* mark is in another file */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000447 posp = &pos_copy;
448
Bram Moolenaar071d4272004-06-13 20:20:40 +0000449 if (namedfm[c].fmark.mark.lnum != 0
450 && changefile && namedfm[c].fmark.fnum)
451 {
452 if (buflist_getfile(namedfm[c].fmark.fnum,
453 (linenr_T)1, GETF_SETMARK, FALSE) == OK)
454 {
455 /* Set the lnum now, autocommands could have changed it */
456 curwin->w_cursor = namedfm[c].fmark.mark;
457 return (pos_T *)-1;
458 }
459 pos_copy.lnum = -1; /* can't get file */
460 }
461 else
462 pos_copy.lnum = 0; /* mark exists, but is not valid in
463 current buffer */
464 }
465 }
466
467 return posp;
468}
469
470/*
471 * Search for the next named mark in the current file.
472 *
473 * Returns pointer to pos_T of the next mark or NULL if no mark is found.
474 */
475 pos_T *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100476getnextmark(
477 pos_T *startpos, /* where to start */
478 int dir, /* direction for search */
479 int begin_line)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000480{
481 int i;
482 pos_T *result = NULL;
483 pos_T pos;
484
485 pos = *startpos;
486
487 /* When searching backward and leaving the cursor on the first non-blank,
488 * position must be in a previous line.
489 * When searching forward and leaving the cursor on the first non-blank,
490 * position must be in a next line. */
491 if (dir == BACKWARD && begin_line)
492 pos.col = 0;
493 else if (dir == FORWARD && begin_line)
494 pos.col = MAXCOL;
495
496 for (i = 0; i < NMARKS; i++)
497 {
498 if (curbuf->b_namedm[i].lnum > 0)
499 {
500 if (dir == FORWARD)
501 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100502 if ((result == NULL || LT_POS(curbuf->b_namedm[i], *result))
503 && LT_POS(pos, curbuf->b_namedm[i]))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000504 result = &curbuf->b_namedm[i];
505 }
506 else
507 {
Bram Moolenaarb5aedf32017-03-12 18:23:53 +0100508 if ((result == NULL || LT_POS(*result, curbuf->b_namedm[i]))
509 && LT_POS(curbuf->b_namedm[i], pos))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510 result = &curbuf->b_namedm[i];
511 }
512 }
513 }
514
515 return result;
516}
517
518/*
519 * For an xtended filemark: set the fnum from the fname.
520 * This is used for marks obtained from the .viminfo file. It's postponed
521 * until the mark is used to avoid a long startup delay.
522 */
523 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100524fname2fnum(xfmark_T *fm)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000525{
526 char_u *p;
527
528 if (fm->fname != NULL)
529 {
530 /*
531 * First expand "~/" in the file name to the home directory.
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000532 * Don't expand the whole name, it may contain other '~' chars.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000533 */
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000534 if (fm->fname[0] == '~' && (fm->fname[1] == '/'
535#ifdef BACKSLASH_IN_FILENAME
536 || fm->fname[1] == '\\'
537#endif
538 ))
539 {
540 int len;
541
542 expand_env((char_u *)"~/", NameBuff, MAXPATHL);
Bram Moolenaarcb4cef22008-03-16 15:04:34 +0000543 len = (int)STRLEN(NameBuff);
Bram Moolenaar525ad4d2008-01-03 19:22:13 +0000544 vim_strncpy(NameBuff + len, fm->fname + 2, MAXPATHL - len - 1);
545 }
546 else
547 vim_strncpy(NameBuff, fm->fname, MAXPATHL - 1);
548
549 /* Try to shorten the file name. */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000550 mch_dirname(IObuff, IOSIZE);
551 p = shorten_fname(NameBuff, IObuff);
552
553 /* buflist_new() will call fmarks_check_names() */
554 (void)buflist_new(NameBuff, p, (linenr_T)1, 0);
555 }
556}
557
558/*
559 * Check all file marks for a name that matches the file name in buf.
560 * May replace the name with an fnum.
561 * Used for marks that come from the .viminfo file.
562 */
563 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100564fmarks_check_names(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000565{
566 char_u *name;
567 int i;
568#ifdef FEAT_JUMPLIST
569 win_T *wp;
570#endif
571
572 if (buf->b_ffname == NULL)
573 return;
574
575 name = home_replace_save(buf, buf->b_ffname);
576 if (name == NULL)
577 return;
578
579 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
580 fmarks_check_one(&namedfm[i], name, buf);
581
582#ifdef FEAT_JUMPLIST
583 FOR_ALL_WINDOWS(wp)
584 {
585 for (i = 0; i < wp->w_jumplistlen; ++i)
586 fmarks_check_one(&wp->w_jumplist[i], name, buf);
587 }
588#endif
589
590 vim_free(name);
591}
592
593 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100594fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000595{
596 if (fm->fmark.fnum == 0
597 && fm->fname != NULL
598 && fnamecmp(name, fm->fname) == 0)
599 {
600 fm->fmark.fnum = buf->b_fnum;
601 vim_free(fm->fname);
602 fm->fname = NULL;
603 }
604}
605
606/*
607 * Check a if a position from a mark is valid.
608 * Give and error message and return FAIL if not.
609 */
610 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100611check_mark(pos_T *pos)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000612{
613 if (pos == NULL)
614 {
615 EMSG(_(e_umark));
616 return FAIL;
617 }
618 if (pos->lnum <= 0)
619 {
620 /* lnum is negative if mark is in another file can can't get that
621 * file, error message already give then. */
622 if (pos->lnum == 0)
623 EMSG(_(e_marknotset));
624 return FAIL;
625 }
626 if (pos->lnum > curbuf->b_ml.ml_line_count)
627 {
628 EMSG(_(e_markinval));
629 return FAIL;
630 }
631 return OK;
632}
633
634/*
635 * clrallmarks() - clear all marks in the buffer 'buf'
636 *
637 * Used mainly when trashing the entire buffer during ":e" type commands
638 */
639 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100640clrallmarks(buf_T *buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000641{
642 static int i = -1;
643
644 if (i == -1) /* first call ever: initialize */
645 for (i = 0; i < NMARKS + 1; i++)
646 {
647 namedfm[i].fmark.mark.lnum = 0;
648 namedfm[i].fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200649#ifdef FEAT_VIMINFO
650 namedfm[i].time_set = 0;
651#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000652 }
653
654 for (i = 0; i < NMARKS; i++)
655 buf->b_namedm[i].lnum = 0;
656 buf->b_op_start.lnum = 0; /* start/end op mark cleared */
657 buf->b_op_end.lnum = 0;
658 buf->b_last_cursor.lnum = 1; /* '" mark cleared */
659 buf->b_last_cursor.col = 0;
660#ifdef FEAT_VIRTUALEDIT
661 buf->b_last_cursor.coladd = 0;
662#endif
663 buf->b_last_insert.lnum = 0; /* '^ mark cleared */
664 buf->b_last_change.lnum = 0; /* '. mark cleared */
665#ifdef FEAT_JUMPLIST
666 buf->b_changelistlen = 0;
667#endif
668}
669
670/*
671 * Get name of file from a filemark.
672 * When it's in the current buffer, return the text at the mark.
673 * Returns an allocated string.
674 */
675 char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100676fm_getname(fmark_T *fmark, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000677{
678 if (fmark->fnum == curbuf->b_fnum) /* current buffer */
679 return mark_line(&(fmark->mark), lead_len);
680 return buflist_nr2name(fmark->fnum, FALSE, TRUE);
681}
682
683/*
684 * Return the line at mark "mp". Truncate to fit in window.
685 * The returned string has been allocated.
686 */
687 static char_u *
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100688mark_line(pos_T *mp, int lead_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689{
690 char_u *s, *p;
691 int len;
692
693 if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count)
694 return vim_strsave((char_u *)"-invalid-");
695 s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (int)Columns);
696 if (s == NULL)
697 return NULL;
698 /* Truncate the line to fit it in the window */
699 len = 0;
Bram Moolenaar91acfff2017-03-12 19:22:36 +0100700 for (p = s; *p != NUL; MB_PTR_ADV(p))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000701 {
702 len += ptr2cells(p);
703 if (len >= Columns - lead_len)
704 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000705 }
706 *p = NUL;
707 return s;
708}
709
710/*
711 * print the marks
712 */
713 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100714do_marks(exarg_T *eap)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000715{
716 char_u *arg = eap->arg;
717 int i;
718 char_u *name;
719
720 if (arg != NULL && *arg == NUL)
721 arg = NULL;
722
723 show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE);
724 for (i = 0; i < NMARKS; ++i)
725 show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE);
726 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
727 {
728 if (namedfm[i].fmark.fnum != 0)
729 name = fm_getname(&namedfm[i].fmark, 15);
730 else
731 name = namedfm[i].fname;
732 if (name != NULL)
733 {
734 show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A',
735 arg, &namedfm[i].fmark.mark, name,
736 namedfm[i].fmark.fnum == curbuf->b_fnum);
737 if (namedfm[i].fmark.fnum != 0)
738 vim_free(name);
739 }
740 }
741 show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE);
742 show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE);
743 show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE);
744 show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE);
745 show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE);
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000746 show_one_mark('<', arg, &curbuf->b_visual.vi_start, NULL, TRUE);
747 show_one_mark('>', arg, &curbuf->b_visual.vi_end, NULL, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000748 show_one_mark(-1, arg, NULL, NULL, FALSE);
749}
750
751 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100752show_one_mark(
753 int c,
754 char_u *arg,
755 pos_T *p,
756 char_u *name,
757 int current) /* in current file */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000758{
759 static int did_title = FALSE;
760 int mustfree = FALSE;
761
762 if (c == -1) /* finish up */
763 {
764 if (did_title)
765 did_title = FALSE;
766 else
767 {
768 if (arg == NULL)
769 MSG(_("No marks set"));
770 else
771 EMSG2(_("E283: No marks matching \"%s\""), arg);
772 }
773 }
774 /* don't output anything if 'q' typed at --more-- prompt */
775 else if (!got_int
776 && (arg == NULL || vim_strchr(arg, c) != NULL)
777 && p->lnum != 0)
778 {
779 if (!did_title)
780 {
781 /* Highlight title */
782 MSG_PUTS_TITLE(_("\nmark line col file/text"));
783 did_title = TRUE;
784 }
785 msg_putchar('\n');
786 if (!got_int)
787 {
788 sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col);
789 msg_outtrans(IObuff);
790 if (name == NULL && current)
791 {
792 name = mark_line(p, 15);
793 mustfree = TRUE;
794 }
795 if (name != NULL)
796 {
Bram Moolenaar8820b482017-03-16 17:23:31 +0100797 msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000798 if (mustfree)
799 vim_free(name);
800 }
801 }
802 out_flush(); /* show one line at a time */
803 }
804}
805
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000806/*
807 * ":delmarks[!] [marks]"
808 */
809 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100810ex_delmarks(exarg_T *eap)
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000811{
812 char_u *p;
813 int from, to;
814 int i;
815 int lower;
816 int digit;
817 int n;
818
819 if (*eap->arg == NUL && eap->forceit)
820 /* clear all marks */
821 clrallmarks(curbuf);
822 else if (eap->forceit)
823 EMSG(_(e_invarg));
824 else if (*eap->arg == NUL)
825 EMSG(_(e_argreq));
826 else
827 {
828 /* clear specified marks only */
829 for (p = eap->arg; *p != NUL; ++p)
830 {
831 lower = ASCII_ISLOWER(*p);
832 digit = VIM_ISDIGIT(*p);
833 if (lower || digit || ASCII_ISUPPER(*p))
834 {
835 if (p[1] == '-')
836 {
837 /* clear range of marks */
838 from = *p;
839 to = p[2];
840 if (!(lower ? ASCII_ISLOWER(p[2])
841 : (digit ? VIM_ISDIGIT(p[2])
842 : ASCII_ISUPPER(p[2])))
843 || to < from)
844 {
845 EMSG2(_(e_invarg2), p);
846 return;
847 }
848 p += 2;
849 }
850 else
851 /* clear one lower case mark */
852 from = to = *p;
853
854 for (i = from; i <= to; ++i)
855 {
856 if (lower)
857 curbuf->b_namedm[i - 'a'].lnum = 0;
858 else
859 {
860 if (digit)
861 n = i - '0' + NMARKS;
862 else
863 n = i - 'A';
864 namedfm[n].fmark.mark.lnum = 0;
865 vim_free(namedfm[n].fname);
866 namedfm[n].fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +0200867#ifdef FEAT_VIMINFO
868 namedfm[n].time_set = 0;
869#endif
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000870 }
871 }
872 }
873 else
874 switch (*p)
875 {
876 case '"': curbuf->b_last_cursor.lnum = 0; break;
877 case '^': curbuf->b_last_insert.lnum = 0; break;
878 case '.': curbuf->b_last_change.lnum = 0; break;
879 case '[': curbuf->b_op_start.lnum = 0; break;
880 case ']': curbuf->b_op_end.lnum = 0; break;
Bram Moolenaara226a6d2006-02-26 23:59:20 +0000881 case '<': curbuf->b_visual.vi_start.lnum = 0; break;
882 case '>': curbuf->b_visual.vi_end.lnum = 0; break;
Bram Moolenaarc0197e22004-09-13 20:26:32 +0000883 case ' ': break;
884 default: EMSG2(_(e_invarg2), p);
885 return;
886 }
887 }
888 }
889}
890
Bram Moolenaar071d4272004-06-13 20:20:40 +0000891#if defined(FEAT_JUMPLIST) || defined(PROTO)
892/*
893 * print the jumplist
894 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000895 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100896ex_jumps(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897{
898 int i;
899 char_u *name;
900
901 cleanup_jumplist();
902 /* Highlight title */
903 MSG_PUTS_TITLE(_("\n jump line col file/text"));
904 for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i)
905 {
906 if (curwin->w_jumplist[i].fmark.mark.lnum != 0)
907 {
908 if (curwin->w_jumplist[i].fmark.fnum == 0)
909 fname2fnum(&curwin->w_jumplist[i]);
910 name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
911 if (name == NULL) /* file name not available */
912 continue;
913
914 msg_putchar('\n');
915 if (got_int)
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000916 {
917 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000918 break;
Bram Moolenaared39e1d2008-08-09 17:55:22 +0000919 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000920 sprintf((char *)IObuff, "%c %2d %5ld %4d ",
921 i == curwin->w_jumplistidx ? '>' : ' ',
922 i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx
923 : curwin->w_jumplistidx - i,
924 curwin->w_jumplist[i].fmark.mark.lnum,
925 curwin->w_jumplist[i].fmark.mark.col);
926 msg_outtrans(IObuff);
927 msg_outtrans_attr(name,
928 curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
Bram Moolenaar8820b482017-03-16 17:23:31 +0100929 ? HL_ATTR(HLF_D) : 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000930 vim_free(name);
931 ui_breakcheck();
932 }
933 out_flush();
934 }
935 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
936 MSG_PUTS("\n>");
937}
938
Bram Moolenaar2d358992016-06-12 21:20:54 +0200939 void
940ex_clearjumps(exarg_T *eap UNUSED)
941{
942 free_jumplist(curwin);
943 curwin->w_jumplistlen = 0;
944 curwin->w_jumplistidx = 0;
945}
946
Bram Moolenaar071d4272004-06-13 20:20:40 +0000947/*
948 * print the changelist
949 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000950 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +0100951ex_changes(exarg_T *eap UNUSED)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000952{
953 int i;
954 char_u *name;
955
956 /* Highlight title */
957 MSG_PUTS_TITLE(_("\nchange line col text"));
958
959 for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i)
960 {
961 if (curbuf->b_changelist[i].lnum != 0)
962 {
963 msg_putchar('\n');
964 if (got_int)
965 break;
966 sprintf((char *)IObuff, "%c %3d %5ld %4d ",
967 i == curwin->w_changelistidx ? '>' : ' ',
968 i > curwin->w_changelistidx ? i - curwin->w_changelistidx
969 : curwin->w_changelistidx - i,
970 (long)curbuf->b_changelist[i].lnum,
971 curbuf->b_changelist[i].col);
972 msg_outtrans(IObuff);
973 name = mark_line(&curbuf->b_changelist[i], 17);
974 if (name == NULL)
975 break;
Bram Moolenaar8820b482017-03-16 17:23:31 +0100976 msg_outtrans_attr(name, HL_ATTR(HLF_D));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000977 vim_free(name);
978 ui_breakcheck();
979 }
980 out_flush();
981 }
982 if (curwin->w_changelistidx == curbuf->b_changelistlen)
983 MSG_PUTS("\n>");
984}
985#endif
986
987#define one_adjust(add) \
988 { \
989 lp = add; \
990 if (*lp >= line1 && *lp <= line2) \
991 { \
992 if (amount == MAXLNUM) \
993 *lp = 0; \
994 else \
995 *lp += amount; \
996 } \
997 else if (amount_after && *lp > line2) \
998 *lp += amount_after; \
999 }
1000
1001/* don't delete the line, just put at first deleted line */
1002#define one_adjust_nodel(add) \
1003 { \
1004 lp = add; \
1005 if (*lp >= line1 && *lp <= line2) \
1006 { \
1007 if (amount == MAXLNUM) \
1008 *lp = line1; \
1009 else \
1010 *lp += amount; \
1011 } \
1012 else if (amount_after && *lp > line2) \
1013 *lp += amount_after; \
1014 }
1015
1016/*
1017 * Adjust marks between line1 and line2 (inclusive) to move 'amount' lines.
1018 * Must be called before changed_*(), appended_lines() or deleted_lines().
1019 * May be called before or after changing the text.
1020 * When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks
1021 * within this range are made invalid.
1022 * If 'amount_after' is non-zero adjust marks after line2.
1023 * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2);
1024 * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0);
1025 * or: mark_adjust(56, 55, MAXLNUM, 2);
1026 */
1027 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001028mark_adjust(
1029 linenr_T line1,
1030 linenr_T line2,
1031 long amount,
1032 long amount_after)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001033{
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001034 mark_adjust_internal(line1, line2, amount, amount_after, TRUE);
1035}
1036
1037 void
1038mark_adjust_nofold(
1039 linenr_T line1,
1040 linenr_T line2,
1041 long amount,
1042 long amount_after)
1043{
1044 mark_adjust_internal(line1, line2, amount, amount_after, FALSE);
1045}
1046
1047 static void
1048mark_adjust_internal(
1049 linenr_T line1,
1050 linenr_T line2,
1051 long amount,
1052 long amount_after,
1053 int adjust_folds UNUSED)
1054{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001055 int i;
1056 int fnum = curbuf->b_fnum;
1057 linenr_T *lp;
1058 win_T *win;
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001059 tabpage_T *tab;
Bram Moolenaarb6a76ff2013-02-06 12:33:21 +01001060 static pos_T initpos = INIT_POS_T(1, 0, 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001061
1062 if (line2 < line1 && amount_after == 0L) /* nothing to do */
1063 return;
1064
1065 if (!cmdmod.lockmarks)
1066 {
1067 /* named marks, lower case and upper case */
1068 for (i = 0; i < NMARKS; i++)
1069 {
1070 one_adjust(&(curbuf->b_namedm[i].lnum));
1071 if (namedfm[i].fmark.fnum == fnum)
1072 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1073 }
1074 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1075 {
1076 if (namedfm[i].fmark.fnum == fnum)
1077 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum));
1078 }
1079
1080 /* last Insert position */
1081 one_adjust(&(curbuf->b_last_insert.lnum));
1082
1083 /* last change position */
1084 one_adjust(&(curbuf->b_last_change.lnum));
1085
Bram Moolenaarb6a76ff2013-02-06 12:33:21 +01001086 /* last cursor position, if it was set */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001087 if (!EQUAL_POS(curbuf->b_last_cursor, initpos))
Bram Moolenaarb6a76ff2013-02-06 12:33:21 +01001088 one_adjust(&(curbuf->b_last_cursor.lnum));
1089
1090
Bram Moolenaar071d4272004-06-13 20:20:40 +00001091#ifdef FEAT_JUMPLIST
1092 /* list of change positions */
1093 for (i = 0; i < curbuf->b_changelistlen; ++i)
1094 one_adjust_nodel(&(curbuf->b_changelist[i].lnum));
1095#endif
1096
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097 /* Visual area */
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001098 one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum));
1099 one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100
1101#ifdef FEAT_QUICKFIX
1102 /* quickfix marks */
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001103 qf_mark_adjust(NULL, line1, line2, amount, amount_after);
1104 /* location lists */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001105 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar28c258f2006-01-25 22:02:51 +00001106 qf_mark_adjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001107#endif
1108
1109#ifdef FEAT_SIGNS
1110 sign_mark_adjust(line1, line2, amount, amount_after);
1111#endif
1112 }
1113
1114 /* previous context mark */
1115 one_adjust(&(curwin->w_pcmark.lnum));
1116
1117 /* previous pcmark */
1118 one_adjust(&(curwin->w_prev_pcmark.lnum));
1119
1120 /* saved cursor for formatting */
1121 if (saved_cursor.lnum != 0)
1122 one_adjust_nodel(&(saved_cursor.lnum));
1123
1124 /*
1125 * Adjust items in all windows related to the current buffer.
1126 */
Bram Moolenaarbd1e5d22009-04-29 09:02:44 +00001127 FOR_ALL_TAB_WINDOWS(tab, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128 {
1129#ifdef FEAT_JUMPLIST
1130 if (!cmdmod.lockmarks)
1131 /* Marks in the jumplist. When deleting lines, this may create
1132 * duplicate marks in the jumplist, they will be removed later. */
1133 for (i = 0; i < win->w_jumplistlen; ++i)
1134 if (win->w_jumplist[i].fmark.fnum == fnum)
1135 one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum));
1136#endif
1137
1138 if (win->w_buffer == curbuf)
1139 {
1140 if (!cmdmod.lockmarks)
1141 /* marks in the tag stack */
1142 for (i = 0; i < win->w_tagstacklen; i++)
1143 if (win->w_tagstack[i].fmark.fnum == fnum)
1144 one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum));
1145
Bram Moolenaar071d4272004-06-13 20:20:40 +00001146 /* the displayed Visual area */
1147 if (win->w_old_cursor_lnum != 0)
1148 {
1149 one_adjust_nodel(&(win->w_old_cursor_lnum));
1150 one_adjust_nodel(&(win->w_old_visual_lnum));
1151 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001152
1153 /* topline and cursor position for windows with the same buffer
1154 * other than the current window */
1155 if (win != curwin)
1156 {
1157 if (win->w_topline >= line1 && win->w_topline <= line2)
1158 {
1159 if (amount == MAXLNUM) /* topline is deleted */
1160 {
1161 if (line1 <= 1)
1162 win->w_topline = 1;
1163 else
1164 win->w_topline = line1 - 1;
1165 }
1166 else /* keep topline on the same line */
1167 win->w_topline += amount;
1168#ifdef FEAT_DIFF
1169 win->w_topfill = 0;
1170#endif
1171 }
1172 else if (amount_after && win->w_topline > line2)
1173 {
1174 win->w_topline += amount_after;
1175#ifdef FEAT_DIFF
1176 win->w_topfill = 0;
1177#endif
1178 }
1179 if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2)
1180 {
1181 if (amount == MAXLNUM) /* line with cursor is deleted */
1182 {
1183 if (line1 <= 1)
1184 win->w_cursor.lnum = 1;
1185 else
1186 win->w_cursor.lnum = line1 - 1;
1187 win->w_cursor.col = 0;
1188 }
1189 else /* keep cursor on the same line */
1190 win->w_cursor.lnum += amount;
1191 }
1192 else if (amount_after && win->w_cursor.lnum > line2)
1193 win->w_cursor.lnum += amount_after;
1194 }
1195
1196#ifdef FEAT_FOLDING
1197 /* adjust folds */
Bram Moolenaar88d298a2017-03-14 21:53:58 +01001198 if (adjust_folds)
1199 foldMarkAdjust(win, line1, line2, amount, amount_after);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001200#endif
1201 }
1202 }
1203
1204#ifdef FEAT_DIFF
1205 /* adjust diffs */
1206 diff_mark_adjust(line1, line2, amount, amount_after);
1207#endif
1208}
1209
1210/* This code is used often, needs to be fast. */
1211#define col_adjust(pp) \
1212 { \
1213 posp = pp; \
1214 if (posp->lnum == lnum && posp->col >= mincol) \
1215 { \
1216 posp->lnum += lnum_amount; \
1217 if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \
1218 posp->col = 0; \
1219 else \
1220 posp->col += col_amount; \
1221 } \
1222 }
1223
1224/*
1225 * Adjust marks in line "lnum" at column "mincol" and further: add
1226 * "lnum_amount" to the line number and add "col_amount" to the column
1227 * position.
1228 */
1229 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001230mark_col_adjust(
1231 linenr_T lnum,
1232 colnr_T mincol,
1233 long lnum_amount,
1234 long col_amount)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001235{
1236 int i;
1237 int fnum = curbuf->b_fnum;
1238 win_T *win;
1239 pos_T *posp;
1240
1241 if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks)
1242 return; /* nothing to do */
1243
1244 /* named marks, lower case and upper case */
1245 for (i = 0; i < NMARKS; i++)
1246 {
1247 col_adjust(&(curbuf->b_namedm[i]));
1248 if (namedfm[i].fmark.fnum == fnum)
1249 col_adjust(&(namedfm[i].fmark.mark));
1250 }
1251 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1252 {
1253 if (namedfm[i].fmark.fnum == fnum)
1254 col_adjust(&(namedfm[i].fmark.mark));
1255 }
1256
1257 /* last Insert position */
1258 col_adjust(&(curbuf->b_last_insert));
1259
1260 /* last change position */
1261 col_adjust(&(curbuf->b_last_change));
1262
1263#ifdef FEAT_JUMPLIST
1264 /* list of change positions */
1265 for (i = 0; i < curbuf->b_changelistlen; ++i)
1266 col_adjust(&(curbuf->b_changelist[i]));
1267#endif
1268
Bram Moolenaar071d4272004-06-13 20:20:40 +00001269 /* Visual area */
Bram Moolenaara226a6d2006-02-26 23:59:20 +00001270 col_adjust(&(curbuf->b_visual.vi_start));
1271 col_adjust(&(curbuf->b_visual.vi_end));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001272
1273 /* previous context mark */
1274 col_adjust(&(curwin->w_pcmark));
1275
1276 /* previous pcmark */
1277 col_adjust(&(curwin->w_prev_pcmark));
1278
1279 /* saved cursor for formatting */
1280 col_adjust(&saved_cursor);
1281
1282 /*
1283 * Adjust items in all windows related to the current buffer.
1284 */
1285 FOR_ALL_WINDOWS(win)
1286 {
1287#ifdef FEAT_JUMPLIST
1288 /* marks in the jumplist */
1289 for (i = 0; i < win->w_jumplistlen; ++i)
1290 if (win->w_jumplist[i].fmark.fnum == fnum)
1291 col_adjust(&(win->w_jumplist[i].fmark.mark));
1292#endif
1293
1294 if (win->w_buffer == curbuf)
1295 {
1296 /* marks in the tag stack */
1297 for (i = 0; i < win->w_tagstacklen; i++)
1298 if (win->w_tagstack[i].fmark.fnum == fnum)
1299 col_adjust(&(win->w_tagstack[i].fmark.mark));
1300
1301 /* cursor position for other windows with the same buffer */
1302 if (win != curwin)
1303 col_adjust(&win->w_cursor);
1304 }
1305 }
1306}
1307
1308#ifdef FEAT_JUMPLIST
1309/*
1310 * When deleting lines, this may create duplicate marks in the
1311 * jumplist. They will be removed here for the current window.
1312 */
1313 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001314cleanup_jumplist(void)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315{
1316 int i;
1317 int from, to;
1318
1319 to = 0;
1320 for (from = 0; from < curwin->w_jumplistlen; ++from)
1321 {
1322 if (curwin->w_jumplistidx == from)
1323 curwin->w_jumplistidx = to;
1324 for (i = from + 1; i < curwin->w_jumplistlen; ++i)
1325 if (curwin->w_jumplist[i].fmark.fnum
1326 == curwin->w_jumplist[from].fmark.fnum
1327 && curwin->w_jumplist[from].fmark.fnum != 0
1328 && curwin->w_jumplist[i].fmark.mark.lnum
1329 == curwin->w_jumplist[from].fmark.mark.lnum)
1330 break;
1331 if (i >= curwin->w_jumplistlen) /* no duplicate */
1332 curwin->w_jumplist[to++] = curwin->w_jumplist[from];
1333 else
1334 vim_free(curwin->w_jumplist[from].fname);
1335 }
1336 if (curwin->w_jumplistidx == curwin->w_jumplistlen)
1337 curwin->w_jumplistidx = to;
1338 curwin->w_jumplistlen = to;
1339}
1340
Bram Moolenaar071d4272004-06-13 20:20:40 +00001341/*
1342 * Copy the jumplist from window "from" to window "to".
1343 */
1344 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001345copy_jumplist(win_T *from, win_T *to)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001346{
1347 int i;
1348
1349 for (i = 0; i < from->w_jumplistlen; ++i)
1350 {
1351 to->w_jumplist[i] = from->w_jumplist[i];
1352 if (from->w_jumplist[i].fname != NULL)
1353 to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname);
1354 }
1355 to->w_jumplistlen = from->w_jumplistlen;
1356 to->w_jumplistidx = from->w_jumplistidx;
1357}
1358
1359/*
1360 * Free items in the jumplist of window "wp".
1361 */
1362 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001363free_jumplist(win_T *wp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001364{
1365 int i;
1366
1367 for (i = 0; i < wp->w_jumplistlen; ++i)
1368 vim_free(wp->w_jumplist[i].fname);
1369}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001370#endif /* FEAT_JUMPLIST */
1371
1372 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001373set_last_cursor(win_T *win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001374{
Bram Moolenaar9db12932013-11-03 00:20:52 +01001375 if (win->w_buffer != NULL)
1376 win->w_buffer->b_last_cursor = win->w_cursor;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001377}
1378
Bram Moolenaarea408852005-06-25 22:49:46 +00001379#if defined(EXITFREE) || defined(PROTO)
1380 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001381free_all_marks(void)
Bram Moolenaarea408852005-06-25 22:49:46 +00001382{
1383 int i;
1384
1385 for (i = 0; i < NMARKS + EXTRA_MARKS; i++)
1386 if (namedfm[i].fmark.mark.lnum != 0)
1387 vim_free(namedfm[i].fname);
1388}
1389#endif
1390
Bram Moolenaar071d4272004-06-13 20:20:40 +00001391#if defined(FEAT_VIMINFO) || defined(PROTO)
1392 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001393read_viminfo_filemark(vir_T *virp, int force)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394{
1395 char_u *str;
1396 xfmark_T *fm;
1397 int i;
1398
1399 /* We only get here if line[0] == '\'' or '-'.
1400 * Illegal mark names are ignored (for future expansion). */
1401 str = virp->vir_line + 1;
1402 if (
1403#ifndef EBCDIC
1404 *str <= 127 &&
1405#endif
1406 ((*virp->vir_line == '\'' && (VIM_ISDIGIT(*str) || isupper(*str)))
1407 || (*virp->vir_line == '-' && *str == '\'')))
1408 {
1409 if (*str == '\'')
1410 {
1411#ifdef FEAT_JUMPLIST
1412 /* If the jumplist isn't full insert fmark as oldest entry */
1413 if (curwin->w_jumplistlen == JUMPLISTSIZE)
1414 fm = NULL;
1415 else
1416 {
1417 for (i = curwin->w_jumplistlen; i > 0; --i)
1418 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
1419 ++curwin->w_jumplistidx;
1420 ++curwin->w_jumplistlen;
1421 fm = &curwin->w_jumplist[0];
1422 fm->fmark.mark.lnum = 0;
1423 fm->fname = NULL;
1424 }
1425#else
1426 fm = NULL;
1427#endif
1428 }
1429 else if (VIM_ISDIGIT(*str))
1430 fm = &namedfm[*str - '0' + NMARKS];
1431 else
1432 fm = &namedfm[*str - 'A'];
1433 if (fm != NULL && (fm->fmark.mark.lnum == 0 || force))
1434 {
1435 str = skipwhite(str + 1);
1436 fm->fmark.mark.lnum = getdigits(&str);
1437 str = skipwhite(str);
1438 fm->fmark.mark.col = getdigits(&str);
1439#ifdef FEAT_VIRTUALEDIT
1440 fm->fmark.mark.coladd = 0;
1441#endif
1442 fm->fmark.fnum = 0;
1443 str = skipwhite(str);
1444 vim_free(fm->fname);
1445 fm->fname = viminfo_readstring(virp, (int)(str - virp->vir_line),
1446 FALSE);
Bram Moolenaar2d358992016-06-12 21:20:54 +02001447 fm->time_set = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001448 }
1449 }
1450 return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd);
1451}
1452
Bram Moolenaar2d358992016-06-12 21:20:54 +02001453static xfmark_T *vi_namedfm = NULL;
1454#ifdef FEAT_JUMPLIST
1455static xfmark_T *vi_jumplist = NULL;
1456static int vi_jumplist_len = 0;
1457#endif
1458
1459/*
1460 * Prepare for reading viminfo marks when writing viminfo later.
1461 */
1462 void
1463prepare_viminfo_marks(void)
1464{
1465 vi_namedfm = (xfmark_T *)alloc_clear((NMARKS + EXTRA_MARKS)
1466 * (int)sizeof(xfmark_T));
1467#ifdef FEAT_JUMPLIST
1468 vi_jumplist = (xfmark_T *)alloc_clear(JUMPLISTSIZE
1469 * (int)sizeof(xfmark_T));
1470 vi_jumplist_len = 0;
1471#endif
1472}
1473
1474 void
1475finish_viminfo_marks(void)
1476{
1477 int i;
1478
1479 if (vi_namedfm != NULL)
1480 {
1481 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i)
1482 vim_free(vi_namedfm[i].fname);
1483 vim_free(vi_namedfm);
1484 vi_namedfm = NULL;
1485 }
1486#ifdef FEAT_JUMPLIST
1487 if (vi_jumplist != NULL)
1488 {
1489 for (i = 0; i < vi_jumplist_len; ++i)
1490 vim_free(vi_jumplist[i].fname);
1491 vim_free(vi_jumplist);
1492 vi_jumplist = NULL;
1493 }
1494#endif
1495}
1496
1497/*
1498 * Accept a new style mark line from the viminfo, store it when it's new.
1499 */
1500 void
1501handle_viminfo_mark(garray_T *values, int force)
1502{
1503 bval_T *vp = (bval_T *)values->ga_data;
1504 int name;
1505 linenr_T lnum;
1506 colnr_T col;
1507 time_t timestamp;
1508 xfmark_T *fm = NULL;
1509
1510 /* Check the format:
1511 * |{bartype},{name},{lnum},{col},{timestamp},{filename} */
1512 if (values->ga_len < 5
1513 || vp[0].bv_type != BVAL_NR
1514 || vp[1].bv_type != BVAL_NR
1515 || vp[2].bv_type != BVAL_NR
1516 || vp[3].bv_type != BVAL_NR
1517 || vp[4].bv_type != BVAL_STRING)
1518 return;
1519
1520 name = vp[0].bv_nr;
1521 if (name != '\'' && !VIM_ISDIGIT(name) && !ASCII_ISUPPER(name))
1522 return;
1523 lnum = vp[1].bv_nr;
1524 col = vp[2].bv_nr;
1525 if (lnum <= 0 || col < 0)
1526 return;
1527 timestamp = (time_t)vp[3].bv_nr;
1528
1529 if (name == '\'')
1530 {
1531#ifdef FEAT_JUMPLIST
1532 if (vi_jumplist != NULL)
1533 {
1534 if (vi_jumplist_len < JUMPLISTSIZE)
1535 fm = &vi_jumplist[vi_jumplist_len++];
1536 }
1537 else
1538 {
1539 int idx;
1540 int i;
1541
1542 /* If we have a timestamp insert it in the right place. */
1543 if (timestamp != 0)
1544 {
1545 for (idx = curwin->w_jumplistlen - 1; idx >= 0; --idx)
1546 if (curwin->w_jumplist[idx].time_set < timestamp)
Bram Moolenaarece74ab2016-06-13 22:22:15 +02001547 {
1548 ++idx;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001549 break;
Bram Moolenaarece74ab2016-06-13 22:22:15 +02001550 }
Bram Moolenaar678e4802016-06-17 22:38:46 +02001551 /* idx cannot be zero now */
Bram Moolenaarece74ab2016-06-13 22:22:15 +02001552 if (idx < 0 && curwin->w_jumplistlen < JUMPLISTSIZE)
1553 /* insert as the oldest entry */
1554 idx = 0;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001555 }
1556 else if (curwin->w_jumplistlen < JUMPLISTSIZE)
1557 /* insert as oldest entry */
1558 idx = 0;
1559 else
1560 idx = -1;
1561
1562 if (idx >= 0)
1563 {
1564 if (curwin->w_jumplistlen == JUMPLISTSIZE)
1565 {
1566 /* Drop the oldest entry. */
Bram Moolenaar28607ba2016-06-15 21:44:51 +02001567 --idx;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001568 vim_free(curwin->w_jumplist[0].fname);
1569 for (i = 0; i < idx; ++i)
1570 curwin->w_jumplist[i] = curwin->w_jumplist[i + 1];
1571 }
1572 else
1573 {
1574 /* Move newer entries forward. */
Bram Moolenaar2d358992016-06-12 21:20:54 +02001575 for (i = curwin->w_jumplistlen; i > idx; --i)
1576 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1];
1577 ++curwin->w_jumplistidx;
1578 ++curwin->w_jumplistlen;
1579 }
1580 fm = &curwin->w_jumplist[idx];
1581 fm->fmark.mark.lnum = 0;
1582 fm->fname = NULL;
1583 fm->time_set = 0;
1584 }
1585 }
1586#endif
1587 }
1588 else
1589 {
1590 int idx;
1591
1592 if (VIM_ISDIGIT(name))
1593 {
1594 if (vi_namedfm != NULL)
1595 idx = name - '0' + NMARKS;
1596 else
1597 {
1598 int i;
1599
1600 /* Do not use the name from the viminfo file, insert in time
1601 * order. */
1602 for (idx = NMARKS; idx < NMARKS + EXTRA_MARKS; ++idx)
1603 if (namedfm[idx].time_set < timestamp)
1604 break;
1605 if (idx == NMARKS + EXTRA_MARKS)
1606 /* All existing entries are newer. */
1607 return;
1608 i = NMARKS + EXTRA_MARKS - 1;
1609
1610 vim_free(namedfm[i].fname);
1611 for ( ; i > idx; --i)
1612 namedfm[i] = namedfm[i - 1];
1613 namedfm[idx].fname = NULL;
1614 }
1615 }
1616 else
1617 idx = name - 'A';
1618 if (vi_namedfm != NULL)
1619 fm = &vi_namedfm[idx];
1620 else
1621 fm = &namedfm[idx];
1622 }
1623
1624 if (fm != NULL)
1625 {
Bram Moolenaar156919f2016-10-15 20:46:20 +02001626 if (vi_namedfm != NULL || fm->fmark.mark.lnum == 0
1627 || fm->time_set < timestamp || force)
Bram Moolenaar2d358992016-06-12 21:20:54 +02001628 {
1629 fm->fmark.mark.lnum = lnum;
1630 fm->fmark.mark.col = col;
1631#ifdef FEAT_VIRTUALEDIT
1632 fm->fmark.mark.coladd = 0;
1633#endif
1634 fm->fmark.fnum = 0;
1635 vim_free(fm->fname);
1636 if (vp[4].bv_allocated)
1637 {
1638 fm->fname = vp[4].bv_string;
1639 vp[4].bv_string = NULL;
1640 }
1641 else
1642 fm->fname = vim_strsave(vp[4].bv_string);
1643 fm->time_set = timestamp;
1644 }
1645 }
1646}
1647
Bram Moolenaare6278052017-08-13 18:11:17 +02001648/*
1649 * Return TRUE if marks for "buf" should not be written.
1650 */
1651 static int
1652skip_for_viminfo(buf_T *buf)
1653{
1654 return
1655#ifdef FEAT_TERMINAL
1656 bt_terminal(buf) ||
1657#endif
1658 removable(buf->b_ffname);
1659}
1660
Bram Moolenaar071d4272004-06-13 20:20:40 +00001661 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001662write_viminfo_filemarks(FILE *fp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001663{
1664 int i;
1665 char_u *name;
1666 buf_T *buf;
1667 xfmark_T *fm;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001668 int vi_idx;
1669 int idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001670
1671 if (get_viminfo_parameter('f') == 0)
1672 return;
1673
Bram Moolenaar2f1e0502010-08-13 11:18:02 +02001674 fputs(_("\n# File marks:\n"), fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001675
Bram Moolenaar2d358992016-06-12 21:20:54 +02001676 /* Write the filemarks 'A - 'Z */
1677 for (i = 0; i < NMARKS; i++)
1678 {
1679 if (vi_namedfm != NULL && (vi_namedfm[i].time_set > namedfm[i].time_set
1680 || namedfm[i].fmark.mark.lnum == 0))
1681 fm = &vi_namedfm[i];
1682 else
1683 fm = &namedfm[i];
1684 write_one_filemark(fp, fm, '\'', i + 'A');
1685 }
1686
Bram Moolenaar071d4272004-06-13 20:20:40 +00001687 /*
1688 * Find a mark that is the same file and position as the cursor.
1689 * That one, or else the last one is deleted.
1690 * Move '0 to '1, '1 to '2, etc. until the matching one or '9
Bram Moolenaar2d358992016-06-12 21:20:54 +02001691 * Set the '0 mark to current cursor position.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001692 */
Bram Moolenaare6278052017-08-13 18:11:17 +02001693 if (curbuf->b_ffname != NULL && !skip_for_viminfo(curbuf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001694 {
1695 name = buflist_nr2name(curbuf->b_fnum, TRUE, FALSE);
1696 for (i = NMARKS; i < NMARKS + EXTRA_MARKS - 1; ++i)
1697 if (namedfm[i].fmark.mark.lnum == curwin->w_cursor.lnum
1698 && (namedfm[i].fname == NULL
1699 ? namedfm[i].fmark.fnum == curbuf->b_fnum
1700 : (name != NULL
1701 && STRCMP(name, namedfm[i].fname) == 0)))
1702 break;
1703 vim_free(name);
1704
1705 vim_free(namedfm[i].fname);
1706 for ( ; i > NMARKS; --i)
1707 namedfm[i] = namedfm[i - 1];
1708 namedfm[NMARKS].fmark.mark = curwin->w_cursor;
1709 namedfm[NMARKS].fmark.fnum = curbuf->b_fnum;
1710 namedfm[NMARKS].fname = NULL;
Bram Moolenaar2d358992016-06-12 21:20:54 +02001711 namedfm[NMARKS].time_set = vim_time();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001712 }
1713
Bram Moolenaar2d358992016-06-12 21:20:54 +02001714 /* Write the filemarks '0 - '9. Newest (highest timestamp) first. */
1715 vi_idx = NMARKS;
1716 idx = NMARKS;
1717 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++)
1718 {
Bram Moolenaar36f0f062016-06-14 23:02:46 +02001719 xfmark_T *vi_fm = vi_namedfm != NULL ? &vi_namedfm[vi_idx] : NULL;
1720
1721 if (vi_fm != NULL
1722 && vi_fm->fmark.mark.lnum != 0
1723 && (vi_fm->time_set > namedfm[idx].time_set
Bram Moolenaar2d358992016-06-12 21:20:54 +02001724 || namedfm[idx].fmark.mark.lnum == 0))
Bram Moolenaar36f0f062016-06-14 23:02:46 +02001725 {
1726 fm = vi_fm;
1727 ++vi_idx;
1728 }
Bram Moolenaar2d358992016-06-12 21:20:54 +02001729 else
Bram Moolenaar36f0f062016-06-14 23:02:46 +02001730 {
Bram Moolenaar2d358992016-06-12 21:20:54 +02001731 fm = &namedfm[idx++];
Bram Moolenaar36f0f062016-06-14 23:02:46 +02001732 if (vi_fm != NULL
1733 && vi_fm->fmark.mark.lnum == fm->fmark.mark.lnum
1734 && vi_fm->time_set == fm->time_set
1735 && ((vi_fm->fmark.fnum != 0
1736 && vi_fm->fmark.fnum == fm->fmark.fnum)
1737 || (vi_fm->fname != NULL
1738 && fm->fname != NULL
1739 && STRCMP(vi_fm->fname, fm->fname) == 0)))
1740 ++vi_idx; /* skip duplicate */
1741 }
Bram Moolenaar2d358992016-06-12 21:20:54 +02001742 write_one_filemark(fp, fm, '\'', i - NMARKS + '0');
1743 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001744
1745#ifdef FEAT_JUMPLIST
1746 /* Write the jumplist with -' */
Bram Moolenaar2f1e0502010-08-13 11:18:02 +02001747 fputs(_("\n# Jumplist (newest first):\n"), fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 setpcmark(); /* add current cursor position */
1749 cleanup_jumplist();
Bram Moolenaarece74ab2016-06-13 22:22:15 +02001750 vi_idx = 0;
1751 idx = curwin->w_jumplistlen - 1;
1752 for (i = 0; i < JUMPLISTSIZE; ++i)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 {
Bram Moolenaarece74ab2016-06-13 22:22:15 +02001754 xfmark_T *vi_fm;
1755
1756 fm = idx >= 0 ? &curwin->w_jumplist[idx] : NULL;
1757 vi_fm = vi_idx < vi_jumplist_len ? &vi_jumplist[vi_idx] : NULL;
1758 if (fm == NULL && vi_fm == NULL)
1759 break;
1760 if (fm == NULL || (vi_fm != NULL && fm->time_set < vi_fm->time_set))
1761 {
1762 fm = vi_fm;
1763 ++vi_idx;
1764 }
1765 else
1766 --idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767 if (fm->fmark.fnum == 0
1768 || ((buf = buflist_findnr(fm->fmark.fnum)) != NULL
Bram Moolenaare6278052017-08-13 18:11:17 +02001769 && !skip_for_viminfo(buf)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001770 write_one_filemark(fp, fm, '-', '\'');
1771 }
1772#endif
1773}
1774
1775 static void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001776write_one_filemark(
1777 FILE *fp,
1778 xfmark_T *fm,
1779 int c1,
1780 int c2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001781{
1782 char_u *name;
1783
1784 if (fm->fmark.mark.lnum == 0) /* not set */
1785 return;
1786
1787 if (fm->fmark.fnum != 0) /* there is a buffer */
1788 name = buflist_nr2name(fm->fmark.fnum, TRUE, FALSE);
1789 else
1790 name = fm->fname; /* use name from .viminfo */
1791 if (name != NULL && *name != NUL)
1792 {
1793 fprintf(fp, "%c%c %ld %ld ", c1, c2, (long)fm->fmark.mark.lnum,
1794 (long)fm->fmark.mark.col);
1795 viminfo_writestring(fp, name);
Bram Moolenaar2d358992016-06-12 21:20:54 +02001796
1797 /* Barline: |{bartype},{name},{lnum},{col},{timestamp},{filename}
1798 * size up to filename: 8 + 3 * 20 */
1799 fprintf(fp, "|%d,%d,%ld,%ld,%ld,", BARTYPE_MARK, c2,
1800 (long)fm->fmark.mark.lnum, (long)fm->fmark.mark.col,
1801 (long)fm->time_set);
1802 barline_writestring(fp, name, LSIZE - 70);
1803 putc('\n', fp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804 }
1805
1806 if (fm->fmark.fnum != 0)
1807 vim_free(name);
1808}
1809
1810/*
1811 * Return TRUE if "name" is on removable media (depending on 'viminfo').
1812 */
1813 int
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001814removable(char_u *name)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001815{
1816 char_u *p;
1817 char_u part[51];
1818 int retval = FALSE;
Bram Moolenaarcfc6c432005-06-06 21:50:35 +00001819 size_t n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001820
1821 name = home_replace_save(NULL, name);
1822 if (name != NULL)
1823 {
1824 for (p = p_viminfo; *p; )
1825 {
1826 copy_option_part(&p, part, 51, ", ");
Bram Moolenaarcfc6c432005-06-06 21:50:35 +00001827 if (part[0] == 'r')
Bram Moolenaar071d4272004-06-13 20:20:40 +00001828 {
Bram Moolenaarcfc6c432005-06-06 21:50:35 +00001829 n = STRLEN(part + 1);
1830 if (MB_STRNICMP(part + 1, name, n) == 0)
1831 {
1832 retval = TRUE;
1833 break;
1834 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835 }
1836 }
1837 vim_free(name);
1838 }
1839 return retval;
1840}
1841
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001842 static void
1843write_one_mark(FILE *fp_out, int c, pos_T *pos)
1844{
1845 if (pos->lnum != 0)
1846 fprintf(fp_out, "\t%c\t%ld\t%d\n", c, (long)pos->lnum, (int)pos->col);
1847}
1848
1849
1850 static void
1851write_buffer_marks(buf_T *buf, FILE *fp_out)
1852{
1853 int i;
1854 pos_T pos;
1855
1856 home_replace(NULL, buf->b_ffname, IObuff, IOSIZE, TRUE);
1857 fprintf(fp_out, "\n> ");
1858 viminfo_writestring(fp_out, IObuff);
1859
1860 /* Write the last used timestamp as the lnum of the non-existing mark '*'.
1861 * Older Vims will ignore it and/or copy it. */
1862 pos.lnum = (linenr_T)buf->b_last_used;
1863 pos.col = 0;
1864 write_one_mark(fp_out, '*', &pos);
1865
1866 write_one_mark(fp_out, '"', &buf->b_last_cursor);
1867 write_one_mark(fp_out, '^', &buf->b_last_insert);
1868 write_one_mark(fp_out, '.', &buf->b_last_change);
1869#ifdef FEAT_JUMPLIST
1870 /* changelist positions are stored oldest first */
1871 for (i = 0; i < buf->b_changelistlen; ++i)
1872 {
1873 /* skip duplicates */
Bram Moolenaarb5aedf32017-03-12 18:23:53 +01001874 if (i == 0 || !EQUAL_POS(buf->b_changelist[i - 1],
1875 buf->b_changelist[i]))
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001876 write_one_mark(fp_out, '+', &buf->b_changelist[i]);
1877 }
1878#endif
1879 for (i = 0; i < NMARKS; i++)
1880 write_one_mark(fp_out, 'a' + i, &buf->b_namedm[i]);
1881}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001882
1883/*
1884 * Write all the named marks for all buffers.
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001885 * When "buflist" is not NULL fill it with the buffers for which marks are to
1886 * be written.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001887 */
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001888 void
1889write_viminfo_marks(FILE *fp_out, garray_T *buflist)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001890{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001891 buf_T *buf;
1892 int is_mark_set;
1893 int i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001894 win_T *win;
Bram Moolenaarf740b292006-02-16 22:11:02 +00001895 tabpage_T *tp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001896
1897 /*
1898 * Set b_last_cursor for the all buffers that have a window.
1899 */
Bram Moolenaarf740b292006-02-16 22:11:02 +00001900 FOR_ALL_TAB_WINDOWS(tp, win)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001901 set_last_cursor(win);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001902
Bram Moolenaar2f1e0502010-08-13 11:18:02 +02001903 fputs(_("\n# History of marks within files (newest to oldest):\n"), fp_out);
Bram Moolenaar29323592016-07-24 22:04:11 +02001904 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001905 {
1906 /*
1907 * Only write something if buffer has been loaded and at least one
1908 * mark is set.
1909 */
1910 if (buf->b_marks_read)
1911 {
1912 if (buf->b_last_cursor.lnum != 0)
1913 is_mark_set = TRUE;
1914 else
1915 {
1916 is_mark_set = FALSE;
1917 for (i = 0; i < NMARKS; i++)
1918 if (buf->b_namedm[i].lnum != 0)
1919 {
1920 is_mark_set = TRUE;
1921 break;
1922 }
1923 }
1924 if (is_mark_set && buf->b_ffname != NULL
Bram Moolenaare6278052017-08-13 18:11:17 +02001925 && buf->b_ffname[0] != NUL
1926 && !skip_for_viminfo(buf))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001927 {
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001928 if (buflist == NULL)
1929 write_buffer_marks(buf, fp_out);
1930 else if (ga_grow(buflist, 1) == OK)
1931 ((buf_T **)buflist->ga_data)[buflist->ga_len++] = buf;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001932 }
1933 }
1934 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001935}
1936
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001937/*
1938 * Compare functions for qsort() below, that compares b_last_used.
1939 */
1940 static int
1941#ifdef __BORLANDC__
1942_RTLENTRYF
1943#endif
1944buf_compare(const void *s1, const void *s2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001945{
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001946 buf_T *buf1 = *(buf_T **)s1;
1947 buf_T *buf2 = *(buf_T **)s2;
1948
1949 if (buf1->b_last_used == buf2->b_last_used)
1950 return 0;
1951 return buf1->b_last_used > buf2->b_last_used ? -1 : 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001952}
1953
1954/*
1955 * Handle marks in the viminfo file:
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001956 * fp_out != NULL: copy marks, in time order with buffers in "buflist".
Bram Moolenaard812df62008-11-09 12:46:09 +00001957 * fp_out == NULL && (flags & VIF_WANT_MARKS): read marks for curbuf only
1958 * fp_out == NULL && (flags & VIF_GET_OLDFILES | VIF_FORCEIT): fill v:oldfiles
Bram Moolenaar071d4272004-06-13 20:20:40 +00001959 */
1960 void
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001961copy_viminfo_marks(
1962 vir_T *virp,
1963 FILE *fp_out,
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001964 garray_T *buflist,
Bram Moolenaar52ea13d2016-01-30 18:51:09 +01001965 int eof,
1966 int flags)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001967{
1968 char_u *line = virp->vir_line;
1969 buf_T *buf;
1970 int num_marked_files;
1971 int load_marks;
1972 int copy_marks_out;
1973 char_u *str;
1974 int i;
1975 char_u *p;
1976 char_u *name_buf;
1977 pos_T pos;
Bram Moolenaard812df62008-11-09 12:46:09 +00001978#ifdef FEAT_EVAL
1979 list_T *list = NULL;
1980#endif
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001981 int count = 0;
1982 int buflist_used = 0;
1983 buf_T *buflist_buf = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001984
1985 if ((name_buf = alloc(LSIZE)) == NULL)
1986 return;
1987 *name_buf = NUL;
Bram Moolenaard812df62008-11-09 12:46:09 +00001988
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02001989 if (fp_out != NULL && buflist->ga_len > 0)
1990 {
1991 /* Sort the list of buffers on b_last_used. */
1992 qsort(buflist->ga_data, (size_t)buflist->ga_len,
1993 sizeof(buf_T *), buf_compare);
1994 buflist_buf = ((buf_T **)buflist->ga_data)[0];
1995 }
1996
Bram Moolenaard812df62008-11-09 12:46:09 +00001997#ifdef FEAT_EVAL
1998 if (fp_out == NULL && (flags & (VIF_GET_OLDFILES | VIF_FORCEIT)))
1999 {
2000 list = list_alloc();
2001 if (list != NULL)
2002 set_vim_var_list(VV_OLDFILES, list);
2003 }
2004#endif
2005
Bram Moolenaar071d4272004-06-13 20:20:40 +00002006 num_marked_files = get_viminfo_parameter('\'');
2007 while (!eof && (count < num_marked_files || fp_out == NULL))
2008 {
2009 if (line[0] != '>')
2010 {
2011 if (line[0] != '\n' && line[0] != '\r' && line[0] != '#')
2012 {
2013 if (viminfo_error("E576: ", _("Missing '>'"), line))
2014 break; /* too many errors, return now */
2015 }
2016 eof = vim_fgets(line, LSIZE, virp->vir_fd);
2017 continue; /* Skip this dud line */
2018 }
2019
2020 /*
2021 * Handle long line and translate escaped characters.
2022 * Find file name, set str to start.
2023 * Ignore leading and trailing white space.
2024 */
2025 str = skipwhite(line + 1);
2026 str = viminfo_readstring(virp, (int)(str - virp->vir_line), FALSE);
2027 if (str == NULL)
2028 continue;
2029 p = str + STRLEN(str);
2030 while (p != str && (*p == NUL || vim_isspace(*p)))
2031 p--;
2032 if (*p)
2033 p++;
2034 *p = NUL;
2035
Bram Moolenaard812df62008-11-09 12:46:09 +00002036#ifdef FEAT_EVAL
2037 if (list != NULL)
2038 list_append_string(list, str, -1);
2039#endif
2040
Bram Moolenaar071d4272004-06-13 20:20:40 +00002041 /*
2042 * If fp_out == NULL, load marks for current buffer.
2043 * If fp_out != NULL, copy marks for buffers not in buflist.
2044 */
2045 load_marks = copy_marks_out = FALSE;
2046 if (fp_out == NULL)
2047 {
Bram Moolenaard812df62008-11-09 12:46:09 +00002048 if ((flags & VIF_WANT_MARKS) && curbuf->b_ffname != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002049 {
2050 if (*name_buf == NUL) /* only need to do this once */
2051 home_replace(NULL, curbuf->b_ffname, name_buf, LSIZE, TRUE);
2052 if (fnamecmp(str, name_buf) == 0)
2053 load_marks = TRUE;
2054 }
2055 }
2056 else /* fp_out != NULL */
2057 {
2058 /* This is slow if there are many buffers!! */
Bram Moolenaar29323592016-07-24 22:04:11 +02002059 FOR_ALL_BUFFERS(buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002060 if (buf->b_ffname != NULL)
2061 {
2062 home_replace(NULL, buf->b_ffname, name_buf, LSIZE, TRUE);
2063 if (fnamecmp(str, name_buf) == 0)
2064 break;
2065 }
2066
2067 /*
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002068 * Copy marks if the buffer has not been loaded.
Bram Moolenaar071d4272004-06-13 20:20:40 +00002069 */
2070 if (buf == NULL || !buf->b_marks_read)
2071 {
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002072 int did_read_line = FALSE;
2073
2074 if (buflist_buf != NULL)
2075 {
2076 /* Read the next line. If it has the "*" mark compare the
2077 * time stamps. Write entries from "buflist" that are
2078 * newer. */
2079 if (!(eof = viminfo_readline(virp)) && line[0] == TAB)
2080 {
2081 did_read_line = TRUE;
2082 if (line[1] == '*')
2083 {
2084 long ltime;
2085
2086 sscanf((char *)line + 2, "%ld ", &ltime);
2087 while ((time_T)ltime < buflist_buf->b_last_used)
2088 {
2089 write_buffer_marks(buflist_buf, fp_out);
2090 if (++count >= num_marked_files)
2091 break;
2092 if (++buflist_used == buflist->ga_len)
2093 {
2094 buflist_buf = NULL;
2095 break;
2096 }
2097 buflist_buf =
2098 ((buf_T **)buflist->ga_data)[buflist_used];
2099 }
2100 }
2101 else
2102 {
2103 /* No timestamp, must be written by an older Vim.
2104 * Assume all remaining buffers are older then
2105 * ours. */
2106 while (count < num_marked_files
2107 && buflist_used < buflist->ga_len)
2108 {
2109 buflist_buf = ((buf_T **)buflist->ga_data)
2110 [buflist_used++];
2111 write_buffer_marks(buflist_buf, fp_out);
2112 ++count;
2113 }
2114 buflist_buf = NULL;
2115 }
2116
2117 if (count >= num_marked_files)
2118 {
2119 vim_free(str);
2120 break;
2121 }
2122 }
2123 }
2124
Bram Moolenaar071d4272004-06-13 20:20:40 +00002125 fputs("\n> ", fp_out);
2126 viminfo_writestring(fp_out, str);
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002127 if (did_read_line)
2128 fputs((char *)line, fp_out);
2129
Bram Moolenaar071d4272004-06-13 20:20:40 +00002130 count++;
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002131 copy_marks_out = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002132 }
2133 }
2134 vim_free(str);
2135
2136#ifdef FEAT_VIRTUALEDIT
2137 pos.coladd = 0;
2138#endif
2139 while (!(eof = viminfo_readline(virp)) && line[0] == TAB)
2140 {
2141 if (load_marks)
2142 {
2143 if (line[1] != NUL)
2144 {
Bram Moolenaare698add2011-02-25 15:11:22 +01002145 unsigned u;
2146
2147 sscanf((char *)line + 2, "%ld %u", &pos.lnum, &u);
2148 pos.col = u;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002149 switch (line[1])
2150 {
2151 case '"': curbuf->b_last_cursor = pos; break;
2152 case '^': curbuf->b_last_insert = pos; break;
2153 case '.': curbuf->b_last_change = pos; break;
2154 case '+':
2155#ifdef FEAT_JUMPLIST
2156 /* changelist positions are stored oldest
2157 * first */
2158 if (curbuf->b_changelistlen == JUMPLISTSIZE)
2159 /* list is full, remove oldest entry */
2160 mch_memmove(curbuf->b_changelist,
2161 curbuf->b_changelist + 1,
2162 sizeof(pos_T) * (JUMPLISTSIZE - 1));
2163 else
2164 ++curbuf->b_changelistlen;
2165 curbuf->b_changelist[
2166 curbuf->b_changelistlen - 1] = pos;
2167#endif
2168 break;
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002169
2170 /* Using the line number for the last-used
2171 * timestamp. */
2172 case '*': curbuf->b_last_used = pos.lnum; break;
2173
Bram Moolenaar071d4272004-06-13 20:20:40 +00002174 default: if ((i = line[1] - 'a') >= 0 && i < NMARKS)
2175 curbuf->b_namedm[i] = pos;
2176 }
2177 }
2178 }
2179 else if (copy_marks_out)
2180 fputs((char *)line, fp_out);
2181 }
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002182
Bram Moolenaar071d4272004-06-13 20:20:40 +00002183 if (load_marks)
2184 {
2185#ifdef FEAT_JUMPLIST
2186 win_T *wp;
2187
2188 FOR_ALL_WINDOWS(wp)
2189 {
2190 if (wp->w_buffer == curbuf)
2191 wp->w_changelistidx = curbuf->b_changelistlen;
2192 }
2193#endif
2194 break;
2195 }
2196 }
Bram Moolenaarab9c89b2016-07-03 17:47:26 +02002197
2198 if (fp_out != NULL)
2199 /* Write any remaining entries from buflist. */
2200 while (count < num_marked_files && buflist_used < buflist->ga_len)
2201 {
2202 buflist_buf = ((buf_T **)buflist->ga_data)[buflist_used++];
2203 write_buffer_marks(buflist_buf, fp_out);
2204 ++count;
2205 }
2206
Bram Moolenaar071d4272004-06-13 20:20:40 +00002207 vim_free(name_buf);
2208}
2209#endif /* FEAT_VIMINFO */