commit ea2d226df6f344451306274f3f99911d459fb9dd
parent 8ee82da3cfd228d50816aeb5f5db22175f5a4ede
Author: zeertzjq <zeertzjq@outlook.com>
Date: Mon, 21 Jul 2025 12:02:05 +0800
Merge pull request #34894 from janlazo/vim-8.1.0857
vim-patch:8.1.{770,857,914,977,1004,1526,1551,1565,1584,1629,1641,1703,1728,1730,1736,1802,1853,1891,2127,2200},8.2.3922,9.0.{546,928},9.1.1382
Diffstat:
38 files changed, 950 insertions(+), 928 deletions(-)
diff --git a/runtime/doc/vimeval.txt b/runtime/doc/vimeval.txt
@@ -2116,7 +2116,6 @@ text...
# Number
* Funcref
-
:unl[et][!] {name} ... *:unlet* *:unl* *E108* *E795*
Remove the internal variable {name}. Several variable
names can be given, they are all removed. The name
diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt
@@ -727,6 +727,19 @@ v:version
:if has("nvim-0.2.1")
<
+ *v:versionlong* *versionlong-variable*
+v:versionlong
+ Like v:version, but also including the patchlevel in the last
+ four digits. Version 8.1 with patch 123 has value 8010123.
+ This can be used like this: >
+ if v:versionlong >= 8010123
+<
+ However, if there are gaps in the list of patches included
+ this will not work well. This can happen if a recent patch
+ was included into an older version, e.g. for a security fix.
+ Use the has() function to make sure the patch is actually
+ included.
+
*v:vim_did_enter* *vim_did_enter-variable*
v:vim_did_enter
0 during startup, 1 just before |VimEnter|.
diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua
@@ -772,6 +772,21 @@ vim.v.val = ...
--- @type integer
vim.v.version = ...
+--- Like v:version, but also including the patchlevel in the last
+--- four digits. Version 8.1 with patch 123 has value 8010123.
+--- This can be used like this:
+--- ```
+--- if v:versionlong >= 8010123
+--- ```
+---
+--- However, if there are gaps in the list of patches included
+--- this will not work well. This can happen if a recent patch
+--- was included into an older version, e.g. for a security fix.
+--- Use the has() function to make sure the patch is actually
+--- included.
+--- @type integer
+vim.v.versionlong = ...
+
--- 0 during startup, 1 just before `VimEnter`.
--- Read-only.
--- @type integer
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
@@ -1566,7 +1566,7 @@ bool has_event(event_T event) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
/// Return true when there is a CursorHold/CursorHoldI autocommand defined for
/// the current mode.
-bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+static bool has_cursorhold(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
return has_event((get_real_state() == MODE_NORMAL_BUSY ? EVENT_CURSORHOLD : EVENT_CURSORHOLDI));
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
@@ -1683,7 +1683,7 @@ void set_curbuf(buf_T *buf, int action, bool update_jumplist)
/// Enter a new current buffer.
/// Old curbuf must have been abandoned already! This also means "curbuf" may
/// be pointing to freed memory.
-void enter_buffer(buf_T *buf)
+static void enter_buffer(buf_T *buf)
{
// when closing the current buffer stop Visual mode
if (VIsual_active
@@ -2207,7 +2207,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
}
/// Go to the last known line number for the current buffer.
-void buflist_getfpos(void)
+static void buflist_getfpos(void)
{
pos_T *fpos = &buflist_findfmark(curbuf)->mark;
diff --git a/src/nvim/change.c b/src/nvim/change.c
@@ -957,118 +957,6 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
return OK;
}
-/// Copy the indent from ptr to the current line (and fill to size).
-/// Leaves the cursor on the first non-blank in the line.
-///
-/// @return true if the line was changed.
-bool copy_indent(int size, char *src)
-{
- char *p = NULL;
- char *line = NULL;
- int ind_len;
- int line_len = 0;
- int tab_pad;
-
- // Round 1: compute the number of characters needed for the indent
- // Round 2: copy the characters.
- for (int round = 1; round <= 2; round++) {
- int todo = size;
- ind_len = 0;
- int ind_done = 0;
- int ind_col = 0;
- char *s = src;
-
- // Count/copy the usable portion of the source line.
- while (todo > 0 && ascii_iswhite(*s)) {
- if (*s == TAB) {
- tab_pad = tabstop_padding(ind_done,
- curbuf->b_p_ts,
- curbuf->b_p_vts_array);
-
- // Stop if this tab will overshoot the target.
- if (todo < tab_pad) {
- break;
- }
- todo -= tab_pad;
- ind_done += tab_pad;
- ind_col += tab_pad;
- } else {
- todo--;
- ind_done++;
- ind_col++;
- }
- ind_len++;
-
- if (p != NULL) {
- *p++ = *s;
- }
- s++;
- }
-
- // Fill to next tabstop with a tab, if possible.
- tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_array);
-
- if ((todo >= tab_pad) && !curbuf->b_p_et) {
- todo -= tab_pad;
- ind_len++;
- ind_col += tab_pad;
-
- if (p != NULL) {
- *p++ = TAB;
- }
- }
-
- // Add tabs required for indent.
- if (!curbuf->b_p_et) {
- while (true) {
- tab_pad = tabstop_padding(ind_col,
- curbuf->b_p_ts,
- curbuf->b_p_vts_array);
- if (todo < tab_pad) {
- break;
- }
- todo -= tab_pad;
- ind_len++;
- ind_col += tab_pad;
- if (p != NULL) {
- *p++ = TAB;
- }
- }
- }
-
- // Count/add spaces required for indent.
- while (todo > 0) {
- todo--;
- ind_len++;
-
- if (p != NULL) {
- *p++ = ' ';
- }
- }
-
- if (p == NULL) {
- // Allocate memory for the result: the copied indent, new indent
- // and the rest of the line.
- line_len = get_cursor_line_len() + 1;
- assert(ind_len + line_len >= 0);
- size_t line_size;
- STRICT_ADD(ind_len, line_len, &line_size, size_t);
- line = xmalloc(line_size);
- p = line;
- }
- }
-
- // Append the original line
- memmove(p, get_cursor_line_ptr(), (size_t)line_len);
-
- // Replace the line
- ml_replace(curwin->w_cursor.lnum, line, false);
-
- // Put the cursor after the indent.
- curwin->w_cursor.col = ind_len;
- return true;
-}
-
/// open_line: Add a new line below or above the current line.
///
/// For MODE_VREPLACE state, we only add a new line when we get to the end of
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
@@ -17,6 +17,7 @@
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
+#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
@@ -24,6 +25,7 @@
#include "nvim/event/rstream.h"
#include "nvim/event/socket.h"
#include "nvim/event/wstream.h"
+#include "nvim/ex_cmds.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
@@ -1001,3 +1003,65 @@ Array channel_all_info(Arena *arena)
}
return ret;
}
+
+/// "prompt_setcallback({buffer}, {callback})" function
+void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ Callback prompt_callback = { .type = kCallbackNone };
+
+ if (check_secure()) {
+ return;
+ }
+ buf_T *buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) {
+ if (!callback_from_typval(&prompt_callback, &argvars[1])) {
+ return;
+ }
+ }
+
+ callback_free(&buf->b_prompt_callback);
+ buf->b_prompt_callback = prompt_callback;
+}
+
+/// "prompt_setinterrupt({buffer}, {callback})" function
+void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ Callback interrupt_callback = { .type = kCallbackNone };
+
+ if (check_secure()) {
+ return;
+ }
+ buf_T *buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) {
+ if (!callback_from_typval(&interrupt_callback, &argvars[1])) {
+ return;
+ }
+ }
+
+ callback_free(&buf->b_prompt_interrupt);
+ buf->b_prompt_interrupt = interrupt_callback;
+}
+
+/// "prompt_setprompt({buffer}, {text})" function
+void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (check_secure()) {
+ return;
+ }
+ buf_T *buf = tv_get_buf(&argvars[0], false);
+ if (buf == NULL) {
+ return;
+ }
+
+ const char *text = tv_get_string(&argvars[1]);
+ xfree(buf->b_prompt_text);
+ buf->b_prompt_text = xstrdup(text);
+}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
@@ -1791,7 +1791,7 @@ void digraph_getlist_common(bool list_all, typval_T *rettv)
}
}
-struct dg_header_entry {
+static struct dg_header_entry {
int dg_start;
const char *dg_header;
} header_table[] = {
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
@@ -1636,204 +1636,6 @@ void undisplay_dollar(void)
redrawWinline(curwin, curwin->w_cursor.lnum);
}
-/// Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
-/// Keep the cursor on the same character.
-/// type == INDENT_INC increase indent (for CTRL-T or <Tab>)
-/// type == INDENT_DEC decrease indent (for CTRL-D)
-/// type == INDENT_SET set indent to "amount"
-///
-/// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec).
-/// @param call_changed_bytes call changed_bytes()
-void change_indent(int type, int amount, int round, bool call_changed_bytes)
-{
- int insstart_less; // reduction for Insstart.col
- colnr_T orig_col = 0; // init for GCC
- char *orig_line = NULL; // init for GCC
-
- // MODE_VREPLACE state needs to know what the line was like before changing
- if (State & VREPLACE_FLAG) {
- orig_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
- orig_col = curwin->w_cursor.col;
- }
-
- // for the following tricks we don't want list mode
- int save_p_list = curwin->w_p_list;
- curwin->w_p_list = false;
- colnr_T vc = getvcol_nolist(&curwin->w_cursor);
- int vcol = vc;
-
- // For Replace mode we need to fix the replace stack later, which is only
- // possible when the cursor is in the indent. Remember the number of
- // characters before the cursor if it's possible.
- int start_col = curwin->w_cursor.col;
-
- // determine offset from first non-blank
- int new_cursor_col = curwin->w_cursor.col;
- beginline(BL_WHITE);
- new_cursor_col -= curwin->w_cursor.col;
-
- insstart_less = curwin->w_cursor.col;
-
- // If the cursor is in the indent, compute how many screen columns the
- // cursor is to the left of the first non-blank.
- if (new_cursor_col < 0) {
- vcol = get_indent() - vcol;
- }
-
- if (new_cursor_col > 0) { // can't fix replace stack
- start_col = -1;
- }
-
- // Set the new indent. The cursor will be put on the first non-blank.
- if (type == INDENT_SET) {
- set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
- } else {
- int save_State = State;
-
- // Avoid being called recursively.
- if (State & VREPLACE_FLAG) {
- State = MODE_INSERT;
- }
- shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
- State = save_State;
- }
- insstart_less -= curwin->w_cursor.col;
-
- // Try to put cursor on same character.
- // If the cursor is at or after the first non-blank in the line,
- // compute the cursor column relative to the column of the first
- // non-blank character.
- // If we are not in insert mode, leave the cursor on the first non-blank.
- // If the cursor is before the first non-blank, position it relative
- // to the first non-blank, counted in screen columns.
- if (new_cursor_col >= 0) {
- // When changing the indent while the cursor is touching it, reset
- // Insstart_col to 0.
- if (new_cursor_col == 0) {
- insstart_less = MAXCOL;
- }
- new_cursor_col += curwin->w_cursor.col;
- } else if (!(State & MODE_INSERT)) {
- new_cursor_col = curwin->w_cursor.col;
- } else {
- // Compute the screen column where the cursor should be.
- vcol = get_indent() - vcol;
- int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol);
- curwin->w_virtcol = end_vcol;
-
- // Advance the cursor until we reach the right screen column.
- new_cursor_col = 0;
- char *const line = get_cursor_line_ptr();
- vcol = 0;
- if (*line != NUL) {
- CharsizeArg csarg;
- CSType cstype = init_charsize_arg(&csarg, curwin, 0, line);
- StrCharInfo ci = utf_ptr2StrCharInfo(line);
- while (true) {
- int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width;
- if (next_vcol > end_vcol) {
- break;
- }
- vcol = next_vcol;
- ci = utfc_next(ci);
- if (*ci.ptr == NUL) {
- break;
- }
- }
- new_cursor_col = (int)(ci.ptr - line);
- }
-
- // May need to insert spaces to be able to position the cursor on
- // the right screen column.
- if (vcol != (int)curwin->w_virtcol) {
- curwin->w_cursor.col = (colnr_T)new_cursor_col;
- const size_t ptrlen = (size_t)(curwin->w_virtcol - vcol);
- char *ptr = xmallocz(ptrlen);
- memset(ptr, ' ', ptrlen);
- new_cursor_col += (int)ptrlen;
- ins_str(ptr, ptrlen);
- xfree(ptr);
- }
-
- // When changing the indent while the cursor is in it, reset
- // Insstart_col to 0.
- insstart_less = MAXCOL;
- }
-
- curwin->w_p_list = save_p_list;
- curwin->w_cursor.col = MAX(0, (colnr_T)new_cursor_col);
- curwin->w_set_curswant = true;
- changed_cline_bef_curs(curwin);
-
- // May have to adjust the start of the insert.
- if (State & MODE_INSERT) {
- if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) {
- if ((int)Insstart.col <= insstart_less) {
- Insstart.col = 0;
- } else {
- Insstart.col -= insstart_less;
- }
- }
- if ((int)ai_col <= insstart_less) {
- ai_col = 0;
- } else {
- ai_col -= insstart_less;
- }
- }
-
- // For MODE_REPLACE state, may have to fix the replace stack, if it's
- // possible. If the number of characters before the cursor decreased, need
- // to pop a few characters from the replace stack.
- // If the number of characters before the cursor increased, need to push a
- // few NULs onto the replace stack.
- if (REPLACE_NORMAL(State) && start_col >= 0) {
- while (start_col > (int)curwin->w_cursor.col) {
- replace_join(0); // remove a NUL from the replace stack
- start_col--;
- }
- while (start_col < (int)curwin->w_cursor.col) {
- replace_push_nul();
- start_col++;
- }
- }
-
- // For MODE_VREPLACE state, we also have to fix the replace stack. In this
- // case it is always possible because we backspace over the whole line and
- // then put it back again the way we wanted it.
- if (State & VREPLACE_FLAG) {
- // Save new line
- char *new_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
-
- // We only put back the new line up to the cursor
- new_line[curwin->w_cursor.col] = NUL;
- int new_col = curwin->w_cursor.col;
-
- // Put back original line
- ml_replace(curwin->w_cursor.lnum, orig_line, false);
- curwin->w_cursor.col = orig_col;
-
- curbuf_splice_pending++;
-
- // Backspace from cursor to start of line
- backspace_until_column(0);
-
- // Insert new stuff into line again
- ins_bytes(new_line);
-
- xfree(new_line);
-
- curbuf_splice_pending--;
-
- // TODO(bfredl): test for crazy edge cases, like we stand on a TAB or
- // something? does this even do the right text change then?
- int delta = orig_col - new_col;
- extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, new_col,
- delta < 0 ? -delta : 0,
- delta > 0 ? delta : 0,
- kExtmarkUndo);
- }
-}
-
/// Truncate the space at the end of a line. This is to be used only in an
/// insert mode. It handles fixing the replace stack for MODE_REPLACE and
/// MODE_VREPLACE modes.
@@ -2885,7 +2687,7 @@ static int replace_pop_if_nul(void)
/// encountered.
///
/// @param off offset for which NUL to remove
-static void replace_join(int off)
+void replace_join(int off)
{
for (ssize_t i = (ssize_t)kv_size(replace_stack); --i >= 0;) {
if (kv_A(replace_stack, i) == NUL && off-- <= 0) {
@@ -2978,233 +2780,6 @@ static void replace_do_bs(int limit_col)
}
}
-/// Check that C-indenting is on.
-bool cindent_on(void)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
-}
-
-/// Check that "cinkeys" contains the key "keytyped",
-/// when == '*': Only if key is preceded with '*' (indent before insert)
-/// when == '!': Only if key is preceded with '!' (don't insert)
-/// when == ' ': Only if key is not preceded with '*' or '!' (indent afterwards)
-///
-/// "keytyped" can have a few special values:
-/// KEY_OPEN_FORW :
-/// KEY_OPEN_BACK :
-/// KEY_COMPLETE : Just finished completion.
-///
-/// @param keytyped key that was typed
-/// @param when condition on when to perform the check
-/// @param line_is_empty when true, accept keys with '0' before them.
-bool in_cinkeys(int keytyped, int when, bool line_is_empty)
-{
- char *look;
- bool try_match;
- bool try_match_word;
- char *p;
- bool icase;
-
- if (keytyped == NUL) {
- // Can happen with CTRL-Y and CTRL-E on a short line.
- return false;
- }
-
- if (*curbuf->b_p_inde != NUL) {
- look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
- } else {
- look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
- }
- while (*look) {
- // Find out if we want to try a match with this key, depending on
- // 'when' and a '*' or '!' before the key.
- switch (when) {
- case '*':
- try_match = (*look == '*'); break;
- case '!':
- try_match = (*look == '!'); break;
- default:
- try_match = (*look != '*') && (*look != '!'); break;
- }
- if (*look == '*' || *look == '!') {
- look++;
- }
-
- // If there is a '0', only accept a match if the line is empty.
- // But may still match when typing last char of a word.
- if (*look == '0') {
- try_match_word = try_match;
- if (!line_is_empty) {
- try_match = false;
- }
- look++;
- } else {
- try_match_word = false;
- }
-
- // Does it look like a control character?
- if (*look == '^' && look[1] >= '?' && look[1] <= '_') {
- if (try_match && keytyped == CTRL_CHR(look[1])) {
- return true;
- }
- look += 2;
-
- // 'o' means "o" command, open forward.
- // 'O' means "O" command, open backward.
- } else if (*look == 'o') {
- if (try_match && keytyped == KEY_OPEN_FORW) {
- return true;
- }
- look++;
- } else if (*look == 'O') {
- if (try_match && keytyped == KEY_OPEN_BACK) {
- return true;
- }
- look++;
-
- // 'e' means to check for "else" at start of line and just before the
- // cursor.
- } else if (*look == 'e') {
- if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) {
- p = get_cursor_line_ptr();
- if (skipwhite(p) == p + curwin->w_cursor.col - 4
- && strncmp(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
- return true;
- }
- }
- look++;
-
- // ':' only causes an indent if it is at the end of a label or case
- // statement, or when it was before typing the ':' (to fix
- // class::method for C++).
- } else if (*look == ':') {
- if (try_match && keytyped == ':') {
- p = get_cursor_line_ptr();
- if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) {
- return true;
- }
- // Need to get the line again after cin_islabel().
- p = get_cursor_line_ptr();
- if (curwin->w_cursor.col > 2
- && p[curwin->w_cursor.col - 1] == ':'
- && p[curwin->w_cursor.col - 2] == ':') {
- p[curwin->w_cursor.col - 1] = ' ';
- const bool i = cin_iscase(p, false)
- || cin_isscopedecl(p)
- || cin_islabel();
- p = get_cursor_line_ptr();
- p[curwin->w_cursor.col - 1] = ':';
- if (i) {
- return true;
- }
- }
- }
- look++;
-
- // Is it a key in <>, maybe?
- } else if (*look == '<') {
- if (try_match) {
- // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
- // <:> and <!> so that people can re-indent on o, O, e, 0, <,
- // >, *, : and ! keys if they really really want to.
- if (vim_strchr("<>!*oOe0:", (uint8_t)look[1]) != NULL
- && keytyped == look[1]) {
- return true;
- }
-
- if (keytyped == get_special_key_code(look + 1)) {
- return true;
- }
- }
- while (*look && *look != '>') {
- look++;
- }
- while (*look == '>') {
- look++;
- }
- // Is it a word: "=word"?
- } else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
- look++;
- if (*look == '~') {
- icase = true;
- look++;
- } else {
- icase = false;
- }
- p = vim_strchr(look, ',');
- if (p == NULL) {
- p = look + strlen(look);
- }
- if ((try_match || try_match_word)
- && curwin->w_cursor.col >= (colnr_T)(p - look)) {
- bool match = false;
-
- if (keytyped == KEY_COMPLETE) {
- char *n, *s;
-
- // Just completed a word, check if it starts with "look".
- // search back for the start of a word.
- char *line = get_cursor_line_ptr();
- for (s = line + curwin->w_cursor.col; s > line; s = n) {
- n = mb_prevptr(line, s);
- if (!vim_iswordp(n)) {
- break;
- }
- }
- assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
- if (s + (p - look) <= line + curwin->w_cursor.col
- && (icase
- ? mb_strnicmp(s, look, (size_t)(p - look))
- : strncmp(s, look, (size_t)(p - look))) == 0) {
- match = true;
- }
- } else {
- // TODO(@brammool): multi-byte
- if (keytyped == (int)(uint8_t)p[-1]
- || (icase && keytyped < 256 && keytyped >= 0
- && TOLOWER_LOC(keytyped) == TOLOWER_LOC((uint8_t)p[-1]))) {
- char *line = get_cursor_pos_ptr();
- assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
- if ((curwin->w_cursor.col == (colnr_T)(p - look)
- || !vim_iswordc((uint8_t)line[-(p - look) - 1]))
- && (icase
- ? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
- : strncmp(line - (p - look), look, (size_t)(p - look))) == 0) {
- match = true;
- }
- }
- }
- if (match && try_match_word && !try_match) {
- // "0=word": Check if there are only blanks before the
- // word.
- if (getwhitecols_curline() !=
- (int)(curwin->w_cursor.col - (p - look))) {
- match = false;
- }
- }
- if (match) {
- return true;
- }
- }
- look = p;
-
- // Ok, it's a boring generic character.
- } else {
- if (try_match && (uint8_t)(*look) == keytyped) {
- return true;
- }
- if (*look != NUL) {
- look++;
- }
- }
-
- // Skip over ", ".
- look = skip_to_option_part(look);
- }
- return false;
-}
-
static void ins_reg(void)
{
bool need_redraw = false;
@@ -4630,80 +4205,6 @@ static int ins_ctrl_ey(int tc)
return c;
}
-// Try to do some very smart auto-indenting.
-// Used when inserting a "normal" character.
-static void ins_try_si(int c)
-{
- pos_T *pos;
-
- // do some very smart indenting when entering '{' or '}'
- if (((did_si || can_si_back) && c == '{') || (can_si && c == '}' && inindent(0))) {
- pos_T old_pos;
- char *ptr;
- int i;
- bool temp;
- // for '}' set indent equal to indent of line containing matching '{'
- if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) {
- old_pos = curwin->w_cursor;
- // If the matching '{' has a ')' immediately before it (ignoring
- // white-space), then line up with the start of the line
- // containing the matching '(' if there is one. This handles the
- // case where an "if (..\n..) {" statement continues over multiple
- // lines -- webb
- ptr = ml_get(pos->lnum);
- i = pos->col;
- if (i > 0) { // skip blanks before '{'
- while (--i > 0 && ascii_iswhite(ptr[i])) {}
- }
- curwin->w_cursor.lnum = pos->lnum;
- curwin->w_cursor.col = i;
- if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) {
- curwin->w_cursor = *pos;
- }
- i = get_indent();
- curwin->w_cursor = old_pos;
- if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, i, false, true);
- } else {
- set_indent(i, SIN_CHANGED);
- }
- } else if (curwin->w_cursor.col > 0) {
- // when inserting '{' after "O" reduce indent, but not
- // more than indent of previous line
- temp = true;
- if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) {
- old_pos = curwin->w_cursor;
- i = get_indent();
- while (curwin->w_cursor.lnum > 1) {
- ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
-
- // ignore empty lines and lines starting with '#'.
- if (*ptr != '#' && *ptr != NUL) {
- break;
- }
- }
- if (get_indent() >= i) {
- temp = false;
- }
- curwin->w_cursor = old_pos;
- }
- if (temp) {
- shift_line(true, false, 1, true);
- }
- }
- }
-
- // set indent of '#' always to 0
- if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) {
- // remember current indent for next line
- old_indent = get_indent();
- set_indent(0, SIN_CHANGED);
- }
-
- // Adjust ai_col, the char at this position can be deleted.
- ai_col = MIN(ai_col, curwin->w_cursor.col);
-}
-
// Get the value that w_virtcol would have when 'list' is off.
// Unless 'cpo' contains the 'L' flag.
colnr_T get_nolist_virtcol(void)
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
@@ -266,6 +266,7 @@ static struct vimvar {
VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO),
VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO),
VV(VV_EVENT, "event", VAR_DICT, VV_RO),
+ VV(VV_VERSIONLONG, "versionlong", VAR_NUMBER, VV_RO),
VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO),
VV(VV_ARGV, "argv", VAR_LIST, VV_RO),
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
@@ -433,6 +434,7 @@ void eval_init(void)
}
}
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+ vimvars[VV_VERSIONLONG].vv_nr = VIM_VERSION_100 * 10000 + highest_patch();
dict_T *const msgpack_types_dict = tv_dict_alloc();
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
@@ -162,6 +162,7 @@ typedef enum {
VV_TYPE_BOOL,
VV_TYPE_BLOB,
VV_EVENT,
+ VV_VERSIONLONG,
VV_ECHOSPACE,
VV_ARGV,
VV_COLLATE,
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
@@ -706,27 +706,3 @@ void restore_buffer(bufref_T *save_curbuf)
curbuf->b_nwindows++;
}
}
-
-/// Find a window for buffer "buf".
-/// If found true is returned and "wp" and "tp" are set to
-/// the window and tabpage.
-/// If not found, false is returned.
-///
-/// @param buf buffer to find a window for
-/// @param[out] wp stores the found window
-/// @param[out] tp stores the found tabpage
-///
-/// @return true if a window was found for the buffer.
-bool find_win_for_buf(buf_T *buf, win_T **wp, tabpage_T **tp)
-{
- *wp = NULL;
- *tp = NULL;
- FOR_ALL_TAB_WINDOWS(tp2, wp2) {
- if (wp2->w_buffer == buf) {
- *tp = tp2;
- *wp = wp2;
- return true;
- }
- }
- return false;
-}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
@@ -777,20 +777,6 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_col(argvars, rettv, true);
}
-/// "cindent(lnum)" function
-static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- pos_T pos = curwin->w_cursor;
- linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = lnum;
- rettv->vval.v_number = get_c_indent();
- curwin->w_cursor = pos;
- } else {
- rettv->vval.v_number = -1;
- }
-}
-
win_T *get_optional_window(typval_T *argvars, int idx)
{
if (argvars[idx].v_type == VAR_UNKNOWN) {
@@ -3246,17 +3232,6 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = xstrdup(hostname);
}
-/// "indent()" function
-static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- rettv->vval.v_number = get_indent_lnum(lnum);
- } else {
- rettv->vval.v_number = -1;
- }
-}
-
/// "index()" function
static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -4415,20 +4390,6 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "lispindent(lnum)" function
-static void f_lispindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const pos_T pos = curwin->w_cursor;
- const linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = lnum;
- rettv->vval.v_number = get_lisp_indent();
- curwin->w_cursor = pos;
- } else {
- rettv->vval.v_number = -1;
- }
-}
-
/// "localtime()" function
static void f_localtime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -5280,52 +5241,6 @@ static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "prompt_setcallback({buffer}, {callback})" function
-static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- Callback prompt_callback = { .type = kCallbackNone };
-
- if (check_secure()) {
- return;
- }
- buf_T *buf = tv_get_buf(&argvars[0], false);
- if (buf == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) {
- if (!callback_from_typval(&prompt_callback, &argvars[1])) {
- return;
- }
- }
-
- callback_free(&buf->b_prompt_callback);
- buf->b_prompt_callback = prompt_callback;
-}
-
-/// "prompt_setinterrupt({buffer}, {callback})" function
-static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- Callback interrupt_callback = { .type = kCallbackNone };
-
- if (check_secure()) {
- return;
- }
- buf_T *buf = tv_get_buf(&argvars[0], false);
- if (buf == NULL) {
- return;
- }
-
- if (argvars[1].v_type != VAR_STRING || *argvars[1].vval.v_string != NUL) {
- if (!callback_from_typval(&interrupt_callback, &argvars[1])) {
- return;
- }
- }
-
- callback_free(&buf->b_prompt_interrupt);
- buf->b_prompt_interrupt = interrupt_callback;
-}
-
/// "prompt_getprompt({buffer})" function
static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
@@ -5346,22 +5261,6 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData
rettv->vval.v_string = xstrdup(buf_prompt_text(buf));
}
-/// "prompt_setprompt({buffer}, {text})" function
-static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- if (check_secure()) {
- return;
- }
- buf_T *buf = tv_get_buf(&argvars[0], false);
- if (buf == NULL) {
- return;
- }
-
- const char *text = tv_get_string(&argvars[1]);
- xfree(buf->b_prompt_text);
- buf->b_prompt_text = xstrdup(text);
-}
-
/// "prompt_getinput({buffer})" function
static void f_prompt_getinput(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
@@ -190,7 +190,7 @@ void tv_list_watch_remove(list_T *const l, listwatch_T *const lwrem)
///
/// @param[out] l List from which item is removed.
/// @param[in] item List item being removed.
-void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
+static void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
@@ -2144,7 +2144,7 @@ void do_wqall(exarg_T *eap)
/// Check the 'write' option.
///
/// @return true and give a message when it's not st.
-bool not_writing(void)
+static bool not_writing(void)
{
if (p_write) {
return false;
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
@@ -799,7 +799,7 @@ void report_make_pending(int pending, void *value)
/// If something pending in a finally clause is resumed at the ":endtry", report
/// it if required by the 'verbose' option or when debugging.
-void report_resume_pending(int pending, void *value)
+static void report_resume_pending(int pending, void *value)
{
if (p_verbose >= 14 || debug_break_level > 0) {
if (debug_break_level <= 0) {
@@ -814,7 +814,7 @@ void report_resume_pending(int pending, void *value)
/// If something pending in a finally clause is discarded, report it if required
/// by the 'verbose' option or when debugging.
-void report_discard_pending(int pending, void *value)
+static void report_discard_pending(int pending, void *value)
{
if (p_verbose >= 14 || debug_break_level > 0) {
if (debug_break_level <= 0) {
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
@@ -1031,7 +1031,7 @@ fail:
/// Free the list of lists of visited files and directories
/// Can handle it if the passed search_context is NULL;
-void vim_findfile_free_visited(void *search_ctx_arg)
+static void vim_findfile_free_visited(void *search_ctx_arg)
{
if (search_ctx_arg == NULL) {
return;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
@@ -2049,20 +2049,6 @@ static char *readfile_charconvert(char *fname, char *fenc, int *fdp)
return tmpname;
}
-/// Read marks for the current buffer from the ShaDa file, when we support
-/// buffer marks and the buffer has a name.
-static void check_marks_read(void)
-{
- if (!curbuf->b_marks_read && get_shada_parameter('\'') > 0
- && curbuf->b_ffname != NULL) {
- shada_read_marks();
- }
-
- // Always set b_marks_read; needed when 'shada' is changed to include
- // the ' parameter after opening a buffer.
- curbuf->b_marks_read = true;
-}
-
/// Set the name of the current buffer. Use when the buffer doesn't have a
/// name and a ":r" or ":w" command with a file name is used.
int set_rw_fname(char *fname, char *sfname)
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
@@ -1282,7 +1282,7 @@ void may_sync_undo(void)
}
/// Make "typebuf" empty and allocate new buffers.
-void alloc_typebuf(void)
+static void alloc_typebuf(void)
{
typebuf.tb_buf = xmalloc(TYPELEN_INIT);
typebuf.tb_noremap = xmalloc(TYPELEN_INIT);
@@ -1298,7 +1298,7 @@ void alloc_typebuf(void)
}
/// Free the buffers of "typebuf".
-void free_typebuf(void)
+static void free_typebuf(void)
{
if (typebuf.tb_buf == typebuf_init) {
internal_error("Free typebuf 1");
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
@@ -15,6 +15,7 @@
#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
@@ -31,6 +32,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
@@ -276,7 +278,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, const colnr_
}
/// See if two tabstop arrays contain the same values.
-bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2)
+static bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2)
{
if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
return false;
@@ -333,7 +335,7 @@ int get_sw_value(buf_T *buf)
}
/// Idem, using "pos".
-int get_sw_value_pos(buf_T *buf, pos_T *pos, bool left)
+static int get_sw_value_pos(buf_T *buf, pos_T *pos, bool left)
{
pos_T save_cursor = curwin->w_cursor;
@@ -977,12 +979,482 @@ bool inindent(int extra)
return false;
}
+/// Handle reindenting a block of lines.
+void op_reindent(oparg_T *oap, Indenter how)
+{
+ int i = 0;
+ linenr_T first_changed = 0;
+ linenr_T last_changed = 0;
+ linenr_T start_lnum = curwin->w_cursor.lnum;
+
+ // Don't even try when 'modifiable' is off.
+ if (!MODIFIABLE(curbuf)) {
+ emsg(_(e_modifiable));
+ return;
+ }
+
+ // Save for undo. Do this once for all lines, much faster than doing this
+ // for each line separately, especially when undoing.
+ if (u_savecommon(curbuf, start_lnum - 1, start_lnum + oap->line_count,
+ start_lnum + oap->line_count, false) == OK) {
+ int amount;
+ for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
+ // it's a slow thing to do, so give feedback so there's no worry
+ // that the computer's just hung.
+
+ if (i > 1
+ && (i % 50 == 0 || i == oap->line_count - 1)
+ && oap->line_count > p_report) {
+ smsg(0, _("%" PRId64 " lines to indent... "), (int64_t)i);
+ }
+
+ // Be vi-compatible: For lisp indenting the first line is not
+ // indented, unless there is only one line.
+ if (i != oap->line_count - 1 || oap->line_count == 1
+ || how != get_lisp_indent) {
+ char *l = skipwhite(get_cursor_line_ptr());
+ if (*l == NUL) { // empty or blank line
+ amount = 0;
+ } else {
+ amount = how(); // get the indent for this line
+ }
+ if (amount >= 0 && set_indent(amount, 0)) {
+ // did change the indent, call changed_lines() later
+ if (first_changed == 0) {
+ first_changed = curwin->w_cursor.lnum;
+ }
+ last_changed = curwin->w_cursor.lnum;
+ }
+ }
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0; // make sure it's valid
+ }
+ }
+
+ // put cursor on first non-blank of indented line
+ curwin->w_cursor.lnum = start_lnum;
+ beginline(BL_SOL | BL_FIX);
+
+ // Mark changed lines so that they will be redrawn. When Visual
+ // highlighting was present, need to continue until the last line. When
+ // there is no change still need to remove the Visual highlighting.
+ if (last_changed != 0) {
+ changed_lines(curbuf, first_changed, 0,
+ oap->is_VIsual ? start_lnum + oap->line_count
+ : last_changed + 1, 0, true);
+ } else if (oap->is_VIsual) {
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+
+ if (oap->line_count > p_report) {
+ i = oap->line_count - (i + 1);
+ smsg(0, NGETTEXT("%" PRId64 " line indented ", "%" PRId64 " lines indented ", i), (int64_t)i);
+ }
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // set '[ and '] marks
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
+}
+
+/// @return true if lines starting with '#' should be left aligned.
+bool preprocs_left(void)
+{
+ return ((curbuf->b_p_si && !curbuf->b_p_cin)
+ || (curbuf->b_p_cin && in_cinkeys('#', ' ', true)
+ && curbuf->b_ind_hash_comment == 0));
+}
+
/// @return true if the conditions are OK for smart indenting.
bool may_do_si(void)
{
return curbuf->b_p_si && !curbuf->b_p_cin && *curbuf->b_p_inde == NUL && !p_paste;
}
+// Try to do some very smart auto-indenting.
+// Used when inserting a "normal" character.
+void ins_try_si(int c)
+{
+ pos_T *pos;
+
+ // do some very smart indenting when entering '{' or '}'
+ if (((did_si || can_si_back) && c == '{') || (can_si && c == '}' && inindent(0))) {
+ pos_T old_pos;
+ char *ptr;
+ int i;
+ bool temp;
+ // for '}' set indent equal to indent of line containing matching '{'
+ if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) {
+ old_pos = curwin->w_cursor;
+ // If the matching '{' has a ')' immediately before it (ignoring
+ // white-space), then line up with the start of the line
+ // containing the matching '(' if there is one. This handles the
+ // case where an "if (..\n..) {" statement continues over multiple
+ // lines -- webb
+ ptr = ml_get(pos->lnum);
+ i = pos->col;
+ if (i > 0) { // skip blanks before '{'
+ while (--i > 0 && ascii_iswhite(ptr[i])) {}
+ }
+ curwin->w_cursor.lnum = pos->lnum;
+ curwin->w_cursor.col = i;
+ if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) {
+ curwin->w_cursor = *pos;
+ }
+ i = get_indent();
+ curwin->w_cursor = old_pos;
+ if (State & VREPLACE_FLAG) {
+ change_indent(INDENT_SET, i, false, true);
+ } else {
+ set_indent(i, SIN_CHANGED);
+ }
+ } else if (curwin->w_cursor.col > 0) {
+ // when inserting '{' after "O" reduce indent, but not
+ // more than indent of previous line
+ temp = true;
+ if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) {
+ old_pos = curwin->w_cursor;
+ i = get_indent();
+ while (curwin->w_cursor.lnum > 1) {
+ ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
+
+ // ignore empty lines and lines starting with '#'.
+ if (*ptr != '#' && *ptr != NUL) {
+ break;
+ }
+ }
+ if (get_indent() >= i) {
+ temp = false;
+ }
+ curwin->w_cursor = old_pos;
+ }
+ if (temp) {
+ shift_line(true, false, 1, true);
+ }
+ }
+ }
+
+ // set indent of '#' always to 0
+ if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) {
+ // remember current indent for next line
+ old_indent = get_indent();
+ set_indent(0, SIN_CHANGED);
+ }
+
+ // Adjust ai_col, the char at this position can be deleted.
+ ai_col = MIN(ai_col, curwin->w_cursor.col);
+}
+
+/// Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
+/// Keep the cursor on the same character.
+/// type == INDENT_INC increase indent (for CTRL-T or <Tab>)
+/// type == INDENT_DEC decrease indent (for CTRL-D)
+/// type == INDENT_SET set indent to "amount"
+///
+/// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec).
+/// @param call_changed_bytes call changed_bytes()
+void change_indent(int type, int amount, int round, bool call_changed_bytes)
+{
+ int insstart_less; // reduction for Insstart.col
+ colnr_T orig_col = 0; // init for GCC
+ char *orig_line = NULL; // init for GCC
+
+ // MODE_VREPLACE state needs to know what the line was like before changing
+ if (State & VREPLACE_FLAG) {
+ orig_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
+ orig_col = curwin->w_cursor.col;
+ }
+
+ // for the following tricks we don't want list mode
+ int save_p_list = curwin->w_p_list;
+ curwin->w_p_list = false;
+ colnr_T vc = getvcol_nolist(&curwin->w_cursor);
+ int vcol = vc;
+
+ // For Replace mode we need to fix the replace stack later, which is only
+ // possible when the cursor is in the indent. Remember the number of
+ // characters before the cursor if it's possible.
+ int start_col = curwin->w_cursor.col;
+
+ // determine offset from first non-blank
+ int new_cursor_col = curwin->w_cursor.col;
+ beginline(BL_WHITE);
+ new_cursor_col -= curwin->w_cursor.col;
+
+ insstart_less = curwin->w_cursor.col;
+
+ // If the cursor is in the indent, compute how many screen columns the
+ // cursor is to the left of the first non-blank.
+ if (new_cursor_col < 0) {
+ vcol = get_indent() - vcol;
+ }
+
+ if (new_cursor_col > 0) { // can't fix replace stack
+ start_col = -1;
+ }
+
+ // Set the new indent. The cursor will be put on the first non-blank.
+ if (type == INDENT_SET) {
+ set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
+ } else {
+ int save_State = State;
+
+ // Avoid being called recursively.
+ if (State & VREPLACE_FLAG) {
+ State = MODE_INSERT;
+ }
+ shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
+ State = save_State;
+ }
+ insstart_less -= curwin->w_cursor.col;
+
+ // Try to put cursor on same character.
+ // If the cursor is at or after the first non-blank in the line,
+ // compute the cursor column relative to the column of the first
+ // non-blank character.
+ // If we are not in insert mode, leave the cursor on the first non-blank.
+ // If the cursor is before the first non-blank, position it relative
+ // to the first non-blank, counted in screen columns.
+ if (new_cursor_col >= 0) {
+ // When changing the indent while the cursor is touching it, reset
+ // Insstart_col to 0.
+ if (new_cursor_col == 0) {
+ insstart_less = MAXCOL;
+ }
+ new_cursor_col += curwin->w_cursor.col;
+ } else if (!(State & MODE_INSERT)) {
+ new_cursor_col = curwin->w_cursor.col;
+ } else {
+ // Compute the screen column where the cursor should be.
+ vcol = get_indent() - vcol;
+ int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol);
+ curwin->w_virtcol = end_vcol;
+
+ // Advance the cursor until we reach the right screen column.
+ new_cursor_col = 0;
+ char *const line = get_cursor_line_ptr();
+ vcol = 0;
+ if (*line != NUL) {
+ CharsizeArg csarg;
+ CSType cstype = init_charsize_arg(&csarg, curwin, 0, line);
+ StrCharInfo ci = utf_ptr2StrCharInfo(line);
+ while (true) {
+ int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width;
+ if (next_vcol > end_vcol) {
+ break;
+ }
+ vcol = next_vcol;
+ ci = utfc_next(ci);
+ if (*ci.ptr == NUL) {
+ break;
+ }
+ }
+ new_cursor_col = (int)(ci.ptr - line);
+ }
+
+ // May need to insert spaces to be able to position the cursor on
+ // the right screen column.
+ if (vcol != (int)curwin->w_virtcol) {
+ curwin->w_cursor.col = (colnr_T)new_cursor_col;
+ const size_t ptrlen = (size_t)(curwin->w_virtcol - vcol);
+ char *ptr = xmallocz(ptrlen);
+ memset(ptr, ' ', ptrlen);
+ new_cursor_col += (int)ptrlen;
+ ins_str(ptr, ptrlen);
+ xfree(ptr);
+ }
+
+ // When changing the indent while the cursor is in it, reset
+ // Insstart_col to 0.
+ insstart_less = MAXCOL;
+ }
+
+ curwin->w_p_list = save_p_list;
+ curwin->w_cursor.col = MAX(0, (colnr_T)new_cursor_col);
+ curwin->w_set_curswant = true;
+ changed_cline_bef_curs(curwin);
+
+ // May have to adjust the start of the insert.
+ if (State & MODE_INSERT) {
+ if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) {
+ if ((int)Insstart.col <= insstart_less) {
+ Insstart.col = 0;
+ } else {
+ Insstart.col -= insstart_less;
+ }
+ }
+ if ((int)ai_col <= insstart_less) {
+ ai_col = 0;
+ } else {
+ ai_col -= insstart_less;
+ }
+ }
+
+ // For MODE_REPLACE state, may have to fix the replace stack, if it's
+ // possible. If the number of characters before the cursor decreased, need
+ // to pop a few characters from the replace stack.
+ // If the number of characters before the cursor increased, need to push a
+ // few NULs onto the replace stack.
+ if (REPLACE_NORMAL(State) && start_col >= 0) {
+ while (start_col > (int)curwin->w_cursor.col) {
+ replace_join(0); // remove a NUL from the replace stack
+ start_col--;
+ }
+ while (start_col < (int)curwin->w_cursor.col) {
+ replace_push_nul();
+ start_col++;
+ }
+ }
+
+ // For MODE_VREPLACE state, we also have to fix the replace stack. In this
+ // case it is always possible because we backspace over the whole line and
+ // then put it back again the way we wanted it.
+ if (State & VREPLACE_FLAG) {
+ // Save new line
+ char *new_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
+
+ // We only put back the new line up to the cursor
+ new_line[curwin->w_cursor.col] = NUL;
+ int new_col = curwin->w_cursor.col;
+
+ // Put back original line
+ ml_replace(curwin->w_cursor.lnum, orig_line, false);
+ curwin->w_cursor.col = orig_col;
+
+ curbuf_splice_pending++;
+
+ // Backspace from cursor to start of line
+ backspace_until_column(0);
+
+ // Insert new stuff into line again
+ ins_bytes(new_line);
+
+ xfree(new_line);
+
+ curbuf_splice_pending--;
+
+ // TODO(bfredl): test for crazy edge cases, like we stand on a TAB or
+ // something? does this even do the right text change then?
+ int delta = orig_col - new_col;
+ extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, new_col,
+ delta < 0 ? -delta : 0,
+ delta > 0 ? delta : 0,
+ kExtmarkUndo);
+ }
+}
+
+/// Copy the indent from ptr to the current line (and fill to size).
+/// Leaves the cursor on the first non-blank in the line.
+///
+/// @return true if the line was changed.
+bool copy_indent(int size, char *src)
+{
+ char *p = NULL;
+ char *line = NULL;
+ int ind_len;
+ int line_len = 0;
+ int tab_pad;
+
+ // Round 1: compute the number of characters needed for the indent
+ // Round 2: copy the characters.
+ for (int round = 1; round <= 2; round++) {
+ int todo = size;
+ ind_len = 0;
+ int ind_done = 0;
+ int ind_col = 0;
+ char *s = src;
+
+ // Count/copy the usable portion of the source line.
+ while (todo > 0 && ascii_iswhite(*s)) {
+ if (*s == TAB) {
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+
+ // Stop if this tab will overshoot the target.
+ if (todo < tab_pad) {
+ break;
+ }
+ todo -= tab_pad;
+ ind_done += tab_pad;
+ ind_col += tab_pad;
+ } else {
+ todo--;
+ ind_done++;
+ ind_col++;
+ }
+ ind_len++;
+
+ if (p != NULL) {
+ *p++ = *s;
+ }
+ s++;
+ }
+
+ // Fill to next tabstop with a tab, if possible.
+ tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_array);
+
+ if ((todo >= tab_pad) && !curbuf->b_p_et) {
+ todo -= tab_pad;
+ ind_len++;
+ ind_col += tab_pad;
+
+ if (p != NULL) {
+ *p++ = TAB;
+ }
+ }
+
+ // Add tabs required for indent.
+ if (!curbuf->b_p_et) {
+ while (true) {
+ tab_pad = tabstop_padding(ind_col,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+ if (todo < tab_pad) {
+ break;
+ }
+ todo -= tab_pad;
+ ind_len++;
+ ind_col += tab_pad;
+ if (p != NULL) {
+ *p++ = TAB;
+ }
+ }
+ }
+
+ // Count/add spaces required for indent.
+ while (todo > 0) {
+ todo--;
+ ind_len++;
+
+ if (p != NULL) {
+ *p++ = ' ';
+ }
+ }
+
+ if (p == NULL) {
+ // Allocate memory for the result: the copied indent, new indent
+ // and the rest of the line.
+ line_len = get_cursor_line_len() + 1;
+ assert(ind_len + line_len >= 0);
+ size_t line_size;
+ STRICT_ADD(ind_len, line_len, &line_size, size_t);
+ line = xmalloc(line_size);
+ p = line;
+ }
+ }
+
+ // Append the original line
+ memmove(p, get_cursor_line_ptr(), (size_t)line_len);
+
+ // Replace the line
+ ml_replace(curwin->w_cursor.lnum, line, false);
+
+ // Put the cursor after the indent.
+ curwin->w_cursor.col = ind_len;
+ return true;
+}
+
/// Give a "resulting text too long" error and maybe set got_int.
static void emsg_text_too_long(void)
{
@@ -1474,3 +1946,28 @@ void fix_indent(void)
do_c_expr_indent();
}
}
+
+/// "indent()" function
+void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ rettv->vval.v_number = get_indent_lnum(lnum);
+ } else {
+ rettv->vval.v_number = -1;
+ }
+}
+
+/// "lispindent(lnum)" function
+void f_lispindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const pos_T pos = curwin->w_cursor;
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum = lnum;
+ rettv->vval.v_number = get_lisp_indent();
+ curwin->w_cursor = pos;
+ } else {
+ rettv->vval.v_number = -1;
+ }
+}
diff --git a/src/nvim/indent.h b/src/nvim/indent.h
@@ -1,6 +1,7 @@
#pragma once
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
+#include "nvim/normal_defs.h"
#include "nvim/pos_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
@@ -14,6 +15,8 @@ enum {
SIN_NOMARK = 8, ///< don't adjust extmarks
};
+typedef int (*Indenter)(void);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent.h.generated.h"
#endif
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
@@ -9,9 +9,11 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
+#include "nvim/eval/typval.h"
#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
#include "nvim/math.h"
@@ -232,6 +234,13 @@ bool cin_is_cinword(const char *line)
return retval;
}
+/// Check that C-indenting is on.
+bool cindent_on(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
+}
+
// Skip over white space and C comments within the line.
// Also skip over Perl/shell comments if desired.
static const char *cin_skipcomment(const char *s)
@@ -346,7 +355,7 @@ static bool cin_islabel_skip(const char **s)
// Recognize a label: "label:".
// Note: curwin->w_cursor must be where we are looking for the label.
-bool cin_islabel(void) // XXX
+static bool cin_islabel(void) // XXX
{
const char *s = cin_skipcomment(get_cursor_line_ptr());
@@ -445,7 +454,7 @@ static int cin_isinit(void)
/// Recognize a switch label: "case .*:" or "default:".
///
/// @param strict Allow relaxed check of case statement for JS
-bool cin_iscase(const char *s, bool strict)
+static bool cin_iscase(const char *s, bool strict)
{
s = cin_skipcomment(s);
if (cin_starts_with(s, "case")) {
@@ -491,7 +500,7 @@ static int cin_isdefault(const char *s)
}
/// Recognize a scope declaration label set in 'cinscopedecls'.
-bool cin_isscopedecl(const char *p)
+static bool cin_isscopedecl(const char *p)
{
const char *s = cin_skipcomment(p);
@@ -3695,6 +3704,226 @@ static int find_match(int lookfor, linenr_T ourscope)
return FAIL;
}
+/// Check that "cinkeys" contains the key "keytyped",
+/// when == '*': Only if key is preceded with '*' (indent before insert)
+/// when == '!': Only if key is preceded with '!' (don't insert)
+/// when == ' ': Only if key is not preceded with '*' or '!' (indent afterwards)
+///
+/// "keytyped" can have a few special values:
+/// KEY_OPEN_FORW :
+/// KEY_OPEN_BACK :
+/// KEY_COMPLETE : Just finished completion.
+///
+/// @param keytyped key that was typed
+/// @param when condition on when to perform the check
+/// @param line_is_empty when true, accept keys with '0' before them.
+bool in_cinkeys(int keytyped, int when, bool line_is_empty)
+{
+ char *look;
+ bool try_match;
+ bool try_match_word;
+ char *p;
+ bool icase;
+
+ if (keytyped == NUL) {
+ // Can happen with CTRL-Y and CTRL-E on a short line.
+ return false;
+ }
+
+ if (*curbuf->b_p_inde != NUL) {
+ look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
+ } else {
+ look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
+ }
+ while (*look) {
+ // Find out if we want to try a match with this key, depending on
+ // 'when' and a '*' or '!' before the key.
+ switch (when) {
+ case '*':
+ try_match = (*look == '*'); break;
+ case '!':
+ try_match = (*look == '!'); break;
+ default:
+ try_match = (*look != '*') && (*look != '!'); break;
+ }
+ if (*look == '*' || *look == '!') {
+ look++;
+ }
+
+ // If there is a '0', only accept a match if the line is empty.
+ // But may still match when typing last char of a word.
+ if (*look == '0') {
+ try_match_word = try_match;
+ if (!line_is_empty) {
+ try_match = false;
+ }
+ look++;
+ } else {
+ try_match_word = false;
+ }
+
+ // Does it look like a control character?
+ if (*look == '^' && look[1] >= '?' && look[1] <= '_') {
+ if (try_match && keytyped == CTRL_CHR(look[1])) {
+ return true;
+ }
+ look += 2;
+
+ // 'o' means "o" command, open forward.
+ // 'O' means "O" command, open backward.
+ } else if (*look == 'o') {
+ if (try_match && keytyped == KEY_OPEN_FORW) {
+ return true;
+ }
+ look++;
+ } else if (*look == 'O') {
+ if (try_match && keytyped == KEY_OPEN_BACK) {
+ return true;
+ }
+ look++;
+
+ // 'e' means to check for "else" at start of line and just before the
+ // cursor.
+ } else if (*look == 'e') {
+ if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) {
+ p = get_cursor_line_ptr();
+ if (skipwhite(p) == p + curwin->w_cursor.col - 4
+ && strncmp(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
+ return true;
+ }
+ }
+ look++;
+
+ // ':' only causes an indent if it is at the end of a label or case
+ // statement, or when it was before typing the ':' (to fix
+ // class::method for C++).
+ } else if (*look == ':') {
+ if (try_match && keytyped == ':') {
+ p = get_cursor_line_ptr();
+ if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) {
+ return true;
+ }
+ // Need to get the line again after cin_islabel().
+ p = get_cursor_line_ptr();
+ if (curwin->w_cursor.col > 2
+ && p[curwin->w_cursor.col - 1] == ':'
+ && p[curwin->w_cursor.col - 2] == ':') {
+ p[curwin->w_cursor.col - 1] = ' ';
+ const bool i = cin_iscase(p, false)
+ || cin_isscopedecl(p)
+ || cin_islabel();
+ p = get_cursor_line_ptr();
+ p[curwin->w_cursor.col - 1] = ':';
+ if (i) {
+ return true;
+ }
+ }
+ }
+ look++;
+
+ // Is it a key in <>, maybe?
+ } else if (*look == '<') {
+ if (try_match) {
+ // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
+ // <:> and <!> so that people can re-indent on o, O, e, 0, <,
+ // >, *, : and ! keys if they really really want to.
+ if (vim_strchr("<>!*oOe0:", (uint8_t)look[1]) != NULL
+ && keytyped == look[1]) {
+ return true;
+ }
+
+ if (keytyped == get_special_key_code(look + 1)) {
+ return true;
+ }
+ }
+ while (*look && *look != '>') {
+ look++;
+ }
+ while (*look == '>') {
+ look++;
+ }
+ // Is it a word: "=word"?
+ } else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
+ look++;
+ if (*look == '~') {
+ icase = true;
+ look++;
+ } else {
+ icase = false;
+ }
+ p = vim_strchr(look, ',');
+ if (p == NULL) {
+ p = look + strlen(look);
+ }
+ if ((try_match || try_match_word)
+ && curwin->w_cursor.col >= (colnr_T)(p - look)) {
+ bool match = false;
+
+ if (keytyped == KEY_COMPLETE) {
+ char *n, *s;
+
+ // Just completed a word, check if it starts with "look".
+ // search back for the start of a word.
+ char *line = get_cursor_line_ptr();
+ for (s = line + curwin->w_cursor.col; s > line; s = n) {
+ n = mb_prevptr(line, s);
+ if (!vim_iswordp(n)) {
+ break;
+ }
+ }
+ assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
+ if (s + (p - look) <= line + curwin->w_cursor.col
+ && (icase
+ ? mb_strnicmp(s, look, (size_t)(p - look))
+ : strncmp(s, look, (size_t)(p - look))) == 0) {
+ match = true;
+ }
+ } else {
+ // TODO(@brammool): multi-byte
+ if (keytyped == (int)(uint8_t)p[-1]
+ || (icase && keytyped < 256 && keytyped >= 0
+ && TOLOWER_LOC(keytyped) == TOLOWER_LOC((uint8_t)p[-1]))) {
+ char *line = get_cursor_pos_ptr();
+ assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
+ if ((curwin->w_cursor.col == (colnr_T)(p - look)
+ || !vim_iswordc((uint8_t)line[-(p - look) - 1]))
+ && (icase
+ ? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
+ : strncmp(line - (p - look), look, (size_t)(p - look))) == 0) {
+ match = true;
+ }
+ }
+ }
+ if (match && try_match_word && !try_match) {
+ // "0=word": Check if there are only blanks before the
+ // word.
+ if (getwhitecols_curline() !=
+ (int)(curwin->w_cursor.col - (p - look))) {
+ match = false;
+ }
+ }
+ if (match) {
+ return true;
+ }
+ }
+ look = p;
+
+ // Ok, it's a boring generic character.
+ } else {
+ if (try_match && (uint8_t)(*look) == keytyped) {
+ return true;
+ }
+ if (*look != NUL) {
+ look++;
+ }
+ }
+
+ // Skip over ", ".
+ look = skip_to_option_part(look);
+ }
+ return false;
+}
+
// Do C or expression indenting on the current line.
void do_c_expr_indent(void)
{
@@ -3704,3 +3933,17 @@ void do_c_expr_indent(void)
fixthisline(get_c_indent);
}
}
+
+/// "cindent(lnum)" function
+void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ pos_T pos = curwin->w_cursor;
+ linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum = lnum;
+ rettv->vval.v_number = get_c_indent();
+ curwin->w_cursor = pos;
+ } else {
+ rettv->vval.v_number = -1;
+ }
+}
diff --git a/src/nvim/indent_c.h b/src/nvim/indent_c.h
@@ -1,5 +1,6 @@
#pragma once
+#include "nvim/eval/typval_defs.h"
#include "nvim/pos_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
@@ -1090,7 +1090,7 @@ char *get_menu_names(expand_T *xp, int idx)
///
/// @param name may be modified.
/// @return start of the next element
-char *menu_name_skip(char *const name)
+static char *menu_name_skip(char *const name)
{
char *p;
diff --git a/src/nvim/message.c b/src/nvim/message.c
@@ -662,7 +662,7 @@ void msg_source(int hl_id)
/// If "emsg_off" is set: no error messages at the moment.
/// If "msg" is in 'debug': do error message but without side effects.
/// If "emsg_skip" is set: never do error messages.
-int emsg_not_now(void)
+static int emsg_not_now(void)
{
if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL
&& vim_strchr(p_debug, 't') == NULL)
@@ -3045,7 +3045,7 @@ static bool do_more_prompt(int typed_char)
return retval;
}
-void msg_moremsg(bool full)
+static void msg_moremsg(bool full)
{
int attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M));
grid_line_start(&msg_grid_adj, Rows - 1);
@@ -3739,7 +3739,7 @@ static void copy_confirm_hotkeys(const char *buttons, int default_button_idx,
}
/// Display the ":confirm" message. Also called when screen resized.
-void display_confirm_msg(void)
+static void display_confirm_msg(void)
{
// Avoid that 'q' at the more prompt truncates the message here.
confirm_msg_used++;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
@@ -134,16 +134,6 @@ static char opchars[][3] = {
{ Ctrl_X, NUL, OPF_CHANGE }, // OP_NR_SUB
};
-yankreg_T *get_y_previous(void)
-{
- return y_previous;
-}
-
-void set_y_previous(yankreg_T *yreg)
-{
- y_previous = yreg;
-}
-
/// Translate a command name into an operator type.
/// Must only be called with a valid operator name!
int get_op_type(int char1, int char2)
@@ -723,84 +713,6 @@ static void block_insert(oparg_T *oap, const char *s, size_t slen, bool b_insert
changed_lines(curbuf, oap->start.lnum + 1, 0, oap->end.lnum + 1, 0, true);
}
-/// Handle reindenting a block of lines.
-void op_reindent(oparg_T *oap, Indenter how)
-{
- int i = 0;
- linenr_T first_changed = 0;
- linenr_T last_changed = 0;
- linenr_T start_lnum = curwin->w_cursor.lnum;
-
- // Don't even try when 'modifiable' is off.
- if (!MODIFIABLE(curbuf)) {
- emsg(_(e_modifiable));
- return;
- }
-
- // Save for undo. Do this once for all lines, much faster than doing this
- // for each line separately, especially when undoing.
- if (u_savecommon(curbuf, start_lnum - 1, start_lnum + oap->line_count,
- start_lnum + oap->line_count, false) == OK) {
- int amount;
- for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
- // it's a slow thing to do, so give feedback so there's no worry
- // that the computer's just hung.
-
- if (i > 1
- && (i % 50 == 0 || i == oap->line_count - 1)
- && oap->line_count > p_report) {
- smsg(0, _("%" PRId64 " lines to indent... "), (int64_t)i);
- }
-
- // Be vi-compatible: For lisp indenting the first line is not
- // indented, unless there is only one line.
- if (i != oap->line_count - 1 || oap->line_count == 1
- || how != get_lisp_indent) {
- char *l = skipwhite(get_cursor_line_ptr());
- if (*l == NUL) { // empty or blank line
- amount = 0;
- } else {
- amount = how(); // get the indent for this line
- }
- if (amount >= 0 && set_indent(amount, 0)) {
- // did change the indent, call changed_lines() later
- if (first_changed == 0) {
- first_changed = curwin->w_cursor.lnum;
- }
- last_changed = curwin->w_cursor.lnum;
- }
- }
- curwin->w_cursor.lnum++;
- curwin->w_cursor.col = 0; // make sure it's valid
- }
- }
-
- // put cursor on first non-blank of indented line
- curwin->w_cursor.lnum = start_lnum;
- beginline(BL_SOL | BL_FIX);
-
- // Mark changed lines so that they will be redrawn. When Visual
- // highlighting was present, need to continue until the last line. When
- // there is no change still need to remove the Visual highlighting.
- if (last_changed != 0) {
- changed_lines(curbuf, first_changed, 0,
- oap->is_VIsual ? start_lnum + oap->line_count
- : last_changed + 1, 0, true);
- } else if (oap->is_VIsual) {
- redraw_curbuf_later(UPD_INVERTED);
- }
-
- if (oap->line_count > p_report) {
- i = oap->line_count - (i + 1);
- smsg(0, NGETTEXT("%" PRId64 " line indented ", "%" PRId64 " lines indented ", i), (int64_t)i);
- }
- if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
- // set '[ and '] marks
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
- }
-}
-
// Keep the last expression line here, for repeating.
static char *expr_line = NULL;
@@ -3738,14 +3650,6 @@ void adjust_cursor_eol(void)
}
}
-/// @return true if lines starting with '#' should be left aligned.
-bool preprocs_left(void)
-{
- return ((curbuf->b_p_si && !curbuf->b_p_cin)
- || (curbuf->b_p_cin && in_cinkeys('#', ' ', true)
- && curbuf->b_ind_hash_comment == 0));
-}
-
/// @return the character name of the register with the given number
int get_register_name(int num)
{
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
@@ -33,8 +33,6 @@ struct block_def {
colnr_T start_char_vcols; ///< number of vcols of pre-block char
};
-typedef int (*Indenter)(void);
-
/// flags for do_put()
enum {
PUT_FIXINDENT = 1, ///< make indent look nice
diff --git a/src/nvim/option.c b/src/nvim/option.c
@@ -1537,40 +1537,6 @@ void set_options_bin(int oldval, int newval, int opt_flags)
didset_options_sctx(opt_flags, p_bin_dep_opts);
}
-/// Find the parameter represented by the given character (eg ', :, ", or /),
-/// and return its associated value in the 'shada' string.
-/// Only works for number parameters, not for 'r' or 'n'.
-/// If the parameter is not specified in the string or there is no following
-/// number, return -1.
-int get_shada_parameter(int type)
-{
- char *p = find_shada_parameter(type);
- if (p != NULL && ascii_isdigit(*p)) {
- return atoi(p);
- }
- return -1;
-}
-
-/// Find the parameter represented by the given character (eg ''', ':', '"', or
-/// '/') in the 'shada' option and return a pointer to the string after it.
-/// Return NULL if the parameter is not specified in the string.
-char *find_shada_parameter(int type)
-{
- for (char *p = p_shada; *p; p++) {
- if (*p == type) {
- return p + 1;
- }
- if (*p == 'n') { // 'n' is always the last one
- break;
- }
- p = vim_strchr(p, ','); // skip until next ','
- if (p == NULL) { // hit the end without finding parameter
- break;
- }
- }
- return NULL;
-}
-
/// Expand environment variables for some string options.
/// These string options cannot be indirect!
/// If "val" is NULL expand the current value of the option.
@@ -4950,7 +4916,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
}
/// Check string options in a window for a NULL value.
-void check_win_options(win_T *win)
+static void check_win_options(win_T *win)
{
check_winopt(&win->w_onebuf_opt);
check_winopt(&win->w_allbuf_opt);
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
@@ -43,6 +43,7 @@
#include "nvim/pos_defs.h"
#include "nvim/regexp.h"
#include "nvim/regexp_defs.h"
+#include "nvim/shada.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
@@ -171,7 +171,7 @@ proftime_T profile_self(proftime_T self, proftime_T total, proftime_T children)
/// Gets the current waittime.
///
/// @return the current waittime
-proftime_T profile_get_wait(void) FUNC_ATTR_PURE
+static proftime_T profile_get_wait(void) FUNC_ATTR_PURE
{
return prof_wait_time;
}
@@ -194,7 +194,7 @@ proftime_T profile_sub_wait(proftime_T tm, proftime_T tma) FUNC_ATTR_PURE
/// Checks if time `tm1` is equal to `tm2`.
///
/// @return true if `tm1` == `tm2`
-bool profile_equal(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
+static bool profile_equal(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
{
return tm1 == tm2;
}
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
@@ -3772,3 +3772,51 @@ void shada_read_string(String string, const int flags)
shada_read(&sd_reader, flags);
close_file(&sd_reader);
}
+
+/// Find the parameter represented by the given character (eg ', :, ", or /),
+/// and return its associated value in the 'shada' string.
+/// Only works for number parameters, not for 'r' or 'n'.
+/// If the parameter is not specified in the string or there is no following
+/// number, return -1.
+int get_shada_parameter(int type)
+{
+ char *p = find_shada_parameter(type);
+ if (p != NULL && ascii_isdigit(*p)) {
+ return atoi(p);
+ }
+ return -1;
+}
+
+/// Find the parameter represented by the given character (eg ''', ':', '"', or
+/// '/') in the 'shada' option and return a pointer to the string after it.
+/// Return NULL if the parameter is not specified in the string.
+char *find_shada_parameter(int type)
+{
+ for (char *p = p_shada; *p; p++) {
+ if (*p == type) {
+ return p + 1;
+ }
+ if (*p == 'n') { // 'n' is always the last one
+ break;
+ }
+ p = vim_strchr(p, ','); // skip until next ','
+ if (p == NULL) { // hit the end without finding parameter
+ break;
+ }
+ }
+ return NULL;
+}
+
+/// Read marks for the current buffer from the ShaDa file, when we support
+/// buffer marks and the buffer has a name.
+void check_marks_read(void)
+{
+ if (!curbuf->b_marks_read && get_shada_parameter('\'') > 0
+ && curbuf->b_ffname != NULL) {
+ shada_read_marks();
+ }
+
+ // Always set b_marks_read; needed when 'shada' is changed to include
+ // the ' parameter after opening a buffer.
+ curbuf->b_marks_read = true;
+}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
@@ -2991,7 +2991,7 @@ void u_clearallandblockfree(buf_T *buf)
}
/// Save the line "lnum" for the "U" command.
-void u_saveline(buf_T *buf, linenr_T lnum)
+static void u_saveline(buf_T *buf, linenr_T lnum)
{
if (lnum == buf->b_u_line_lnum) { // line is already saved
return;
diff --git a/src/nvim/version.c b/src/nvim/version.c
@@ -2528,6 +2528,12 @@ bool has_nvim_version(const char *const version_str)
&& patch <= NVIM_VERSION_PATCH))));
}
+int highest_patch(void)
+{
+ // this relies on the highest patch number to be the first entry
+ return included_patches[0];
+}
+
/// Checks whether a Vim patch has been included.
///
/// @param n Patch number.
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
@@ -878,6 +878,21 @@ M.vars = {
<
]=],
},
+ versionlong = {
+ type = 'integer',
+ desc = [=[
+ Like v:version, but also including the patchlevel in the last
+ four digits. Version 8.1 with patch 123 has value 8010123.
+ This can be used like this: >
+ if v:versionlong >= 8010123
+ <
+ However, if there are gaps in the list of patches included
+ this will not work well. This can happen if a recent patch
+ was included into an older version, e.g. for a security fix.
+ Use the has() function to make sure the patch is actually
+ included.
+ ]=],
+ },
vim_did_enter = {
type = 'integer',
desc = [=[
diff --git a/src/nvim/window.c b/src/nvim/window.c
@@ -4262,7 +4262,7 @@ int win_new_tabpage(int after, char *filename)
// Open a new tab page if ":tab cmd" was used. It will edit the same buffer,
// like with ":split".
// Returns OK if a new tab page was created, FAIL otherwise.
-int may_open_tabpage(void)
+static int may_open_tabpage(void)
{
int n = (cmdmod.cmod_tab == 0) ? postponed_split_tab : cmdmod.cmod_tab;
diff --git a/test/old/testdir/test_eval_stuff.vim b/test/old/testdir/test_eval_stuff.vim
@@ -272,6 +272,17 @@ func Test_string_concat_scriptversion1()
endif
endfunc
+" scriptversion 2
+func Test_vvar_scriptversion2()
+ call assert_true(exists('version'))
+ echo version
+ call assert_fails('let version = 1', 'E46:')
+ call assert_equal(v:version, version)
+
+ call assert_equal(v:version, v:versionlong / 10000)
+ call assert_true(v:versionlong > 8011525)
+endfunc
+
" scriptversion 1
func Test_vvar_scriptversion1()
call assert_equal(15, 017)
diff --git a/test/old/testdir/test_ruby.vim b/test/old/testdir/test_ruby.vim
@@ -319,6 +319,9 @@ func Test_ruby_Vim_evaluate()
call assert_equal('foo', rubyeval('Vim::evaluate("\"foo\"")'))
call assert_equal('String', rubyeval('Vim::evaluate("\"foo\"").class'))
+ call assert_equal(["\x01\xAB"], rubyeval('Vim::evaluate("0z01ab").unpack("M")'))
+ call assert_equal('String', rubyeval('Vim::evaluate("0z01ab").class'))
+
call assert_equal([1, 2], rubyeval('Vim::evaluate("[1, 2]")'))
call assert_equal('Array', rubyeval('Vim::evaluate("[1, 2]").class'))
diff --git a/test/unit/profile_spec.lua b/test/unit/profile_spec.lua
@@ -96,9 +96,6 @@ describe('profiling related functions', function()
local function profile_cmp(t1, t2)
return prof.profile_cmp(t1, t2)
end
- local function profile_equal(t1, t2)
- return prof.profile_equal(t1, t2)
- end
local function profile_msg(q)
return ffi.string(prof.profile_msg(q))
end
@@ -110,20 +107,6 @@ describe('profiling related functions', function()
return tonumber(s) + tonumber(us) / 1000000
end
- describe('profile_equal', function()
- itp('times are equal to themselves', function()
- local start = profile_start()
- assert.is_true(profile_equal(start, start))
-
- local e = profile_end(start)
- assert.is_true(profile_equal(e, e))
- end)
-
- itp('times are unequal to others', function()
- assert.is_false(profile_equal(profile_start(), profile_start()))
- end)
- end)
-
-- this is quite difficult to test, as it would rely on other functions in
-- the profiling package. Those functions in turn will probably be tested
-- using profile_cmp... circular reasoning.
@@ -169,7 +152,6 @@ describe('profiling related functions', function()
describe('profile_zero', function()
itp('returns the same value on each call', function()
eq(0, profile_zero())
- assert.is_true(profile_equal(profile_zero(), profile_zero()))
end)
end)
@@ -203,7 +185,7 @@ describe('profiling related functions', function()
describe('profile_setlimit', function()
itp('sets no limit when 0 is passed', function()
- eq(true, profile_equal(profile_setlimit(0), profile_zero()))
+ eq(profile_setlimit(0), profile_zero())
end)
itp('sets a limit in the future otherwise', function()