diff options
Diffstat (limited to 'src/nvim/mark.c')
-rw-r--r-- | src/nvim/mark.c | 737 |
1 files changed, 463 insertions, 274 deletions
diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 39f18b333d..66855c66b5 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -13,7 +13,9 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/cursor.h" #include "nvim/diff.h" +#include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/ex_cmds.h" #include "nvim/extmark.h" @@ -24,6 +26,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" #include "nvim/os/input.h" @@ -61,7 +64,8 @@ static xfmark_T namedfm[NGLOBALMARKS]; */ int setmark(int c) { - return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum); + fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_cursor); + return setmark_pos(c, &curwin->w_cursor, curbuf->b_fnum, &view); } /// Free fmark_T item @@ -90,9 +94,10 @@ void clear_fmark(fmark_T *fm) * When "c" is upper case use file "fnum". * Returns OK on success, FAIL if bad name given. */ -int setmark_pos(int c, pos_T *pos, int fnum) +int setmark_pos(int c, pos_T *pos, int fnum, fmarkv_T *view_pt) { int i; + fmarkv_T view = view_pt != NULL ? *view_pt : (fmarkv_T)INIT_FMARKV; // Check for a special key (may cause islower() to crash). if (c < 0) { @@ -117,7 +122,7 @@ int setmark_pos(int c, pos_T *pos, int fnum) } if (c == '"') { - RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum); + RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum, view); return OK; } @@ -147,7 +152,7 @@ int setmark_pos(int c, pos_T *pos, int fnum) if (ASCII_ISLOWER(c)) { i = c - 'a'; - RESET_FMARK(buf->b_namedm + i, *pos, fnum); + RESET_FMARK(buf->b_namedm + i, *pos, fnum, view); return OK; } if (ASCII_ISUPPER(c) || ascii_isdigit(c)) { @@ -156,7 +161,7 @@ int setmark_pos(int c, pos_T *pos, int fnum) } else { i = c - 'A'; } - RESET_XFMARK(namedfm + i, *pos, fnum, NULL); + RESET_XFMARK(namedfm + i, *pos, fnum, view, NULL); return OK; } return FAIL; @@ -171,7 +176,7 @@ void setpcmark(void) xfmark_T *fm; // for :global the mark is set only once - if (global_busy || listcmd_busy || cmdmod.keepjumps) { + if (global_busy || listcmd_busy || (cmdmod.cmod_flags & CMOD_KEEPJUMPS)) { return; } @@ -202,7 +207,8 @@ void setpcmark(void) curwin->w_jumplistidx = curwin->w_jumplistlen; fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1]; - SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, NULL); + fmarkv_T view = mark_view_make(curwin->w_topline, curwin->w_pcmark); + SET_XFMARK(fm, curwin->w_pcmark, curbuf->b_fnum, view, NULL); } /* @@ -217,249 +223,411 @@ void checkpcmark(void) && (equalpos(curwin->w_pcmark, curwin->w_cursor) || curwin->w_pcmark.lnum == 0)) { curwin->w_pcmark = curwin->w_prev_pcmark; - curwin->w_prev_pcmark.lnum = 0; // Show it has been checked } + curwin->w_prev_pcmark.lnum = 0; // it has been checked } -/* - * move "count" positions in the jump list (count may be negative) - */ -pos_T *movemark(int count) +/// Get mark in "count" position in the |jumplist| relative to the current index. +/// +/// If the mark is in a different buffer, it will be skipped unless the buffer exists. +/// +/// @note cleanup_jumplist() is run, which removes duplicate marks, and +/// changes win->w_jumplistidx. +/// @param[in] win window to get jumplist from. +/// @param[in] count count to move may be negative. +/// +/// @return mark, NULL if out of jumplist bounds. +fmark_T *get_jumplist(win_T *win, int count) { - pos_T *pos; - xfmark_T *jmp; + xfmark_T *jmp = NULL; - cleanup_jumplist(curwin, true); + cleanup_jumplist(win, true); - if (curwin->w_jumplistlen == 0) { // nothing to jump to - return (pos_T *)NULL; + if (win->w_jumplistlen == 0) { // nothing to jump to + return NULL; } for (;;) { - if (curwin->w_jumplistidx + count < 0 - || curwin->w_jumplistidx + count >= curwin->w_jumplistlen) { - return (pos_T *)NULL; + if (win->w_jumplistidx + count < 0 + || win->w_jumplistidx + count >= win->w_jumplistlen) { + return NULL; } - /* - * if first CTRL-O or CTRL-I command after a jump, add cursor position - * to list. Careful: If there are duplicates (CTRL-O immediately after - * starting Vim on a file), another entry may have been removed. - */ - if (curwin->w_jumplistidx == curwin->w_jumplistlen) { + // if first CTRL-O or CTRL-I command after a jump, add cursor position + // to list. Careful: If there are duplicates (CTRL-O immediately after + // starting Vim on a file), another entry may have been removed. + if (win->w_jumplistidx == win->w_jumplistlen) { setpcmark(); - --curwin->w_jumplistidx; // skip the new entry - if (curwin->w_jumplistidx + count < 0) { - return (pos_T *)NULL; + win->w_jumplistidx--; // skip the new entry + if (win->w_jumplistidx + count < 0) { + return NULL; } } - curwin->w_jumplistidx += count; + win->w_jumplistidx += count; - jmp = curwin->w_jumplist + curwin->w_jumplistidx; + jmp = win->w_jumplist + win->w_jumplistidx; if (jmp->fmark.fnum == 0) { + // Resolve the fnum (buff number) in the mark before returning it (shada) fname2fnum(jmp); } if (jmp->fmark.fnum != curbuf->b_fnum) { - // jump to other file - if (buflist_findnr(jmp->fmark.fnum) == NULL) { // Skip this one .. + // Needs to switch buffer, if it can't find it skip the mark + if (buflist_findnr(jmp->fmark.fnum) == NULL) { count += count < 0 ? -1 : 1; continue; } - if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum, - 0, FALSE) == FAIL) { - return (pos_T *)NULL; - } - // Set lnum again, autocommands my have changed it - curwin->w_cursor = jmp->fmark.mark; - pos = (pos_T *)-1; - } else { - pos = &(jmp->fmark.mark); } - return pos; + break; } + return &jmp->fmark; } -/* - * Move "count" positions in the changelist (count may be negative). - */ -pos_T *movechangelist(int count) +/// Get mark in "count" position in the |changelist| relative to the current index. +/// +/// @note Changes the win->w_changelistidx. +/// @param[in] win window to get jumplist from. +/// @param[in] count count to move may be negative. +/// +/// @return mark, NULL if out of bounds. +fmark_T *get_changelist(buf_T *buf, win_T *win, int count) { int n; + fmark_T *fm; - if (curbuf->b_changelistlen == 0) { // nothing to jump to - return (pos_T *)NULL; + if (buf->b_changelistlen == 0) { // nothing to jump to + return NULL; } - n = curwin->w_changelistidx; + n = win->w_changelistidx; if (n + count < 0) { if (n == 0) { - return (pos_T *)NULL; + return NULL; } n = 0; - } else if (n + count >= curbuf->b_changelistlen) { - if (n == curbuf->b_changelistlen - 1) { - return (pos_T *)NULL; + } else if (n + count >= buf->b_changelistlen) { + if (n == buf->b_changelistlen - 1) { + return NULL; } - n = curbuf->b_changelistlen - 1; + n = buf->b_changelistlen - 1; } else { n += count; } - curwin->w_changelistidx = n; - return &(curbuf->b_changelist[n].mark); + win->w_changelistidx = n; + fm = &(buf->b_changelist[n]); + // Changelist marks are always buffer local, Shada does not set it when loading + fm->fnum = curbuf->handle; + return &(buf->b_changelist[n]); } -/* - * Find mark "c" in buffer pointed to by "buf". - * If "changefile" is TRUE it's allowed to edit another file for '0, 'A, etc. - * If "fnum" is not NULL store the fnum there for '0, 'A etc., don't edit - * another file. - * Returns: - * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is - * in another file which can't be gotten. (caller needs to check lnum!) - * - NULL if there is no mark called 'c'. - * - -1 if mark is in other file and jumped there (only if changefile is TRUE) - */ -pos_T *getmark_buf(buf_T *buf, int c, bool changefile) +/// Get a named mark. +/// +/// All types of marks, even those that are not technically a mark will be returned as such. Use +/// mark_move_to() to move to the mark. +/// @note Some of the pointers are statically allocated, if in doubt make a copy. For more +/// information read mark_get_local(). +/// @param buf Buffer to get the mark from. +/// @param win Window to get or calculate the mark from (motion type marks, context mark). +/// @param fmp[out] Optional pointer to store the result in, as a workaround for the note above. +/// @param flag MarkGet value +/// @param name Name of the mark. +/// +/// @return Mark if found, otherwise NULL. For @c kMarkBufLocal, NULL is returned +/// when no mark is found in @a buf. +fmark_T *mark_get(buf_T *buf, win_T *win, fmark_T *fmp, MarkGet flag, int name) { - return getmark_buf_fnum(buf, c, changefile, NULL); + fmark_T *fm = NULL; + if (ASCII_ISUPPER(name) || ascii_isdigit(name)) { + // Global marks + xfmark_T *xfm = mark_get_global(!(flag & kMarkAllNoResolve), name); + fm = &xfm->fmark; + // Only wanted marks belonging to the buffer + if ((flag & kMarkBufLocal) && xfm->fmark.fnum != buf->handle) { + return NULL; + } + } else if (name > 0 && name < NMARK_LOCAL_MAX) { + // Local Marks + fm = mark_get_local(buf, win, name); + } + if (fmp != NULL && fm != NULL) { + *fmp = *fm; + return fmp; + } + return fm; } -pos_T *getmark(int c, bool changefile) +/// Get a global mark {A-Z0-9}. +/// +/// @param name the name of the mark. +/// @param resolve Whether to try resolving the mark fnum (i.e., load the buffer stored in +/// the mark fname and update the xfmark_T (expensive)). +/// +/// @return Mark +xfmark_T *mark_get_global(bool resolve, int name) { - return getmark_buf_fnum(curbuf, c, changefile, NULL); + xfmark_T *mark; + + if (ascii_isdigit(name)) { + name = name - '0' + NMARKS; + } else if (ASCII_ISUPPER(name)) { + name -= 'A'; + } else { + // Not a valid mark name + assert(false); + } + mark = &namedfm[name]; + + if (resolve && mark->fmark.fnum == 0) { + // Resolve filename to fnum (SHADA marks) + fname2fnum(mark); + } + return mark; } -pos_T *getmark_buf_fnum(buf_T *buf, int c, bool changefile, int *fnum) +/// Get a local mark (lowercase and symbols). +/// +/// Some marks are not actually marks, but positions that are never adjusted or motions presented as +/// marks. Search first for marks and fallback to finding motion type marks. If it's known +/// ahead of time that the mark is actually a motion use the mark_get_motion() directly. +/// +/// @note Lowercase, last_cursor '"', last insert '^', last change '.' are not statically +/// allocated, everything else is. +/// @param name the name of the mark. +/// @param win window to retrieve marks that belong to it (motions and context mark). +/// @param buf buf to retrieve marks that belong to it. +/// +/// @return Mark, NULL if not found. +fmark_T *mark_get_local(buf_T *buf, win_T *win, int name) { - pos_T *posp; - pos_T *startp, *endp; - static pos_T pos_copy; + fmark_T *mark = NULL; + if (ASCII_ISLOWER(name)) { + // normal named mark + mark = &buf->b_namedm[name - 'a']; + // to start of previous operator + } else if (name == '[') { + mark = pos_to_mark(buf, NULL, buf->b_op_start); + // to end of previous operator + } else if (name == ']') { + mark = pos_to_mark(buf, NULL, buf->b_op_end); + // visual marks + } else if (name == '<' || name == '>') { + mark = mark_get_visual(buf, name); + // previous context mark + } else if (name == '\'' || name == '`') { + // TODO(muniter): w_pcmark should be stored as a mark, but causes a nasty bug. + mark = pos_to_mark(curbuf, NULL, win->w_pcmark); + // to position when leaving buffer + } else if (name == '"') { + mark = &(buf->b_last_cursor); + // to where last Insert mode stopped + } else if (name == '^') { + mark = &(buf->b_last_insert); + // to where last change was made + } else if (name == '.') { + mark = &buf->b_last_change; + // Mark that are actually not marks but motions, e.g {, }, (, ), ... + } else { + mark = mark_get_motion(buf, win, name); + } - posp = NULL; + if (mark) { + mark->fnum = buf->b_fnum; + } - // Check for special key, can't be a mark name and might cause islower() - // to crash. - if (c < 0) { - return posp; - } - if (c > '~') { // check for islower()/isupper() - } else if (c == '\'' || c == '`') { // previous context mark - pos_copy = curwin->w_pcmark; // need to make a copy because - posp = &pos_copy; // w_pcmark may be changed soon - } else if (c == '"') { // to pos when leaving buffer - posp = &(buf->b_last_cursor.mark); - } else if (c == '^') { // to where Insert mode stopped - posp = &(buf->b_last_insert.mark); - } else if (c == '.') { // to where last change was made - posp = &(buf->b_last_change.mark); - } else if (c == '[') { // to start of previous operator - posp = &(buf->b_op_start); - } else if (c == ']') { // to end of previous operator - posp = &(buf->b_op_end); - } else if (c == '{' || c == '}') { // to previous/next paragraph - pos_T pos; + return mark; +} + +/// Get marks that are actually motions but return them as marks +/// +/// Gets the following motions as marks: '{', '}', '(', ')' +/// @param name name of the mark +/// @param win window to retrieve the cursor to calculate the mark. +/// @param buf buf to wrap motion marks with it's buffer number (fm->fnum). +/// +/// @return[static] Mark. +fmark_T *mark_get_motion(buf_T *buf, win_T *win, int name) +{ + fmark_T *mark = NULL; + const pos_T pos = curwin->w_cursor; + const bool slcb = listcmd_busy; + listcmd_busy = true; // avoid that '' is changed + if (name == '{' || name == '}') { // to previous/next paragraph oparg_T oa; - bool slcb = listcmd_busy; - - pos = curwin->w_cursor; - listcmd_busy = true; // avoid that '' is changed - if (findpar(&oa.inclusive, - c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE)) { - pos_copy = curwin->w_cursor; - posp = &pos_copy; + if (findpar(&oa.inclusive, name == '}' ? FORWARD : BACKWARD, 1L, NUL, false)) { + mark = pos_to_mark(buf, NULL, win->w_cursor); } - curwin->w_cursor = pos; - listcmd_busy = slcb; - } else if (c == '(' || c == ')') { // to previous/next sentence - pos_T pos; - bool slcb = listcmd_busy; - - pos = curwin->w_cursor; - listcmd_busy = true; // avoid that '' is changed - if (findsent(c == ')' ? FORWARD : BACKWARD, 1L)) { - pos_copy = curwin->w_cursor; - posp = &pos_copy; + } else if (name == '(' || name == ')') { // to previous/next sentence + if (findsent(name == ')' ? FORWARD : BACKWARD, 1L)) { + mark = pos_to_mark(buf, NULL, win->w_cursor); } - curwin->w_cursor = pos; - listcmd_busy = slcb; - } else if (c == '<' || c == '>') { // start/end of visual area - startp = &buf->b_visual.vi_start; - endp = &buf->b_visual.vi_end; - if (((c == '<') == lt(*startp, *endp) || endp->lnum == 0) - && startp->lnum != 0) { - posp = startp; + } + curwin->w_cursor = pos; + listcmd_busy = slcb; + return mark; +} + +/// Get visual marks '<', '>' +/// +/// This marks are different to normal marks: +/// 1. Never adjusted. +/// 2. Different behavior depending on editor state (visual mode). +/// 3. Not saved in shada. +/// 4. Re-ordered when defined in reverse. +/// @param buf Buffer to get the mark from. +/// @param name Mark name '<' or '>'. +/// +/// @return[static] Mark +fmark_T *mark_get_visual(buf_T *buf, int name) +{ + fmark_T *mark = NULL; + if (name == '<' || name == '>') { + // start/end of visual area + pos_T startp = buf->b_visual.vi_start; + pos_T endp = buf->b_visual.vi_end; + if (((name == '<') == lt(startp, endp) || endp.lnum == 0) + && startp.lnum != 0) { + mark = pos_to_mark(buf, NULL, startp); } else { - posp = endp; + mark = pos_to_mark(buf, NULL, endp); } - // For Visual line mode, set mark at begin or end of line - if (buf->b_visual.vi_mode == 'V') { - pos_copy = *posp; - posp = &pos_copy; - if (c == '<') { - pos_copy.col = 0; + if (mark != NULL && buf->b_visual.vi_mode == 'V') { + if (name == '<') { + mark->mark.col = 0; } else { - pos_copy.col = MAXCOL; + mark->mark.col = MAXCOL; } - pos_copy.coladd = 0; - } - } else if (ASCII_ISLOWER(c)) { // normal named mark - posp = &(buf->b_namedm[c - 'a'].mark); - } else if (ASCII_ISUPPER(c) || ascii_isdigit(c)) { // named file mark - if (ascii_isdigit(c)) { - c = c - '0' + NMARKS; - } else { - c -= 'A'; + mark->mark.coladd = 0; } - posp = &(namedfm[c].fmark.mark); + } + return mark; +} + +/// Wrap a pos_T into an fmark_T, used to abstract marks handling. +/// +/// Pass an fmp if multiple c +/// @note view fields are set to 0. +/// @param buf for fmark->fnum. +/// @param pos for fmrak->mark. +/// @param fmp pointer to save the mark. +/// +/// @return[static] Mark with the given information. +fmark_T *pos_to_mark(buf_T *buf, fmark_T *fmp, pos_T pos) +{ + static fmark_T fms = INIT_FMARK; + fmark_T *fm = fmp == NULL ? &fms : fmp; + fm->fnum = buf->handle; + fm->mark = pos; + return fm; +} - if (namedfm[c].fmark.fnum == 0) { - fname2fnum(&namedfm[c]); +/// Attempt to switch to the buffer of the given global mark +/// +/// @param fm +/// @param pcmark_on_switch leave a context mark when switching buffer. +/// @return whether the buffer was switched or not. +static MarkMoveRes switch_to_mark_buf(fmark_T *fm, bool pcmark_on_switch) +{ + bool res; + if (fm->fnum != curbuf->b_fnum) { + // Switch to another file. + int getfile_flag = pcmark_on_switch ? GETF_SETMARK : 0; + res = buflist_getfile(fm->fnum, (linenr_T)1, getfile_flag, false) == OK; + return res == true ? kMarkSwitchedBuf : kMarkMoveFailed; + } + return 0; +} + +/// Move to the given file mark, changing the buffer and cursor position. +/// +/// Validate the mark, switch to the buffer, and move the cursor. +/// @param fm Mark, can be NULL will raise E78: Unknown mark +/// @param flags MarkMove flags to configure the movement to the mark. +/// +/// @return MarkMovekRes flags representing the outcome +MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags) +{ + static fmark_T fm_copy = INIT_FMARK; + MarkMoveRes res = kMarkMoveSuccess; + if (!mark_check(fm)) { + res = kMarkMoveFailed; + goto end; + } + + if (fm->fnum != curbuf->handle) { + // Need to change buffer + fm_copy = *fm; // Copy, autocommand may change it + fm = &fm_copy; + res |= switch_to_mark_buf(fm, !(flags & kMarkJumpList)); + // Failed switching buffer + if (res & kMarkMoveFailed) { + goto end; + } + // Check line count now that the **destination buffer is loaded**. + if (!mark_check_line_bounds(curbuf, fm)) { + res |= kMarkMoveFailed; + goto end; } + } else if (flags & kMarkContext) { + // Doing it in this condition avoids double context mark when switching buffer. + setpcmark(); + } + // Move the cursor while keeping track of what changed for the caller + pos_T prev_pos = curwin->w_cursor; + pos_T pos = fm->mark; + curwin->w_cursor = fm->mark; + if (flags & kMarkBeginLine) { + beginline(BL_WHITE | BL_FIX); + } + res = prev_pos.lnum != pos.lnum ? res | kMarkChangedLine | kMarkChangedCursor : res; + res = prev_pos.col != pos.col ? res | kMarkChangedCol | kMarkChangedCursor : res; + if (flags & kMarkSetView) { + mark_view_restore(fm); + } - if (fnum != NULL) { - *fnum = namedfm[c].fmark.fnum; - } else if (namedfm[c].fmark.fnum != buf->b_fnum) { - // mark is in another file - posp = &pos_copy; - - if (namedfm[c].fmark.mark.lnum != 0 - && changefile && namedfm[c].fmark.fnum) { - if (buflist_getfile(namedfm[c].fmark.fnum, - (linenr_T)1, GETF_SETMARK, FALSE) == OK) { - // Set the lnum now, autocommands could have changed it - curwin->w_cursor = namedfm[c].fmark.mark; - return (pos_T *)-1; - } - pos_copy.lnum = -1; // can't get file - } else { - pos_copy.lnum = 0; // mark exists, but is not valid in current buffer - } + if (res & kMarkSwitchedBuf || res & kMarkChangedCursor) { + check_cursor(); + } +end: + return res; +} + +/// Restore the mark view. +/// By remembering the offset between topline and mark lnum at the time of +/// definition, this function restores the "view". +/// @note Assumes the mark has been checked, is valid. +/// @param fm the named mark. +void mark_view_restore(fmark_T *fm) +{ + if (fm != NULL && fm->view.topline_offset >= 0) { + linenr_T topline = fm->mark.lnum - fm->view.topline_offset; + // If the mark does not have a view, topline_offset is MAXLNUM, + // and this check can prevent restoring mark view in that case. + if (topline >= 1) { + set_topline(curwin, topline); } } +} - return posp; +fmarkv_T mark_view_make(linenr_T topline, pos_T pos) +{ + return (fmarkv_T){ pos.lnum - topline }; } -/// Search for the next named mark in the current file. +/// Search for the next named mark in the current file from a start position. /// -/// @param startpos where to start -/// @param dir direction for search +/// @param startpos where to start. +/// @param dir direction for search. /// -/// @return pointer to pos_T of the next mark or NULL if no mark is found. -pos_T *getnextmark(pos_T *startpos, int dir, int begin_line) +/// @return next mark or NULL if no mark is found. +fmark_T *getnextmark(pos_T *startpos, int dir, int begin_line) { int i; - pos_T *result = NULL; + fmark_T *result = NULL; pos_T pos; pos = *startpos; - // When searching backward and leaving the cursor on the first non-blank, - // position must be in a previous line. - // When searching forward and leaving the cursor on the first non-blank, - // position must be in a next line. if (dir == BACKWARD && begin_line) { pos.col = 0; } else if (dir == FORWARD && begin_line) { @@ -469,14 +637,14 @@ pos_T *getnextmark(pos_T *startpos, int dir, int begin_line) for (i = 0; i < NMARKS; i++) { if (curbuf->b_namedm[i].mark.lnum > 0) { if (dir == FORWARD) { - if ((result == NULL || lt(curbuf->b_namedm[i].mark, *result)) + if ((result == NULL || lt(curbuf->b_namedm[i].mark, result->mark)) && lt(pos, curbuf->b_namedm[i].mark)) { - result = &curbuf->b_namedm[i].mark; + result = &curbuf->b_namedm[i]; } } else { - if ((result == NULL || lt(*result, curbuf->b_namedm[i].mark)) + if ((result == NULL || lt(result->mark, curbuf->b_namedm[i].mark)) && lt(curbuf->b_namedm[i].mark, pos)) { - result = &curbuf->b_namedm[i].mark; + result = &curbuf->b_namedm[i]; } } } @@ -518,7 +686,7 @@ static void fname2fnum(xfmark_T *fm) p = path_shorten_fname(NameBuff, IObuff); // buflist_new() will call fmarks_check_names() - (void)buflist_new(NameBuff, p, (linenr_T)1, 0); + (void)buflist_new((char *)NameBuff, (char *)p, (linenr_T)1, 0); } } @@ -529,7 +697,7 @@ static void fname2fnum(xfmark_T *fm) */ void fmarks_check_names(buf_T *buf) { - char_u *name = buf->b_ffname; + char_u *name = (char_u *)buf->b_ffname; int i; if (buf->b_ffname == NULL) { @@ -551,35 +719,54 @@ static void fmarks_check_one(xfmark_T *fm, char_u *name, buf_T *buf) { if (fm->fmark.fnum == 0 && fm->fname != NULL - && fnamecmp(name, fm->fname) == 0) { + && FNAMECMP(name, fm->fname) == 0) { fm->fmark.fnum = buf->b_fnum; XFREE_CLEAR(fm->fname); } } -/* - * Check a if a position from a mark is valid. - * Give and error message and return FAIL if not. - */ -int check_mark(pos_T *pos) +/// Check the position in @a fm is valid. +/// +/// Emit error message and return accordingly. +/// +/// Checks for: +/// - NULL raising unknown mark error. +/// - Line number <= 0 raising mark not set. +/// - Line number > buffer line count, raising invalid mark. +/// @param fm[in] File mark to check. +/// +/// @return true if the mark passes all the above checks, else false. +bool mark_check(fmark_T *fm) { - if (pos == NULL) { + if (fm == NULL) { emsg(_(e_umark)); - return FAIL; - } - if (pos->lnum <= 0) { - // lnum is negative if mark is in another file can can't get that - // file, error message already give then. - if (pos->lnum == 0) { + return false; + } else if (fm->mark.lnum <= 0) { + // In both cases it's an error but only raise when equals to 0 + if (fm->mark.lnum == 0) { emsg(_(e_marknotset)); } - return FAIL; + return false; } - if (pos->lnum > curbuf->b_ml.ml_line_count) { + // Only check for valid line number if the buffer is loaded. + if (fm->fnum == curbuf->handle && !mark_check_line_bounds(curbuf, fm)) { + return false; + } + return true; +} + +/// Check if a mark line number is greater than the buffer line count, and set e_markinval. +/// @note Should be done after the buffer is loaded into memory. +/// @param buf Buffer where the mark is set. +/// @param fm Mark to check. +/// @return true if below line count else false. +bool mark_check_line_bounds(buf_T *buf, fmark_T *fm) +{ + if (buf != NULL && fm->mark.lnum > buf->b_ml.ml_line_count) { emsg(_(e_markinval)); - return FAIL; + return false; } - return OK; + return true; } /// Clear all marks and change list in the given buffer @@ -615,7 +802,7 @@ char_u *fm_getname(fmark_T *fmark, int lead_len) if (fmark->fnum == curbuf->b_fnum) { // current buffer return mark_line(&(fmark->mark), lead_len); } - return buflist_nr2name(fmark->fnum, FALSE, TRUE); + return (char_u *)buflist_nr2name(fmark->fnum, false, true); } /* @@ -630,14 +817,14 @@ static char_u *mark_line(pos_T *mp, int lead_len) if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count) { return vim_strsave((char_u *)"-invalid-"); } - assert(Columns >= 0 && (size_t)Columns <= SIZE_MAX); + assert(Columns >= 0); // Allow for up to 5 bytes per character. - s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (size_t)Columns * 5); + s = vim_strnsave((char_u *)skipwhite((char *)ml_get(mp->lnum)), (size_t)Columns * 5); // Truncate the line to fit it in the window len = 0; for (p = s; *p != NUL; MB_PTR_ADV(p)) { - len += ptr2cells(p); + len += ptr2cells((char *)p); if (len >= Columns - lead_len) { break; } @@ -651,7 +838,7 @@ static char_u *mark_line(pos_T *mp, int lead_len) */ void ex_marks(exarg_T *eap) { - char_u *arg = eap->arg; + char_u *arg = (char_u *)eap->arg; int i; char_u *name; pos_T *posp, *startp, *endp; @@ -668,7 +855,7 @@ void ex_marks(exarg_T *eap) if (namedfm[i].fmark.fnum != 0) { name = fm_getname(&namedfm[i].fmark, 15); } else { - name = namedfm[i].fname; + name = (char_u *)namedfm[i].fname; } if (name != NULL) { show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A', @@ -717,7 +904,7 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu } } } else if (!got_int - && (arg == NULL || vim_strchr(arg, c) != NULL) + && (arg == NULL || vim_strchr((char *)arg, c) != NULL) && p->lnum != 0) { // don't output anything if 'q' typed at --more-- prompt if (name == NULL && current) { @@ -732,8 +919,8 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu } msg_putchar('\n'); if (!got_int) { - snprintf((char *)IObuff, IOSIZE, " %c %6ld %4d ", c, p->lnum, p->col); - msg_outtrans(IObuff); + snprintf((char *)IObuff, IOSIZE, " %c %6" PRIdLINENR " %4d ", c, p->lnum, p->col); + msg_outtrans((char *)IObuff); if (name != NULL) { msg_outtrans_attr(name, current ? HL_ATTR(HLF_D) : 0); } @@ -767,7 +954,7 @@ void ex_delmarks(exarg_T *eap) emsg(_(e_argreq)); } else { // clear specified marks only - for (p = eap->arg; *p != NUL; ++p) { + for (p = (char_u *)eap->arg; *p != NUL; p++) { lower = ASCII_ISLOWER(*p); digit = ascii_isdigit(*p); if (lower || digit || ASCII_ISUPPER(*p)) { @@ -844,6 +1031,11 @@ void ex_jumps(exarg_T *eap) if (curwin->w_jumplist[i].fmark.mark.lnum != 0) { name = fm_getname(&curwin->w_jumplist[i].fmark, 16); + // Make sure to output the current indicator, even when on an wiped + // out buffer. ":filter" may still skip it. + if (name == NULL && i == curwin->w_jumplistidx) { + name = vim_strsave((char_u *)"-invalid-"); + } // apply :filter /pat/ or file name not available if (name == NULL || message_filtered(name)) { xfree(name); @@ -855,13 +1047,11 @@ void ex_jumps(exarg_T *eap) xfree(name); break; } - sprintf((char *)IObuff, "%c %2d %5ld %4d ", - i == curwin->w_jumplistidx ? '>' : ' ', - i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx - : curwin->w_jumplistidx - i, - curwin->w_jumplist[i].fmark.mark.lnum, - curwin->w_jumplist[i].fmark.mark.col); - msg_outtrans(IObuff); + snprintf((char *)IObuff, IOSIZE, "%c %2d %5" PRIdLINENR " %4d ", + i == curwin->w_jumplistidx ? '>' : ' ', + i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx : curwin->w_jumplistidx - i, + curwin->w_jumplist[i].fmark.mark.lnum, curwin->w_jumplist[i].fmark.mark.col); + msg_outtrans((char *)IObuff); msg_outtrans_attr(name, curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum ? HL_ATTR(HLF_D) : 0); @@ -905,7 +1095,7 @@ void ex_changes(exarg_T *eap) : curwin->w_changelistidx - i, (long)curbuf->b_changelist[i].mark.lnum, curbuf->b_changelist[i].mark.col); - msg_outtrans(IObuff); + msg_outtrans((char *)IObuff); name = mark_line(&curbuf->b_changelist[i].mark, 17); msg_outtrans_attr(name, HL_ATTR(HLF_D)); xfree(name); @@ -918,7 +1108,7 @@ void ex_changes(exarg_T *eap) } } -#define one_adjust(add) \ +#define ONE_ADJUST(add) \ { \ lp = add; \ if (*lp >= line1 && *lp <= line2) \ @@ -933,7 +1123,7 @@ void ex_changes(exarg_T *eap) } // don't delete the line, just put at first deleted line -#define one_adjust_nodel(add) \ +#define ONE_ADJUST_NODEL(add) \ { \ lp = add; \ if (*lp >= line1 && *lp <= line2) \ @@ -958,7 +1148,8 @@ void ex_changes(exarg_T *eap) * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0); * or: mark_adjust(56, 55, MAXLNUM, 2); */ -void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after, ExtmarkOp op) +void mark_adjust(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after, + ExtmarkOp op) { mark_adjust_internal(line1, line2, amount, amount_after, true, op); } @@ -968,14 +1159,14 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after, // This is only useful when folds need to be moved in a way different to // calling foldMarkAdjust() with arguments line1, line2, amount, amount_after, // for an example of why this may be necessary, see do_move(). -void mark_adjust_nofold(linenr_T line1, linenr_T line2, long amount, long amount_after, +void mark_adjust_nofold(linenr_T line1, linenr_T line2, linenr_T amount, linenr_T amount_after, ExtmarkOp op) { mark_adjust_internal(line1, line2, amount, amount_after, false, op); } -static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, long amount_after, - bool adjust_folds, ExtmarkOp op) +static void mark_adjust_internal(linenr_T line1, linenr_T line2, linenr_T amount, + linenr_T amount_after, bool adjust_folds, ExtmarkOp op) { int i; int fnum = curbuf->b_fnum; @@ -986,40 +1177,39 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo return; } - if (!cmdmod.lockmarks) { + if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { // named marks, lower case and upper case for (i = 0; i < NMARKS; i++) { - one_adjust(&(curbuf->b_namedm[i].mark.lnum)); + ONE_ADJUST(&(curbuf->b_namedm[i].mark.lnum)); if (namedfm[i].fmark.fnum == fnum) { - one_adjust_nodel(&(namedfm[i].fmark.mark.lnum)); + ONE_ADJUST_NODEL(&(namedfm[i].fmark.mark.lnum)); } } for (i = NMARKS; i < NGLOBALMARKS; i++) { if (namedfm[i].fmark.fnum == fnum) { - one_adjust_nodel(&(namedfm[i].fmark.mark.lnum)); + ONE_ADJUST_NODEL(&(namedfm[i].fmark.mark.lnum)); } } // last Insert position - one_adjust(&(curbuf->b_last_insert.mark.lnum)); + ONE_ADJUST(&(curbuf->b_last_insert.mark.lnum)); // last change position - one_adjust(&(curbuf->b_last_change.mark.lnum)); + ONE_ADJUST(&(curbuf->b_last_change.mark.lnum)); // last cursor position, if it was set if (!equalpos(curbuf->b_last_cursor.mark, initpos)) { - one_adjust(&(curbuf->b_last_cursor.mark.lnum)); + ONE_ADJUST(&(curbuf->b_last_cursor.mark.lnum)); } - // list of change positions - for (i = 0; i < curbuf->b_changelistlen; ++i) { - one_adjust_nodel(&(curbuf->b_changelist[i].mark.lnum)); + for (i = 0; i < curbuf->b_changelistlen; i++) { + ONE_ADJUST_NODEL(&(curbuf->b_changelist[i].mark.lnum)); } // Visual area - one_adjust_nodel(&(curbuf->b_visual.vi_start.lnum)); - one_adjust_nodel(&(curbuf->b_visual.vi_end.lnum)); + ONE_ADJUST_NODEL(&(curbuf->b_visual.vi_start.lnum)); + ONE_ADJUST_NODEL(&(curbuf->b_visual.vi_end.lnum)); // quickfix marks if (!qf_mark_adjust(NULL, line1, line2, amount, amount_after)) { @@ -1042,44 +1232,44 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo } // previous context mark - one_adjust(&(curwin->w_pcmark.lnum)); + ONE_ADJUST(&(curwin->w_pcmark.lnum)); // previous pcmark - one_adjust(&(curwin->w_prev_pcmark.lnum)); + ONE_ADJUST(&(curwin->w_prev_pcmark.lnum)); // saved cursor for formatting if (saved_cursor.lnum != 0) { - one_adjust_nodel(&(saved_cursor.lnum)); + ONE_ADJUST_NODEL(&(saved_cursor.lnum)); } /* * Adjust items in all windows related to the current buffer. */ FOR_ALL_TAB_WINDOWS(tab, win) { - if (!cmdmod.lockmarks) { + if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { // Marks in the jumplist. When deleting lines, this may create // duplicate marks in the jumplist, they will be removed later. for (i = 0; i < win->w_jumplistlen; i++) { if (win->w_jumplist[i].fmark.fnum == fnum) { - one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum)); + ONE_ADJUST_NODEL(&(win->w_jumplist[i].fmark.mark.lnum)); } } } if (win->w_buffer == curbuf) { - if (!cmdmod.lockmarks) { + if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) { // marks in the tag stack for (i = 0; i < win->w_tagstacklen; i++) { if (win->w_tagstack[i].fmark.fnum == fnum) { - one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum)); + ONE_ADJUST_NODEL(&(win->w_tagstack[i].fmark.mark.lnum)); } } } // the displayed Visual area if (win->w_old_cursor_lnum != 0) { - one_adjust_nodel(&(win->w_old_cursor_lnum)); - one_adjust_nodel(&(win->w_old_visual_lnum)); + ONE_ADJUST_NODEL(&(win->w_old_cursor_lnum)); + ONE_ADJUST_NODEL(&(win->w_old_visual_lnum)); } // topline and cursor position for windows with the same buffer @@ -1127,14 +1317,14 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo } // This code is used often, needs to be fast. -#define col_adjust(pp) \ +#define COL_ADJUST(pp) \ { \ posp = pp; \ if (posp->lnum == lnum && posp->col >= mincol) \ { \ posp->lnum += lnum_amount; \ assert(col_amount > INT_MIN && col_amount <= INT_MAX); \ - if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) { \ + if (col_amount < 0 && posp->col <= (colnr_T) - col_amount) { \ posp->col = 0; \ } else if (posp->col < spaces_removed) { \ posp->col = (int)col_amount + spaces_removed; \ @@ -1149,52 +1339,52 @@ static void mark_adjust_internal(linenr_T line1, linenr_T line2, long amount, lo // position. // "spaces_removed" is the number of spaces that were removed, matters when the // cursor is inside them. -void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_amount, +void mark_col_adjust(linenr_T lnum, colnr_T mincol, linenr_T lnum_amount, long col_amount, int spaces_removed) { int i; int fnum = curbuf->b_fnum; pos_T *posp; - if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks) { + if ((col_amount == 0L && lnum_amount == 0L) || (cmdmod.cmod_flags & CMOD_LOCKMARKS)) { return; // nothing to do } // named marks, lower case and upper case for (i = 0; i < NMARKS; i++) { - col_adjust(&(curbuf->b_namedm[i].mark)); + COL_ADJUST(&(curbuf->b_namedm[i].mark)); if (namedfm[i].fmark.fnum == fnum) { - col_adjust(&(namedfm[i].fmark.mark)); + COL_ADJUST(&(namedfm[i].fmark.mark)); } } for (i = NMARKS; i < NGLOBALMARKS; i++) { if (namedfm[i].fmark.fnum == fnum) { - col_adjust(&(namedfm[i].fmark.mark)); + COL_ADJUST(&(namedfm[i].fmark.mark)); } } // last Insert position - col_adjust(&(curbuf->b_last_insert.mark)); + COL_ADJUST(&(curbuf->b_last_insert.mark)); // last change position - col_adjust(&(curbuf->b_last_change.mark)); + COL_ADJUST(&(curbuf->b_last_change.mark)); // list of change positions - for (i = 0; i < curbuf->b_changelistlen; ++i) { - col_adjust(&(curbuf->b_changelist[i].mark)); + for (i = 0; i < curbuf->b_changelistlen; i++) { + COL_ADJUST(&(curbuf->b_changelist[i].mark)); } // Visual area - col_adjust(&(curbuf->b_visual.vi_start)); - col_adjust(&(curbuf->b_visual.vi_end)); + COL_ADJUST(&(curbuf->b_visual.vi_start)); + COL_ADJUST(&(curbuf->b_visual.vi_end)); // previous context mark - col_adjust(&(curwin->w_pcmark)); + COL_ADJUST(&(curwin->w_pcmark)); // previous pcmark - col_adjust(&(curwin->w_prev_pcmark)); + COL_ADJUST(&(curwin->w_prev_pcmark)); // saved cursor for formatting - col_adjust(&saved_cursor); + COL_ADJUST(&saved_cursor); /* * Adjust items in all windows related to the current buffer. @@ -1203,7 +1393,7 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a // marks in the jumplist for (i = 0; i < win->w_jumplistlen; ++i) { if (win->w_jumplist[i].fmark.fnum == fnum) { - col_adjust(&(win->w_jumplist[i].fmark.mark)); + COL_ADJUST(&(win->w_jumplist[i].fmark.mark)); } } @@ -1211,13 +1401,13 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, long lnum_amount, long col_a // marks in the tag stack for (i = 0; i < win->w_tagstacklen; i++) { if (win->w_tagstack[i].fmark.fnum == fnum) { - col_adjust(&(win->w_tagstack[i].fmark.mark)); + COL_ADJUST(&(win->w_tagstack[i].fmark.mark)); } } // cursor position for other windows with the same buffer if (win != curwin) { - col_adjust(&win->w_cursor); + COL_ADJUST(&win->w_cursor); } } } @@ -1306,7 +1496,7 @@ void copy_jumplist(win_T *from, win_T *to) for (i = 0; i < from->w_jumplistlen; ++i) { to->w_jumplist[i] = from->w_jumplist[i]; if (from->w_jumplist[i].fname != NULL) { - to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname); + to->w_jumplist[i].fname = xstrdup(from->w_jumplist[i].fname); } } to->w_jumplistlen = from->w_jumplistlen; @@ -1315,7 +1505,7 @@ void copy_jumplist(win_T *from, win_T *to) /// Iterate over jumplist items /// -/// @warning No jumplist-editing functions must be run while iteration is in +/// @warning No jumplist-editing functions must be called while iteration is in /// progress. /// /// @param[in] iter Iterator. Pass NULL to start iteration. @@ -1328,7 +1518,7 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT { if (iter == NULL && win->w_jumplistlen == 0) { - *fm = (xfmark_T) { { { 0, 0, 0 }, 0, 0, NULL }, NULL }; + *fm = (xfmark_T)INIT_XFMARK; return NULL; } const xfmark_T *const iter_mark = @@ -1345,7 +1535,7 @@ const void *mark_jumplist_iter(const void *const iter, const win_T *const win, x /// Iterate over global marks /// -/// @warning No mark-editing functions must be run while iteration is in +/// @warning No mark-editing functions must be called while iteration is in /// progress. /// /// @param[in] iter Iterator. Pass NULL to start iteration. @@ -1419,7 +1609,7 @@ static inline const fmark_T *next_buffer_mark(const buf_T *const buf, char *cons /// Iterate over buffer marks /// -/// @warning No mark-editing functions must be run while iteration is in +/// @warning No mark-editing functions must be called while iteration is in /// progress. /// /// @param[in] iter Iterator. Pass NULL to start iteration. @@ -1536,7 +1726,7 @@ void free_jumplist(win_T *wp) void set_last_cursor(win_T *win) { if (win->w_buffer != NULL) { - RESET_FMARK(&win->w_buffer->b_last_cursor, win->w_cursor, 0); + RESET_FMARK(&win->w_buffer->b_last_cursor, win->w_cursor, 0, ((fmarkv_T)INIT_FMARKV)); } } @@ -1574,14 +1764,13 @@ void mark_mb_adjustpos(buf_T *buf, pos_T *lp) // double-wide character. if (lp->coladd == 1 && p[lp->col] != TAB - && vim_isprintc(utf_ptr2char(p + lp->col)) - && ptr2cells(p + lp->col) > 1) { + && vim_isprintc(utf_ptr2char((char *)p + lp->col)) + && ptr2cells((char *)p + lp->col) > 1) { lp->coladd = 0; } } } - // Add information about mark 'mname' to list 'l' static int add_mark(list_T *l, const char *mname, const pos_T *pos, int bufnr, const char *fname) FUNC_ATTR_NONNULL_ARG(1, 2, 3) @@ -1609,7 +1798,6 @@ static int add_mark(list_T *l, const char *mname, const pos_T *pos, int bufnr, c return OK; } - /// Get information about marks local to a buffer. /// /// @param[in] buf Buffer to get the marks from @@ -1639,9 +1827,10 @@ void get_buf_local_marks(const buf_T *buf, list_T *l) /// Get a global mark /// +/// @note Mark might not have it's fnum resolved. /// @param[in] Name of named mark /// @param[out] Global/file mark -xfmark_T get_global_mark(char name) +xfmark_T get_raw_global_mark(char name) { return namedfm[mark_global_index(name)]; } @@ -1658,9 +1847,9 @@ void get_global_marks(list_T *l) // Marks 'A' to 'Z' and '0' to '9' for (int i = 0; i < NMARKS + EXTRA_MARKS; i++) { if (namedfm[i].fmark.fnum != 0) { - name = (char *)buflist_nr2name(namedfm[i].fmark.fnum, true, true); + name = buflist_nr2name(namedfm[i].fmark.fnum, true, true); } else { - name = (char *)namedfm[i].fname; + name = namedfm[i].fname; } if (name != NULL) { mname[1] = i >= NMARKS ? (char)(i - NMARKS + '0') : (char)(i + 'A'); |