aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mpack/mpack_core.h1
-rwxr-xr-xsrc/nvim/CMakeLists.txt17
-rw-r--r--src/nvim/api/autocmd.c6
-rw-r--r--src/nvim/api/buffer.c5
-rw-r--r--src/nvim/api/command.c16
-rw-r--r--src/nvim/api/extmark.c13
-rw-r--r--src/nvim/api/keysets.lua3
-rw-r--r--src/nvim/api/private/dispatch.h9
-rw-r--r--src/nvim/api/private/helpers.c47
-rw-r--r--src/nvim/api/private/helpers.h11
-rw-r--r--src/nvim/api/ui.c12
-rw-r--r--src/nvim/api/vim.c147
-rw-r--r--src/nvim/api/vimscript.c11
-rw-r--r--src/nvim/api/win_config.c8
-rw-r--r--src/nvim/api/window.c29
-rw-r--r--src/nvim/arabic.c26
-rw-r--r--src/nvim/arglist.c1156
-rw-r--r--src/nvim/arglist.h11
-rw-r--r--src/nvim/autocmd.c72
-rw-r--r--src/nvim/autocmd.h24
-rw-r--r--src/nvim/buffer.c1587
-rw-r--r--src/nvim/buffer.h8
-rw-r--r--src/nvim/buffer_defs.h282
-rw-r--r--src/nvim/change.c241
-rw-r--r--src/nvim/channel.c5
-rw-r--r--src/nvim/charset.c99
-rw-r--r--src/nvim/cmdexpand.c2908
-rw-r--r--src/nvim/cmdexpand.h44
-rw-r--r--src/nvim/cmdhist.c744
-rw-r--r--src/nvim/cmdhist.h33
-rw-r--r--src/nvim/context.c7
-rw-r--r--src/nvim/cursor.c20
-rw-r--r--src/nvim/cursor_shape.c2
-rw-r--r--src/nvim/debugger.c64
-rw-r--r--src/nvim/decoration.c58
-rw-r--r--src/nvim/decoration_provider.c7
-rw-r--r--src/nvim/decoration_provider.h1
-rw-r--r--src/nvim/diff.c132
-rw-r--r--src/nvim/digraph.c20
-rw-r--r--src/nvim/drawline.c2680
-rw-r--r--src/nvim/drawline.h24
-rw-r--r--src/nvim/drawscreen.c2176
-rw-r--r--src/nvim/drawscreen.h25
-rw-r--r--src/nvim/edit.c902
-rw-r--r--src/nvim/eval.c1054
-rw-r--r--src/nvim/eval.h9
-rw-r--r--src/nvim/eval.lua37
-rw-r--r--src/nvim/eval/encode.c6
-rw-r--r--src/nvim/eval/executor.c3
-rw-r--r--src/nvim/eval/funcs.c1957
-rw-r--r--src/nvim/eval/funcs.h17
-rw-r--r--src/nvim/eval/typval.c30
-rw-r--r--src/nvim/eval/typval.h8
-rw-r--r--src/nvim/eval/userfunc.c428
-rw-r--r--src/nvim/eval/userfunc.h9
-rw-r--r--src/nvim/eval/vars.c49
-rw-r--r--src/nvim/ex_cmds.c1315
-rw-r--r--src/nvim/ex_cmds.h6
-rw-r--r--src/nvim/ex_cmds.lua8
-rw-r--r--src/nvim/ex_cmds2.c2217
-rw-r--r--src/nvim/ex_cmds2.h26
-rw-r--r--src/nvim/ex_cmds_defs.h10
-rw-r--r--src/nvim/ex_docmd.c2221
-rw-r--r--src/nvim/ex_eval.c341
-rw-r--r--src/nvim/ex_eval.h76
-rw-r--r--src/nvim/ex_eval_defs.h79
-rw-r--r--src/nvim/ex_getln.c3510
-rw-r--r--src/nvim/ex_getln.h115
-rw-r--r--src/nvim/ex_session.c7
-rw-r--r--src/nvim/extmark.c4
-rw-r--r--src/nvim/file_search.c134
-rw-r--r--src/nvim/fileio.c331
-rw-r--r--src/nvim/fileio.h2
-rw-r--r--src/nvim/fold.c161
-rw-r--r--src/nvim/garray.c2
-rw-r--r--src/nvim/garray.h2
-rw-r--r--src/nvim/generators/c_grammar.lua1
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua40
-rwxr-xr-xsrc/nvim/generators/gen_api_ui_events.lua6
-rw-r--r--src/nvim/generators/gen_eval.lua15
-rw-r--r--src/nvim/generators/gen_unicode_tables.lua6
-rw-r--r--src/nvim/getchar.c290
-rw-r--r--src/nvim/globals.h77
-rw-r--r--src/nvim/grid.c162
-rw-r--r--src/nvim/grid.h4
-rw-r--r--src/nvim/hardcopy.c192
-rw-r--r--src/nvim/hashtab.c14
-rw-r--r--src/nvim/help.c1179
-rw-r--r--src/nvim/help.h11
-rw-r--r--src/nvim/highlight.c291
-rw-r--r--src/nvim/highlight.h8
-rw-r--r--src/nvim/highlight_defs.h20
-rw-r--r--src/nvim/highlight_group.c197
-rw-r--r--src/nvim/highlight_group.h3
-rw-r--r--src/nvim/if_cscope.c50
-rw-r--r--src/nvim/indent.c423
-rw-r--r--src/nvim/indent_c.c150
-rw-r--r--src/nvim/input.c2
-rw-r--r--src/nvim/insexpand.c2864
-rw-r--r--src/nvim/keycodes.c144
-rw-r--r--src/nvim/locale.c369
-rw-r--r--src/nvim/locale.h10
-rw-r--r--src/nvim/log.c6
-rw-r--r--src/nvim/lua/converter.c2
-rw-r--r--src/nvim/lua/converter.h2
-rw-r--r--src/nvim/lua/executor.c35
-rw-r--r--src/nvim/lua/executor.h2
-rw-r--r--src/nvim/lua/stdlib.c56
-rw-r--r--src/nvim/lua/treesitter.c87
-rw-r--r--src/nvim/lua/xdiff.c6
-rw-r--r--src/nvim/macros.h2
-rw-r--r--src/nvim/main.c147
-rw-r--r--src/nvim/main.h1
-rw-r--r--src/nvim/mapping.c69
-rw-r--r--src/nvim/mark.c41
-rw-r--r--src/nvim/match.c39
-rw-r--r--src/nvim/mbyte.c331
-rw-r--r--src/nvim/mbyte.h49
-rw-r--r--src/nvim/mbyte_defs.h56
-rw-r--r--src/nvim/memfile.c14
-rw-r--r--src/nvim/memline.c209
-rw-r--r--src/nvim/memory.c82
-rw-r--r--src/nvim/memory.h6
-rw-r--r--src/nvim/menu.c57
-rw-r--r--src/nvim/menu.h21
-rw-r--r--src/nvim/menu_defs.h64
-rw-r--r--src/nvim/message.c327
-rw-r--r--src/nvim/mouse.c49
-rw-r--r--src/nvim/mouse.h2
-rw-r--r--src/nvim/move.c96
-rw-r--r--src/nvim/msgpack_rpc/channel.c22
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h2
-rw-r--r--src/nvim/msgpack_rpc/server.c2
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c12
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h2
-rw-r--r--src/nvim/normal.c202
-rw-r--r--src/nvim/normal.h4
-rw-r--r--src/nvim/ops.c966
-rw-r--r--src/nvim/option.c3365
-rw-r--r--src/nvim/option.h17
-rw-r--r--src/nvim/option_defs.h473
-rw-r--r--src/nvim/optionstr.c1663
-rw-r--r--src/nvim/optionstr.h10
-rw-r--r--src/nvim/os/env.c79
-rw-r--r--src/nvim/os/fs.c37
-rw-r--r--src/nvim/os/input.c4
-rw-r--r--src/nvim/os/pty_conpty_win.h3
-rw-r--r--src/nvim/os/shell.c23
-rw-r--r--src/nvim/os/signal.c2
-rw-r--r--src/nvim/path.c212
-rw-r--r--src/nvim/plines.c186
-rw-r--r--src/nvim/plines.h14
-rw-r--r--src/nvim/po/af.po4
-rw-r--r--src/nvim/po/ca.po6
-rw-r--r--src/nvim/po/cs.cp1250.po6
-rw-r--r--src/nvim/po/cs.po6
-rw-r--r--src/nvim/po/da.po4
-rw-r--r--src/nvim/po/de.po4
-rw-r--r--src/nvim/po/en_GB.po4
-rw-r--r--src/nvim/po/eo.po4
-rw-r--r--src/nvim/po/es.po6
-rw-r--r--src/nvim/po/fi.po4
-rw-r--r--src/nvim/po/fr.po4
-rw-r--r--src/nvim/po/ga.po4
-rw-r--r--src/nvim/po/it.po6
-rw-r--r--src/nvim/po/ko.UTF-8.po6
-rw-r--r--src/nvim/po/nb.po6
-rw-r--r--src/nvim/po/nl.po4
-rw-r--r--src/nvim/po/no.po6
-rw-r--r--src/nvim/po/pl.UTF-8.po6
-rw-r--r--src/nvim/po/pt_BR.po4
-rw-r--r--src/nvim/po/ru.po6
-rw-r--r--src/nvim/po/sk.cp1250.po6
-rw-r--r--src/nvim/po/sk.po6
-rw-r--r--src/nvim/po/sr.po4
-rw-r--r--src/nvim/po/sv.po4
-rw-r--r--src/nvim/po/tr.po4
-rw-r--r--src/nvim/po/uk.po4
-rw-r--r--src/nvim/po/vi.po6
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po58
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po366
-rw-r--r--src/nvim/popupmenu.c (renamed from src/nvim/popupmnu.c)58
-rw-r--r--src/nvim/popupmenu.h (renamed from src/nvim/popupmnu.h)8
-rw-r--r--src/nvim/profile.c664
-rw-r--r--src/nvim/profile.h3
-rw-r--r--src/nvim/quickfix.c305
-rw-r--r--src/nvim/regexp.c36
-rw-r--r--src/nvim/regexp_bt.c19
-rw-r--r--src/nvim/regexp_defs.h1
-rw-r--r--src/nvim/regexp_nfa.c10
-rw-r--r--src/nvim/runtime.c1377
-rw-r--r--src/nvim/runtime.h72
-rw-r--r--src/nvim/screen.c6125
-rw-r--r--src/nvim/screen.h34
-rw-r--r--src/nvim/search.c2083
-rw-r--r--src/nvim/shada.c36
-rw-r--r--src/nvim/sign.c129
-rw-r--r--src/nvim/sign_defs.h14
-rw-r--r--src/nvim/spell.c4374
-rw-r--r--src/nvim/spell_defs.h71
-rw-r--r--src/nvim/spellfile.c397
-rw-r--r--src/nvim/spellsuggest.c3800
-rw-r--r--src/nvim/spellsuggest.h9
-rw-r--r--src/nvim/state.c2
-rw-r--r--src/nvim/statusline.c1807
-rw-r--r--src/nvim/statusline.h10
-rw-r--r--src/nvim/strings.c50
-rw-r--r--src/nvim/syntax.c514
-rw-r--r--src/nvim/tag.c285
-rw-r--r--src/nvim/tag.h2
-rw-r--r--src/nvim/terminal.c17
-rw-r--r--src/nvim/testdir/runtest.vim16
-rw-r--r--src/nvim/testdir/setup.vim2
-rw-r--r--src/nvim/testdir/test_arglist.vim44
-rw-r--r--src/nvim/testdir/test_autocmd.vim186
-rw-r--r--src/nvim/testdir/test_blob.vim2
-rw-r--r--src/nvim/testdir/test_buffer.vim206
-rw-r--r--src/nvim/testdir/test_bufline.vim22
-rw-r--r--src/nvim/testdir/test_cd.vim2
-rw-r--r--src/nvim/testdir/test_clientserver.vim11
-rw-r--r--src/nvim/testdir/test_cmdline.vim409
-rw-r--r--src/nvim/testdir/test_comments.vim277
-rw-r--r--src/nvim/testdir/test_cursor_func.vim2
-rw-r--r--src/nvim/testdir/test_debugger.vim19
-rw-r--r--src/nvim/testdir/test_diffmode.vim8
-rw-r--r--src/nvim/testdir/test_digraph.vim13
-rw-r--r--src/nvim/testdir/test_display.vim82
-rw-r--r--src/nvim/testdir/test_edit.vim190
-rw-r--r--src/nvim/testdir/test_environ.vim20
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim12
-rw-r--r--src/nvim/testdir/test_excmd.vim8
-rw-r--r--src/nvim/testdir/test_exit.vim2
-rw-r--r--src/nvim/testdir/test_expand.vim106
-rw-r--r--src/nvim/testdir/test_expand_func.vim52
-rw-r--r--src/nvim/testdir/test_expr.vim1
-rw-r--r--src/nvim/testdir/test_filechanged.vim15
-rw-r--r--src/nvim/testdir/test_filetype.vim8
-rw-r--r--src/nvim/testdir/test_functions.vim94
-rw-r--r--src/nvim/testdir/test_gf.vim16
-rw-r--r--src/nvim/testdir/test_global.vim5
-rw-r--r--src/nvim/testdir/test_goto.vim18
-rw-r--r--src/nvim/testdir/test_highlight.vim5
-rw-r--r--src/nvim/testdir/test_history.vim17
-rw-r--r--src/nvim/testdir/test_ins_complete.vim451
-rw-r--r--src/nvim/testdir/test_lambda.vim18
-rw-r--r--src/nvim/testdir/test_listdict.vim29
-rw-r--r--src/nvim/testdir/test_maparg.vim2
-rw-r--r--src/nvim/testdir/test_messages.vim65
-rw-r--r--src/nvim/testdir/test_normal.vim323
-rw-r--r--src/nvim/testdir/test_options.vim85
-rw-r--r--src/nvim/testdir/test_preview.vim7
-rw-r--r--src/nvim/testdir/test_quickfix.vim6
-rw-r--r--src/nvim/testdir/test_quotestar.vim5
-rw-r--r--src/nvim/testdir/test_regexp_utf8.vim4
-rw-r--r--src/nvim/testdir/test_registers.vim11
-rw-r--r--src/nvim/testdir/test_selectmode.vim150
-rw-r--r--src/nvim/testdir/test_spell.vim10
-rw-r--r--src/nvim/testdir/test_spellfile.vim39
-rw-r--r--src/nvim/testdir/test_startup.vim4
-rw-r--r--src/nvim/testdir/test_substitute.vim38
-rw-r--r--src/nvim/testdir/test_swap.vim25
-rw-r--r--src/nvim/testdir/test_syntax.vim18
-rw-r--r--src/nvim/testdir/test_tabpage.vim16
-rw-r--r--src/nvim/testdir/test_tagjump.vim13
-rw-r--r--src/nvim/testdir/test_textformat.vim272
-rw-r--r--src/nvim/testdir/test_textobjects.vim1
-rw-r--r--src/nvim/testdir/test_trycatch.vim175
-rw-r--r--src/nvim/testdir/test_undo.vim6
-rw-r--r--src/nvim/testdir/test_user_func.vim7
-rw-r--r--src/nvim/testdir/test_utf8.vim45
-rw-r--r--src/nvim/testdir/test_viminfo.vim21
-rw-r--r--src/nvim/testdir/test_vimscript.vim48
-rw-r--r--src/nvim/testdir/test_visual.vim206
-rw-r--r--src/nvim/testdir/test_writefile.vim44
-rw-r--r--src/nvim/testing.c49
-rw-r--r--src/nvim/textformat.c1127
-rw-r--r--src/nvim/textformat.h10
-rw-r--r--src/nvim/textobject.c1742
-rw-r--r--src/nvim/textobject.h11
-rw-r--r--src/nvim/tui/input.c19
-rw-r--r--src/nvim/tui/tui.c70
-rw-r--r--src/nvim/types.h13
-rw-r--r--src/nvim/ui.c15
-rw-r--r--src/nvim/ui.h2
-rw-r--r--src/nvim/ui_bridge.c4
-rw-r--r--src/nvim/ui_client.c2
-rw-r--r--src/nvim/ui_compositor.c4
-rw-r--r--src/nvim/undo.c776
-rw-r--r--src/nvim/undo_defs.h6
-rw-r--r--src/nvim/usercmd.c50
-rw-r--r--src/nvim/usercmd.h6
-rw-r--r--src/nvim/version.c36
-rw-r--r--src/nvim/vim.h19
-rw-r--r--src/nvim/viml/parser/expressions.c4
-rw-r--r--src/nvim/viml/parser/parser.h4
-rw-r--r--src/nvim/window.c372
296 files changed, 39544 insertions, 35234 deletions
diff --git a/src/mpack/mpack_core.h b/src/mpack/mpack_core.h
index 1d601bc82d..336b778931 100644
--- a/src/mpack/mpack_core.h
+++ b/src/mpack/mpack_core.h
@@ -8,6 +8,7 @@
#include <assert.h>
#include <limits.h>
#include <stddef.h>
+#include <stdbool.h>
#ifdef __GNUC__
# define FPURE __attribute__((const))
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index b743e9923f..635833748d 100755
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -676,12 +676,17 @@ function(get_test_target prefix sfile relative_path_var target_var)
endif()
endfunction()
-set(NO_SINGLE_CHECK_HEADERS
- os/win_defs.h
- os/pty_process_win.h
- os/pty_conpty_win.h
- os/os_win_console.h
-)
+if(WIN32)
+ set(NO_SINGLE_CHECK_HEADERS
+ os/pty_process_unix.h
+ os/unix_defs.h)
+else()
+ set(NO_SINGLE_CHECK_HEADERS
+ os/win_defs.h
+ os/pty_process_win.h
+ os/pty_conpty_win.h
+ os/os_win_console.h)
+endif()
foreach(hfile ${NVIM_HEADERS})
get_test_target(test-includes "${hfile}" relative_path texe)
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index bf6402f938..1cf0211f43 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -9,9 +9,9 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/eval/typval.h"
-#include "nvim/fileio.h"
#include "nvim/lua/executor.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -887,12 +887,12 @@ static bool check_autocmd_string_array(Array arr, char *k, Error *err)
static bool unpack_string_or_array(Array *array, Object *v, char *k, bool required, Error *err)
{
if (v->type == kObjectTypeString) {
- ADD(*array, copy_object(*v));
+ ADD(*array, copy_object(*v, NULL));
} else if (v->type == kObjectTypeArray) {
if (!check_autocmd_string_array(v->data.array, k, err)) {
return false;
}
- *array = copy_array(v->data.array);
+ *array = copy_array(v->data.array, NULL);
} else {
if (required) {
api_set_error(err,
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index d3895d31cf..199650fc55 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -18,6 +18,7 @@
#include "nvim/change.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
+#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/extmark.h"
@@ -1020,7 +1021,7 @@ void nvim_buf_del_var(Buffer buffer, String name, Error *err)
/// @param buffer Buffer handle, or 0 for current buffer
/// @param[out] err Error details, if any
/// @return Buffer name
-String nvim_buf_get_name(Buffer buffer, Error *err)
+String nvim_buf_get_name(Buffer buffer, Arena *arena, Error *err)
FUNC_API_SINCE(1)
{
String rv = STRING_INIT;
@@ -1030,7 +1031,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err)
return rv;
}
- return cstr_to_string((char *)buf->b_ffname);
+ return cstr_as_string(buf->b_ffname);
}
/// Sets the full file name for a buffer
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index bc766ff39c..1323fc347b 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -11,6 +11,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/autocmd.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/lua/executor.h"
#include "nvim/ops.h"
#include "nvim/regexp.h"
@@ -300,10 +301,10 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
FUNC_API_SINCE(10)
{
exarg_T ea;
- memset(&ea, 0, sizeof(ea));
+ CLEAR_FIELD(ea);
CmdParseInfo cmdinfo;
- memset(&cmdinfo, 0, sizeof(cmdinfo));
+ CLEAR_FIELD(cmdinfo);
char *cmdline = NULL;
char *cmdname = NULL;
@@ -625,6 +626,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
garray_T capture_local;
const int save_msg_silent = msg_silent;
garray_T * const save_capture_ga = capture_ga;
+ const int save_msg_col = msg_col;
if (output) {
ga_init(&capture_local, 1, 80);
@@ -635,6 +637,7 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
try_start();
if (output) {
msg_silent++;
+ msg_col = 0; // prevent leading spaces
}
WITH_SCRIPT_CONTEXT(channel_id, {
@@ -644,6 +647,8 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error
if (output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
+ // Put msg_col back where it was, since nothing should have been written.
+ msg_col = save_msg_col;
}
try_end(err);
@@ -819,9 +824,12 @@ static void build_cmdline_str(char **cmdlinep, exarg_T *eap, CmdParseInfo *cmdin
char *p = replace_makeprg(eap, eap->arg, cmdlinep);
if (p != eap->arg) {
// If replace_makeprg modified the cmdline string, correct the argument pointers.
- assert(argc == 1);
eap->arg = p;
- eap->args[0] = p;
+ // We can only know the position of the first argument because the argument list can be used
+ // multiple times in makeprg / grepprg.
+ if (argc >= 1) {
+ eap->args[0] = p;
+ }
}
}
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index da1b6beeda..09b004637f 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -10,11 +10,11 @@
#include "nvim/api/private/helpers.h"
#include "nvim/charset.h"
#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
-#include "nvim/screen.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/extmark.c.generated.h"
@@ -51,7 +51,7 @@ Integer nvim_create_namespace(String name)
}
id = next_namespace_id++;
if (name.size > 0) {
- String name_alloc = copy_string(name);
+ String name_alloc = copy_string(name, NULL);
map_put(String, handle_T)(&namespace_ids, name_alloc, id);
}
return (Integer)id;
@@ -441,8 +441,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// the extmark end position (if it exists) will be shifted
/// in when new text is inserted (true for right, false
/// for left). Defaults to false.
-/// - priority: a priority value for the highlight group. For
-/// example treesitter highlighting uses a value of 100.
+/// - priority: a priority value for the highlight group or sign
+/// attribute. For example treesitter highlighting uses a
+/// value of 100.
/// - strict: boolean that indicates extmark should not be placed
/// if the line or column value is past the end of the
/// buffer or end of the line respectively. Defaults to true.
@@ -992,7 +993,7 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
decor_provider_clear(p);
// regardless of what happens, it seems good idea to redraw
- redraw_all_later(NOT_VALID); // TODO(bfredl): too soon?
+ redraw_all_later(UPD_NOT_VALID); // TODO(bfredl): too soon?
struct {
const char *name;
@@ -1030,6 +1031,8 @@ void nvim_set_decoration_provider(Integer ns_id, DictionaryOf(LuaRef) opts, Erro
}
p->active = true;
+ p->hl_valid++;
+ p->hl_cached = false;
return;
error:
decor_provider_clear(p);
diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua
index 4f4ac40ce9..6fad52ba75 100644
--- a/src/nvim/api/keysets.lua
+++ b/src/nvim/api/keysets.lua
@@ -104,7 +104,6 @@ return {
"reverse";
"nocombine";
"default";
- "global";
"cterm";
"foreground"; "fg";
"background"; "bg";
@@ -112,9 +111,9 @@ return {
"ctermbg";
"special"; "sp";
"link";
+ "global_link";
"fallback";
"blend";
- "temp";
};
highlight_cterm = {
"bold";
diff --git a/src/nvim/api/private/dispatch.h b/src/nvim/api/private/dispatch.h
index 4b7c394944..f92b205531 100644
--- a/src/nvim/api/private/dispatch.h
+++ b/src/nvim/api/private/dispatch.h
@@ -5,18 +5,23 @@
typedef Object (*ApiDispatchWrapper)(uint64_t channel_id,
Array args,
+ Arena *arena,
Error *error);
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
-typedef struct {
+struct MsgpackRpcRequestHandler {
const char *name;
ApiDispatchWrapper fn;
bool fast; // Function is safe to be executed immediately while running the
// uv loop (the loop is run very frequently due to breakcheck).
// If "fast" is false, the function is deferred, i e the call will
// be put in the event queue, for safe handling later.
-} MsgpackRpcRequestHandler;
+ bool arena_return; // return value is allocated in the arena (or statically)
+ // and should not be freed as such.
+};
+
+extern const MsgpackRpcRequestHandler method_handlers[];
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/dispatch.h.generated.h"
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index fad75d55be..ebcf6cca6d 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -19,8 +19,8 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_eval.h"
#include "nvim/extmark.h"
-#include "nvim/fileio.h"
#include "nvim/highlight_group.h"
#include "nvim/lib/kvec.h"
#include "nvim/lua/executor.h"
@@ -54,10 +54,11 @@ void try_enter(TryState *const tstate)
// save_dbg_stuff()/restore_dbg_stuff().
*tstate = (TryState) {
.current_exception = current_exception,
- .msg_list = (const struct msglist *const *)msg_list,
+ .msg_list = (const msglist_T *const *)msg_list,
.private_msg_list = NULL,
.trylevel = trylevel,
.got_int = got_int,
+ .did_throw = did_throw,
.need_rethrow = need_rethrow,
.did_emsg = did_emsg,
};
@@ -65,6 +66,7 @@ void try_enter(TryState *const tstate)
current_exception = NULL;
trylevel = 1;
got_int = false;
+ did_throw = false;
need_rethrow = false;
did_emsg = false;
}
@@ -85,14 +87,16 @@ bool try_leave(const TryState *const tstate, Error *const err)
assert(trylevel == 0);
assert(!need_rethrow);
assert(!got_int);
+ assert(!did_throw);
assert(!did_emsg);
assert(msg_list == &tstate->private_msg_list);
assert(*msg_list == NULL);
assert(current_exception == NULL);
- msg_list = (struct msglist **)tstate->msg_list;
+ msg_list = (msglist_T **)tstate->msg_list;
current_exception = tstate->current_exception;
trylevel = tstate->trylevel;
got_int = tstate->got_int;
+ did_throw = tstate->did_throw;
need_rethrow = tstate->need_rethrow;
did_emsg = tstate->did_emsg;
return ret;
@@ -127,7 +131,7 @@ bool try_end(Error *err)
force_abort = false;
if (got_int) {
- if (current_exception) {
+ if (did_throw) {
// If we got an interrupt, discard the current exception
discard_current_exception();
}
@@ -146,7 +150,7 @@ bool try_end(Error *err)
if (should_free) {
xfree(msg);
}
- } else if (current_exception) {
+ } else if (did_throw) {
api_set_error(err, kErrorTypeException, "%s", current_exception->value);
discard_current_exception();
}
@@ -618,6 +622,7 @@ void api_clear_error(Error *value)
value->type = kErrorTypeNone;
}
+/// @returns a shared value. caller must not modify it!
Dictionary api_metadata(void)
{
static Dictionary metadata = ARRAY_DICT_INIT;
@@ -630,7 +635,7 @@ Dictionary api_metadata(void)
init_type_metadata(&metadata);
}
- return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary;
+ return metadata;
}
static void init_function_metadata(Dictionary *metadata)
@@ -715,36 +720,40 @@ static void init_type_metadata(Dictionary *metadata)
PUT(*metadata, "types", DICTIONARY_OBJ(types));
}
-String copy_string(String str)
+// all the copy_[object] functions allow arena=NULL,
+// then global allocations are used, and the resulting object
+// should be freed with an api_free_[object] function
+
+String copy_string(String str, Arena *arena)
{
if (str.data != NULL) {
- return (String){ .data = xmemdupz(str.data, str.size), .size = str.size };
+ return (String){ .data = arena_memdupz(arena, str.data, str.size), .size = str.size };
} else {
return (String)STRING_INIT;
}
}
-Array copy_array(Array array)
+Array copy_array(Array array, Arena *arena)
{
- Array rv = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, array.size);
for (size_t i = 0; i < array.size; i++) {
- ADD(rv, copy_object(array.items[i]));
+ ADD(rv, copy_object(array.items[i], arena));
}
return rv;
}
-Dictionary copy_dictionary(Dictionary dict)
+Dictionary copy_dictionary(Dictionary dict, Arena *arena)
{
- Dictionary rv = ARRAY_DICT_INIT;
+ Dictionary rv = arena_dict(arena, dict.size);
for (size_t i = 0; i < dict.size; i++) {
KeyValuePair item = dict.items[i];
- PUT(rv, item.key.data, copy_object(item.value));
+ PUT_C(rv, copy_string(item.key, arena).data, copy_object(item.value, arena));
}
return rv;
}
/// Creates a deep clone of an object
-Object copy_object(Object obj)
+Object copy_object(Object obj, Arena *arena)
{
switch (obj.type) {
case kObjectTypeBuffer:
@@ -757,13 +766,13 @@ Object copy_object(Object obj)
return obj;
case kObjectTypeString:
- return STRING_OBJ(copy_string(obj.data.string));
+ return STRING_OBJ(copy_string(obj.data.string, arena));
case kObjectTypeArray:
- return ARRAY_OBJ(copy_array(obj.data.array));
+ return ARRAY_OBJ(copy_array(obj.data.array, arena));
case kObjectTypeDictionary:
- return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
+ return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary, arena));
case kObjectTypeLuaRef:
return LUAREF_OBJ(api_new_luaref(obj.data.luaref));
@@ -844,7 +853,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
goto free_exit;
}
- String str = copy_string(chunk.items[0].data.string);
+ String str = copy_string(chunk.items[0].data.string, NULL);
int attr = 0;
if (chunk.size == 2) {
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index 1441da853c..2157ad0ec2 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -3,7 +3,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/decoration.h"
-#include "nvim/ex_eval.h"
+#include "nvim/ex_eval_defs.h"
#include "nvim/getchar.h"
#include "nvim/lib/kvec.h"
#include "nvim/memory.h"
@@ -130,10 +130,11 @@ EXTERN PMap(handle_T) tabpage_handles INIT(= MAP_INIT);
/// processed and that “other VimL code†must not be affected.
typedef struct {
except_T *current_exception;
- struct msglist *private_msg_list;
- const struct msglist *const *msg_list;
+ msglist_T *private_msg_list;
+ const msglist_T *const *msg_list;
int trylevel;
int got_int;
+ bool did_throw;
int need_rethrow;
int did_emsg;
} TryState;
@@ -144,8 +145,8 @@ typedef struct {
// TODO(bfredl): prepare error-handling at "top level" (nv_event).
#define TRY_WRAP(code) \
do { \
- struct msglist **saved_msg_list = msg_list; \
- struct msglist *private_msg_list; \
+ msglist_T **saved_msg_list = msg_list; \
+ msglist_T *private_msg_list; \
msg_list = &private_msg_list; \
private_msg_list = NULL; \
code \
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 6239e414a7..e34dcbdb46 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -11,14 +11,14 @@
#include "nvim/api/ui.h"
#include "nvim/channel.h"
#include "nvim/cursor_shape.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/map.h"
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/helpers.h"
#include "nvim/option.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -224,7 +224,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona
ui->event = remote_ui_event;
ui->inspect = remote_ui_inspect;
- memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
+ CLEAR_FIELD(ui->ui_ext);
for (size_t i = 0; i < options.size; i++) {
ui_set_option(ui, true, options.items[i].key, options.items[i].value, err);
@@ -965,7 +965,7 @@ static Array translate_contents(UI *ui, Array contents)
} else {
ADD(new_item, DICTIONARY_OBJ((Dictionary)ARRAY_DICT_INIT));
}
- ADD(new_item, copy_object(item.items[1]));
+ ADD(new_item, copy_object(item.items[1], NULL));
ADD(new_contents, ARRAY_OBJ(new_item));
}
return new_contents;
@@ -978,7 +978,7 @@ static Array translate_firstarg(UI *ui, Array args)
ADD(new_args, ARRAY_OBJ(translate_contents(ui, contents)));
for (size_t i = 1; i < args.size; i++) {
- ADD(new_args, copy_object(args.items[i]));
+ ADD(new_args, copy_object(args.items[i], NULL));
}
return new_args;
}
@@ -1024,7 +1024,7 @@ static void remote_ui_event(UI *ui, char *name, Array args)
Array items = args.items[0].data.array;
Array new_items = ARRAY_DICT_INIT;
for (size_t i = 0; i < items.size; i++) {
- ADD(new_items, copy_object(items.items[i].data.array.items[0]));
+ ADD(new_items, copy_object(items.items[i].data.array.items[0], NULL));
}
ADD_C(new_args, ARRAY_OBJ(new_items));
push_call(ui, "wildmenu_show", new_args);
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index e2f58dba62..b164106cef 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -23,17 +23,19 @@
#include "nvim/context.h"
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -50,11 +52,13 @@
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/process.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
+#include "nvim/runtime.h"
#include "nvim/state.h"
+#include "nvim/statusline.h"
#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -91,7 +95,6 @@ Dictionary nvim_get_hl_by_name(String name, Boolean rgb, Error *err)
}
/// Gets a highlight definition by id. |hlID()|
-///
/// @param hl_id Highlight id as returned by |hlID()|
/// @param rgb Export RGB colors
/// @param[out] err Error details, if any
@@ -180,35 +183,38 @@ void nvim_set_hl(Integer ns_id, String name, Dict(highlight) *val, Error *err)
}
}
-/// Set active namespace for highlights.
-///
-/// NB: this function can be called from async contexts, but the
-/// semantics are not yet well-defined. To start with
-/// |nvim_set_decoration_provider| on_win and on_line callbacks
-/// are explicitly allowed to change the namespace during a redraw cycle.
+/// Set active namespace for highlights. This can be set for a single window,
+/// see |nvim_win_set_hl_ns|.
///
-/// @param ns_id the namespace to activate
+/// @param ns_id the namespace to use
/// @param[out] err Error details, if any
-void nvim__set_hl_ns(Integer ns_id, Error *err)
- FUNC_API_FAST
+void nvim_set_hl_ns(Integer ns_id, Error *err)
+ FUNC_API_SINCE(10)
{
- if (ns_id >= 0) {
- ns_hl_active = (NS)ns_id;
+ if (ns_id < 0) {
+ api_set_error(err, kErrorTypeValidation, "no such namespace");
+ return;
}
- // TODO(bfredl): this is a little bit hackish. Eventually we want a standard
- // event path for redraws caused by "fast" events. This could tie in with
- // better throttling of async events causing redraws, such as non-batched
- // nvim_buf_set_extmark calls from async contexts.
- if (!provider_active && !ns_hl_changed && must_redraw < NOT_VALID) {
- multiqueue_put(main_loop.events, on_redraw_event, 0);
- }
- ns_hl_changed = true;
+ ns_hl_global = (NS)ns_id;
+ hl_check_ns();
+ redraw_all_later(UPD_NOT_VALID);
}
-static void on_redraw_event(void **argv)
+/// Set active namespace for highlights while redrawing.
+///
+/// This function meant to be called while redrawing, primarily from
+/// |nvim_set_decoration_provider| on_win and on_line callbacks, which
+/// are allowed to change the namespace during a redraw cycle.
+///
+/// @param ns_id the namespace to activate
+/// @param[out] err Error details, if any
+void nvim_set_hl_ns_fast(Integer ns_id, Error *err)
+ FUNC_API_SINCE(10)
+ FUNC_API_FAST
{
- redraw_all_later(NOT_VALID);
+ ns_hl_fast = (NS)ns_id;
+ hl_check_ns();
}
/// Sends input-keys to Nvim, subject to various quirks controlled by `mode`
@@ -478,7 +484,7 @@ Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err)
ADD_C(args, INTEGER_OBJ(log_level));
ADD_C(args, DICTIONARY_OBJ(opts));
- return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err);
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, err);
}
/// Calculates the number of display cells occupied by `text`.
@@ -1473,14 +1479,14 @@ void nvim_del_keymap(uint64_t channel_id, String mode, String lhs, Error *err)
/// 1 is the |api-metadata| map (Dictionary).
///
/// @returns 2-tuple [{channel-id}, {api-metadata}]
-Array nvim_get_api_info(uint64_t channel_id)
+Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
FUNC_API_SINCE(1) FUNC_API_FAST FUNC_API_REMOTE_ONLY
{
- Array rv = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, 2);
assert(channel_id <= INT64_MAX);
- ADD(rv, INTEGER_OBJ((int64_t)channel_id));
- ADD(rv, DICTIONARY_OBJ(api_metadata()));
+ ADD_C(rv, INTEGER_OBJ((int64_t)channel_id));
+ ADD_C(rv, DICTIONARY_OBJ(api_metadata()));
return rv;
}
@@ -1539,9 +1545,9 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
FUNC_API_SINCE(4) FUNC_API_REMOTE_ONLY
{
Dictionary info = ARRAY_DICT_INIT;
- PUT(info, "name", copy_object(STRING_OBJ(name)));
+ PUT(info, "name", copy_object(STRING_OBJ(name), NULL));
- version = copy_dictionary(version);
+ version = copy_dictionary(version, NULL);
bool has_major = false;
for (size_t i = 0; i < version.size; i++) {
if (strequal(version.items[i].key.data, "major")) {
@@ -1554,9 +1560,9 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dictionary version,
}
PUT(info, "version", DICTIONARY_OBJ(version));
- PUT(info, "type", copy_object(STRING_OBJ(type)));
- PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods)));
- PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes)));
+ PUT(info, "type", copy_object(STRING_OBJ(type), NULL));
+ PUT(info, "methods", DICTIONARY_OBJ(copy_dictionary(methods, NULL)));
+ PUT(info, "attributes", DICTIONARY_OBJ(copy_dictionary(attributes, NULL)));
rpc_set_client_info(channel_id, info);
}
@@ -1623,11 +1629,11 @@ Array nvim_list_chans(void)
/// an error, it is a three-element array with the zero-based index of the call
/// which resulted in an error, the error type and the error message. If an
/// error occurred, the values from all preceding calls will still be returned.
-Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
+Array nvim_call_atomic(uint64_t channel_id, Array calls, Arena *arena, Error *err)
FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
{
- Array rv = ARRAY_DICT_INIT;
- Array results = ARRAY_DICT_INIT;
+ Array rv = arena_array(arena, 2);
+ Array results = arena_array(arena, calls.size);
Error nested_error = ERROR_INIT;
size_t i; // also used for freeing the variables
@@ -1636,21 +1642,21 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
api_set_error(err,
kErrorTypeValidation,
"Items in calls array must be arrays");
- goto validation_error;
+ goto theend;
}
Array call = calls.items[i].data.array;
if (call.size != 2) {
api_set_error(err,
kErrorTypeValidation,
"Items in calls array must be arrays of size 2");
- goto validation_error;
+ goto theend;
}
if (call.items[0].type != kObjectTypeString) {
api_set_error(err,
kErrorTypeValidation,
"Name must be String");
- goto validation_error;
+ goto theend;
}
String name = call.items[0].data.string;
@@ -1658,7 +1664,7 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
api_set_error(err,
kErrorTypeValidation,
"Args must be Array");
- goto validation_error;
+ goto theend;
}
Array args = call.items[1].data.array;
@@ -1670,29 +1676,32 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err)
if (ERROR_SET(&nested_error)) {
break;
}
- Object result = handler.fn(channel_id, args, &nested_error);
+
+ Object result = handler.fn(channel_id, args, arena, &nested_error);
if (ERROR_SET(&nested_error)) {
// error handled after loop
break;
}
-
- ADD(results, result);
+ // TODO(bfredl): wastefull copy. It could be avoided to encoding to msgpack
+ // directly here. But `result` might become invalid when next api function
+ // is called in the loop.
+ ADD_C(results, copy_object(result, arena));
+ if (!handler.arena_return) {
+ api_free_object(result);
+ }
}
- ADD(rv, ARRAY_OBJ(results));
+ ADD_C(rv, ARRAY_OBJ(results));
if (ERROR_SET(&nested_error)) {
- Array errval = ARRAY_DICT_INIT;
- ADD(errval, INTEGER_OBJ((Integer)i));
- ADD(errval, INTEGER_OBJ(nested_error.type));
- ADD(errval, STRING_OBJ(cstr_to_string(nested_error.msg)));
- ADD(rv, ARRAY_OBJ(errval));
+ Array errval = arena_array(arena, 3);
+ ADD_C(errval, INTEGER_OBJ((Integer)i));
+ ADD_C(errval, INTEGER_OBJ(nested_error.type));
+ ADD_C(errval, STRING_OBJ(copy_string(cstr_as_string(nested_error.msg), arena)));
+ ADD_C(rv, ARRAY_OBJ(errval));
} else {
- ADD(rv, NIL);
+ ADD_C(rv, NIL);
}
- goto theend;
-validation_error:
- api_free_array(results);
theend:
api_clear_error(&nested_error);
return rv;
@@ -1745,7 +1754,7 @@ static void write_msg(String message, bool to_err)
/// @return its argument.
Object nvim__id(Object obj)
{
- return copy_object(obj);
+ return copy_object(obj, NULL);
}
/// Returns array given as argument.
@@ -1758,7 +1767,7 @@ Object nvim__id(Object obj)
/// @return its argument.
Array nvim__id_array(Array arr)
{
- return copy_object(ARRAY_OBJ(arr)).data.array;
+ return copy_array(arr, NULL);
}
/// Returns dictionary given as argument.
@@ -1771,7 +1780,7 @@ Array nvim__id_array(Array arr)
/// @return its argument.
Dictionary nvim__id_dictionary(Dictionary dct)
{
- return copy_object(DICTIONARY_OBJ(dct)).data.dictionary;
+ return copy_dictionary(dct, NULL);
}
/// Returns floating-point value given as argument.
@@ -1797,6 +1806,7 @@ Dictionary nvim__stats(void)
PUT(rv, "log_skip", INTEGER_OBJ(g_stats.log_skip));
PUT(rv, "lua_refcount", INTEGER_OBJ(nlua_get_global_ref_count()));
PUT(rv, "redraw", INTEGER_OBJ(g_stats.redraw));
+ PUT(rv, "arena_alloc_count", INTEGER_OBJ((Integer)arena_alloc_count));
return rv;
}
@@ -1833,11 +1843,9 @@ Array nvim_get_proc_children(Integer pid, Error *err)
if (rv == 2) {
// syscall failed (possibly because of kernel options), try shelling out.
DLOG("fallback to vim._os_proc_children()");
- Array a = ARRAY_DICT_INIT;
+ MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- String s = STATIC_CSTR_AS_STRING("return vim._os_proc_children(...)");
- Object o = nlua_exec(s, a, err);
- api_free_array(a);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_children(...)", a, err);
if (o.type == kObjectTypeArray) {
rvobj = o.data.array;
} else if (!ERROR_SET(err)) {
@@ -1878,12 +1886,9 @@ Object nvim_get_proc(Integer pid, Error *err)
}
#else
// Cross-platform process info APIs are miserable, so use `ps` instead.
- Array a = ARRAY_DICT_INIT;
+ MAXSIZE_TEMP_ARRAY(a, 1);
ADD(a, INTEGER_OBJ(pid));
- String s = cstr_to_string("return vim._os_proc_info(select(1, ...))");
- Object o = nlua_exec(s, a, err);
- api_free_string(s);
- api_free_array(a);
+ Object o = NLUA_EXEC_STATIC("return vim._os_proc_info(...)", a, err);
if (o.type == kObjectTypeArray && o.data.array.size == 0) {
return NIL; // Process not found.
} else if (o.type == kObjectTypeDictionary) {
@@ -2039,7 +2044,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
// Marks are from an open buffer it fnum is non zero
if (mark->fmark.fnum != 0) {
bufnr = mark->fmark.fnum;
- filename = (char *)buflist_nr2name(bufnr, true, true);
+ filename = buflist_nr2name(bufnr, true, true);
allocated = true;
// Marks comes from shada
} else {
@@ -2087,7 +2092,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// 'fillchars'). Treated as single-width even if it isn't.
/// - highlights: (boolean) Return highlight information.
/// - use_winbar: (boolean) Evaluate winbar instead of statusline.
-/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When |TRUE|, {winid}
+/// - use_tabline: (boolean) Evaluate tabline instead of statusline. When true, {winid}
/// is ignored. Mutually exclusive with {use_winbar}.
///
/// @param[out] err Error details, if any.
@@ -2095,7 +2100,7 @@ Array nvim_get_mark(String name, Dictionary opts, Error *err)
/// - str: (string) Characters that will be displayed on the statusline.
/// - width: (number) Display width of the statusline.
/// - highlights: Array containing highlight information of the statusline. Only included when
-/// the "highlights" key in {opts} is |TRUE|. Each element of the array is a
+/// the "highlights" key in {opts} is true. Each element of the array is a
/// |Dictionary| with these keys:
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
/// - group: (string) Name of highlight group.
@@ -2227,7 +2232,7 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
// If first character doesn't have a defined highlight,
// add the default highlight at the beginning of the highlight list
- if (hltab->start == NULL || ((char *)hltab->start - buf) != 0) {
+ if (hltab->start == NULL || (hltab->start - buf) != 0) {
Dictionary hl_info = ARRAY_DICT_INIT;
grpname = get_default_stl_hl(wp, use_winbar);
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 478e146781..f6d0e39327 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -14,8 +14,9 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ops.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
#include "nvim/viml/parser/expressions.h"
@@ -48,6 +49,7 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
{
const int save_msg_silent = msg_silent;
garray_T *const save_capture_ga = capture_ga;
+ const int save_msg_col = msg_col;
garray_T capture_local;
if (output) {
ga_init(&capture_local, 1, 80);
@@ -57,6 +59,7 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
try_start();
if (output) {
msg_silent++;
+ msg_col = 0; // prevent leading spaces
}
const sctx_T save_current_sctx = api_set_sctx(channel_id);
@@ -65,6 +68,8 @@ String nvim_exec(uint64_t channel_id, String src, Boolean output, Error *err)
if (output) {
capture_ga = save_capture_ga;
msg_silent = save_msg_silent;
+ // Put msg_col back where it was, since nothing should have been written.
+ msg_col = save_msg_col;
}
current_sctx = save_current_sctx;
@@ -132,7 +137,7 @@ Object nvim_eval(String expr, Error *err)
if (!recursive) {
force_abort = false;
suppress_errthrow = false;
- current_exception = NULL;
+ did_throw = false;
// `did_emsg` is set by emsg(), which cancels execution.
did_emsg = false;
}
@@ -191,7 +196,7 @@ static Object _call_function(String fn, Array args, dict_T *self, Error *err)
if (!recursive) {
force_abort = false;
suppress_errthrow = false;
- current_exception = NULL;
+ did_throw = false;
// `did_emsg` is set by emsg(), which cancels execution.
did_emsg = false;
}
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 969643eeef..96c560efa1 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -10,9 +10,9 @@
#include "nvim/api/private/helpers.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii.h"
+#include "nvim/drawscreen.h"
#include "nvim/highlight_group.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -167,7 +167,7 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, E
if (fconfig.style == kWinStyleMinimal) {
win_set_minimal_style(wp);
- didset_window_options(wp);
+ didset_window_options(wp, true);
}
return wp->handle;
}
@@ -202,14 +202,14 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
if (!win_new_float(win, false, fconfig, err)) {
return;
}
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
} else {
win_config_float(win, fconfig);
win->w_pos_changed = true;
}
if (fconfig.style == kWinStyleMinimal) {
win_set_minimal_style(win);
- didset_window_options(win);
+ didset_window_options(win, true);
}
}
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 5a4ff70257..5203003369 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -12,12 +12,12 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/ex_docmd.h"
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -118,7 +118,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err)
// make sure cursor is in visible range even if win != curwin
update_topline_win(win);
- redraw_later(win, VALID);
+ redraw_later(win, UPD_VALID);
win->w_redr_status = true;
}
@@ -426,3 +426,28 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
try_end(err);
return res;
}
+
+/// Set highlight namespace for a window. This will use highlights defined in
+/// this namespace, but fall back to global highlights (ns=0) when missing.
+///
+/// This takes predecence over the 'winhighlight' option.
+///
+/// @param ns_id the namespace to use
+/// @param[out] err Error details, if any
+void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)
+ FUNC_API_SINCE(10)
+{
+ win_T *win = find_window_by_handle(window, err);
+ if (!win) {
+ return;
+ }
+
+ // -1 is allowed as inherit global namespace
+ if (ns_id < -1) {
+ api_set_error(err, kErrorTypeValidation, "no such namespace");
+ }
+
+ win->w_ns_hl = (NS)ns_id;
+ win->w_hl_needs_update = true;
+ redraw_later(win, UPD_NOT_VALID);
+}
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c
index 06536e6e2b..b57019286f 100644
--- a/src/nvim/arabic.c
+++ b/src/nvim/arabic.c
@@ -320,22 +320,22 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c)
int backward_combine = !prev_laa && can_join(prev_c, c);
int forward_combine = can_join(c, next_c);
- if (backward_combine && forward_combine) {
- curr_c = (int)curr_a->medial;
- }
- if (backward_combine && !forward_combine) {
- curr_c = (int)curr_a->final;
- }
- if (!backward_combine && forward_combine) {
- curr_c = (int)curr_a->initial;
- }
- if (!backward_combine && !forward_combine) {
- curr_c = (int)curr_a->isolated;
+ if (backward_combine) {
+ if (forward_combine) {
+ curr_c = (int)curr_a->medial;
+ } else {
+ curr_c = (int)curr_a->final;
+ }
+ } else {
+ if (forward_combine) {
+ curr_c = (int)curr_a->initial;
+ } else {
+ curr_c = (int)curr_a->isolated;
+ }
}
}
- // Sanity check -- curr_c should, in the future, never be 0.
- // We should, in the future, insert a fatal error here.
+ // Character missing from the table means using original character.
if (curr_c == NUL) {
curr_c = c;
}
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
new file mode 100644
index 0000000000..70b2e71949
--- /dev/null
+++ b/src/nvim/arglist.c
@@ -0,0 +1,1156 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// arglist.c: functions for dealing with the argument list
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include "nvim/arglist.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/eval.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds2.h"
+#include "nvim/ex_getln.h"
+#include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/mark.h"
+#include "nvim/memory.h"
+#include "nvim/os/input.h"
+#include "nvim/path.h"
+#include "nvim/regexp.h"
+#include "nvim/strings.h"
+#include "nvim/undo.h"
+#include "nvim/version.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "arglist.c.generated.h"
+#endif
+
+enum {
+ AL_SET = 1,
+ AL_ADD = 2,
+ AL_DEL = 3,
+};
+
+/// Clear an argument list: free all file names and reset it to zero entries.
+void alist_clear(alist_T *al)
+{
+#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
+ GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
+}
+
+/// Init an argument list.
+void alist_init(alist_T *al)
+{
+ ga_init(&al->al_ga, (int)sizeof(aentry_T), 5);
+}
+
+/// Remove a reference from an argument list.
+/// Ignored when the argument list is the global one.
+/// If the argument list is no longer used by any window, free it.
+void alist_unlink(alist_T *al)
+{
+ if (al != &global_alist && --al->al_refcount <= 0) {
+ alist_clear(al);
+ xfree(al);
+ }
+}
+
+/// Create a new argument list and use it for the current window.
+void alist_new(void)
+{
+ curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
+ curwin->w_alist->al_refcount = 1;
+ curwin->w_alist->id = ++max_alist_id;
+ alist_init(curwin->w_alist);
+}
+
+#if !defined(UNIX)
+
+/// Expand the file names in the global argument list.
+/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
+/// numbers to be re-used.
+void alist_expand(int *fnum_list, int fnum_len)
+{
+ char *save_p_su = p_su;
+
+ // Don't use 'suffixes' here. This should work like the shell did the
+ // expansion. Also, the vimrc file isn't read yet, thus the user
+ // can't set the options.
+ p_su = empty_option;
+ char **old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT);
+ for (int i = 0; i < GARGCOUNT; i++) {
+ old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname);
+ }
+ int old_arg_count = GARGCOUNT;
+ char **new_arg_files;
+ int new_arg_file_count;
+ if (expand_wildcards(old_arg_count, old_arg_files,
+ &new_arg_file_count, &new_arg_files,
+ EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK
+ && new_arg_file_count > 0) {
+ alist_set(&global_alist, new_arg_file_count, new_arg_files,
+ true, fnum_list, fnum_len);
+ FreeWild(old_arg_count, old_arg_files);
+ }
+ p_su = save_p_su;
+}
+#endif
+
+/// Set the argument list for the current window.
+/// Takes over the allocated files[] and the allocated fnames in it.
+void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len)
+{
+ static int recursive = 0;
+
+ if (recursive) {
+ emsg(_(e_au_recursive));
+ return;
+ }
+ recursive++;
+
+ alist_clear(al);
+ ga_grow(&al->al_ga, count);
+ {
+ for (int i = 0; i < count; i++) {
+ if (got_int) {
+ // When adding many buffers this can take a long time. Allow
+ // interrupting here.
+ while (i < count) {
+ xfree(files[i++]);
+ }
+ break;
+ }
+
+ // May set buffer name of a buffer previously used for the
+ // argument list, so that it's re-used by alist_add.
+ if (fnum_list != NULL && i < fnum_len) {
+ buf_set_name(fnum_list[i], files[i]);
+ }
+
+ alist_add(al, files[i], use_curbuf ? 2 : 1);
+ os_breakcheck();
+ }
+ xfree(files);
+ }
+
+ if (al == &global_alist) {
+ arg_had_last = false;
+ }
+ recursive--;
+}
+
+/// Add file "fname" to argument list "al".
+/// "fname" must have been allocated and "al" must have been checked for room.
+///
+/// @param set_fnum 1: set buffer number; 2: re-use curbuf
+void alist_add(alist_T *al, char *fname, int set_fnum)
+{
+ if (fname == NULL) { // don't add NULL file names
+ return;
+ }
+#ifdef BACKSLASH_IN_FILENAME
+ slash_adjust(fname);
+#endif
+ AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname;
+ if (set_fnum > 0) {
+ AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
+ buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
+ }
+ al->al_ga.ga_len++;
+}
+
+#if defined(BACKSLASH_IN_FILENAME)
+
+/// Adjust slashes in file names. Called after 'shellslash' was set.
+void alist_slash_adjust(void)
+{
+ for (int i = 0; i < GARGCOUNT; i++) {
+ if (GARGLIST[i].ae_fname != NULL) {
+ slash_adjust(GARGLIST[i].ae_fname);
+ }
+ }
+
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->w_alist != &global_alist) {
+ for (int i = 0; i < WARGCOUNT(wp); i++) {
+ if (WARGLIST(wp)[i].ae_fname != NULL) {
+ slash_adjust(WARGLIST(wp)[i].ae_fname);
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+/// Isolate one argument, taking backticks.
+/// Changes the argument in-place, puts a NUL after it. Backticks remain.
+///
+/// @return a pointer to the start of the next argument.
+static char *do_one_arg(char *str)
+{
+ char *p;
+ bool inbacktick;
+
+ inbacktick = false;
+ for (p = str; *str; str++) {
+ // When the backslash is used for escaping the special meaning of a
+ // character we need to keep it until wildcard expansion.
+ if (rem_backslash(str)) {
+ *p++ = *str++;
+ *p++ = *str;
+ } else {
+ // An item ends at a space not in backticks
+ if (!inbacktick && ascii_isspace(*str)) {
+ break;
+ }
+ if (*str == '`') {
+ inbacktick ^= true;
+ }
+ *p++ = *str;
+ }
+ }
+ str = skipwhite(str);
+ *p = NUL;
+
+ return str;
+}
+
+/// Separate the arguments in "str" and return a list of pointers in the
+/// growarray "gap".
+static void get_arglist(garray_T *gap, char *str, int escaped)
+{
+ ga_init(gap, (int)sizeof(char *), 20);
+ while (*str != NUL) {
+ GA_APPEND(char *, gap, str);
+
+ // If str is escaped, don't handle backslashes or spaces
+ if (!escaped) {
+ return;
+ }
+
+ // Isolate one argument, change it in-place, put a NUL after it.
+ str = do_one_arg(str);
+ }
+}
+
+/// Parse a list of arguments (file names), expand them and return in
+/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
+///
+/// @return FAIL or OK.
+int get_arglist_exp(char *str, int *fcountp, char ***fnamesp, bool wig)
+{
+ garray_T ga;
+ int i;
+
+ get_arglist(&ga, str, true);
+
+ if (wig) {
+ i = expand_wildcards(ga.ga_len, ga.ga_data,
+ fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
+ } else {
+ i = gen_expand_wildcards(ga.ga_len, ga.ga_data,
+ fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
+ }
+
+ ga_clear(&ga);
+ return i;
+}
+
+/// Check the validity of the arg_idx for each other window.
+static void alist_check_arg_idx(void)
+{
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_alist == curwin->w_alist) {
+ check_arg_idx(win);
+ }
+ }
+}
+
+/// Add files[count] to the arglist of the current window after arg "after".
+/// The file names in files[count] must have been allocated and are taken over.
+/// Files[] itself is not taken over.
+///
+/// @param after: where to add: 0 = before first one
+/// @param will_edit will edit adding argument
+static void alist_add_list(int count, char **files, int after, bool will_edit)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int old_argcount = ARGCOUNT;
+ ga_grow(&ALIST(curwin)->al_ga, count);
+ {
+ if (after < 0) {
+ after = 0;
+ }
+ if (after > ARGCOUNT) {
+ after = ARGCOUNT;
+ }
+ if (after < ARGCOUNT) {
+ memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
+ (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
+ }
+ for (int i = 0; i < count; i++) {
+ const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
+ ARGLIST[after + i].ae_fname = files[i];
+ ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
+ }
+ ALIST(curwin)->al_ga.ga_len += count;
+ if (old_argcount > 0 && curwin->w_arg_idx >= after) {
+ curwin->w_arg_idx += count;
+ }
+ return;
+ }
+}
+
+/// @param str
+/// @param what
+/// AL_SET: Redefine the argument list to 'str'.
+/// AL_ADD: add files in 'str' to the argument list after "after".
+/// AL_DEL: remove files in 'str' from the argument list.
+/// @param after
+/// 0 means before first one
+/// @param will_edit will edit added argument
+///
+/// @return FAIL for failure, OK otherwise.
+static int do_arglist(char *str, int what, int after, bool will_edit)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T new_ga;
+ int exp_count;
+ char **exp_files;
+ char *p;
+ int match;
+ int arg_escaped = true;
+
+ // Set default argument for ":argadd" command.
+ if (what == AL_ADD && *str == NUL) {
+ if (curbuf->b_ffname == NULL) {
+ return FAIL;
+ }
+ str = curbuf->b_fname;
+ arg_escaped = false;
+ }
+
+ // Collect all file name arguments in "new_ga".
+ get_arglist(&new_ga, str, arg_escaped);
+
+ if (what == AL_DEL) {
+ regmatch_T regmatch;
+ bool didone;
+
+ // Delete the items: use each item as a regexp and find a match in the
+ // argument list.
+ regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
+ for (int i = 0; i < new_ga.ga_len && !got_int; i++) {
+ p = ((char **)new_ga.ga_data)[i];
+ p = file_pat_to_reg_pat(p, NULL, NULL, false);
+ if (p == NULL) {
+ break;
+ }
+ regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
+ if (regmatch.regprog == NULL) {
+ xfree(p);
+ break;
+ }
+
+ didone = false;
+ for (match = 0; match < ARGCOUNT; match++) {
+ if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
+ didone = true;
+ xfree(ARGLIST[match].ae_fname);
+ memmove(ARGLIST + match, ARGLIST + match + 1,
+ (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len--;
+ if (curwin->w_arg_idx > match) {
+ curwin->w_arg_idx--;
+ }
+ match--;
+ }
+ }
+
+ vim_regfree(regmatch.regprog);
+ xfree(p);
+ if (!didone) {
+ semsg(_(e_nomatch2), ((char **)new_ga.ga_data)[i]);
+ }
+ }
+ ga_clear(&new_ga);
+ } else {
+ int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data,
+ &exp_count, &exp_files,
+ EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
+ ga_clear(&new_ga);
+ if (i == FAIL || exp_count == 0) {
+ emsg(_(e_nomatch));
+ return FAIL;
+ }
+
+ if (what == AL_ADD) {
+ alist_add_list(exp_count, exp_files, after, will_edit);
+ xfree(exp_files);
+ } else {
+ assert(what == AL_SET);
+ alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
+ }
+ }
+
+ alist_check_arg_idx();
+
+ return OK;
+}
+
+/// Redefine the argument list.
+void set_arglist(char *str)
+{
+ do_arglist(str, AL_SET, 0, false);
+}
+
+/// @return true if window "win" is editing the file at the current argument
+/// index.
+bool editing_arg_idx(win_T *win)
+{
+ return !(win->w_arg_idx >= WARGCOUNT(win)
+ || (win->w_buffer->b_fnum
+ != WARGLIST(win)[win->w_arg_idx].ae_fnum
+ && (win->w_buffer->b_ffname == NULL
+ || !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]),
+ win->w_buffer->b_ffname, true,
+ true) & kEqualFiles))));
+}
+
+/// Check if window "win" is editing the w_arg_idx file in its argument list.
+void check_arg_idx(win_T *win)
+{
+ if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) {
+ // We are not editing the current entry in the argument list.
+ // Set "arg_had_last" if we are editing the last one.
+ win->w_arg_idx_invalid = true;
+ if (win->w_arg_idx != WARGCOUNT(win) - 1
+ && arg_had_last == false
+ && ALIST(win) == &global_alist
+ && GARGCOUNT > 0
+ && win->w_arg_idx < GARGCOUNT
+ && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
+ || (win->w_buffer->b_ffname != NULL
+ && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]),
+ win->w_buffer->b_ffname, true, true)
+ & kEqualFiles)))) {
+ arg_had_last = true;
+ }
+ } else {
+ // We are editing the current entry in the argument list.
+ // Set "arg_had_last" if it's also the last one
+ win->w_arg_idx_invalid = false;
+ if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {
+ arg_had_last = true;
+ }
+ }
+}
+
+/// ":args", ":argslocal" and ":argsglobal".
+void ex_args(exarg_T *eap)
+{
+ if (eap->cmdidx != CMD_args) {
+ alist_unlink(ALIST(curwin));
+ if (eap->cmdidx == CMD_argglobal) {
+ ALIST(curwin) = &global_alist;
+ } else { // eap->cmdidx == CMD_arglocal
+ alist_new();
+ }
+ }
+
+ if (*eap->arg != NUL) {
+ // ":args file ..": define new argument list, handle like ":next"
+ // Also for ":argslocal file .." and ":argsglobal file ..".
+ ex_next(eap);
+ } else if (eap->cmdidx == CMD_args) {
+ // ":args": list arguments.
+ if (ARGCOUNT > 0) {
+ char **items = xmalloc(sizeof(char *) * (size_t)ARGCOUNT);
+ // Overwrite the command, for a short list there is no scrolling
+ // required and no wait_return().
+ gotocmdline(true);
+ for (int i = 0; i < ARGCOUNT; i++) {
+ items[i] = alist_name(&ARGLIST[i]);
+ }
+ list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
+ xfree(items);
+ }
+ } else if (eap->cmdidx == CMD_arglocal) {
+ garray_T *gap = &curwin->w_alist->al_ga;
+
+ // ":argslocal": make a local copy of the global argument list.
+ ga_grow(gap, GARGCOUNT);
+ for (int i = 0; i < GARGCOUNT; i++) {
+ if (GARGLIST[i].ae_fname != NULL) {
+ AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname = xstrdup(GARGLIST[i].ae_fname);
+ AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum = GARGLIST[i].ae_fnum;
+ gap->ga_len++;
+ }
+ }
+ }
+}
+
+/// ":previous", ":sprevious", ":Next" and ":sNext".
+void ex_previous(exarg_T *eap)
+{
+ // If past the last one already, go to the last one.
+ if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) {
+ do_argfile(eap, ARGCOUNT - 1);
+ } else {
+ do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
+ }
+}
+
+/// ":rewind", ":first", ":sfirst" and ":srewind".
+void ex_rewind(exarg_T *eap)
+{
+ do_argfile(eap, 0);
+}
+
+/// ":last" and ":slast".
+void ex_last(exarg_T *eap)
+{
+ do_argfile(eap, ARGCOUNT - 1);
+}
+
+/// ":argument" and ":sargument".
+void ex_argument(exarg_T *eap)
+{
+ int i;
+
+ if (eap->addr_count > 0) {
+ i = (int)eap->line2 - 1;
+ } else {
+ i = curwin->w_arg_idx;
+ }
+ do_argfile(eap, i);
+}
+
+/// Edit file "argn" of the argument lists.
+void do_argfile(exarg_T *eap, int argn)
+{
+ int other;
+ char *p;
+ int old_arg_idx = curwin->w_arg_idx;
+
+ if (argn < 0 || argn >= ARGCOUNT) {
+ if (ARGCOUNT <= 1) {
+ emsg(_("E163: There is only one file to edit"));
+ } else if (argn < 0) {
+ emsg(_("E164: Cannot go before first file"));
+ } else {
+ emsg(_("E165: Cannot go beyond last file"));
+ }
+ } else {
+ setpcmark();
+
+ // split window or create new tab page first
+ if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
+ if (win_split(0, 0) == FAIL) {
+ return;
+ }
+ RESET_BINDING(curwin);
+ } else {
+ // if 'hidden' set, only check for changed file when re-editing
+ // the same buffer
+ other = true;
+ if (buf_hide(curbuf)) {
+ p = fix_fname(alist_name(&ARGLIST[argn]));
+ other = otherfile(p);
+ xfree(p);
+ }
+ if ((!buf_hide(curbuf) || !other)
+ && check_changed(curbuf, CCGD_AW
+ | (other ? 0 : CCGD_MULTWIN)
+ | (eap->forceit ? CCGD_FORCEIT : 0)
+ | CCGD_EXCMD)) {
+ return;
+ }
+ }
+
+ curwin->w_arg_idx = argn;
+ if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
+ arg_had_last = true;
+ }
+
+ // Edit the file; always use the last known line number.
+ // When it fails (e.g. Abort for already edited file) restore the
+ // argument index.
+ if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
+ eap, ECMD_LAST,
+ (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
+ + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
+ curwin->w_arg_idx = old_arg_idx;
+ } else if (eap->cmdidx != CMD_argdo) {
+ // like Vi: set the mark where the cursor is in the file.
+ setmark('\'');
+ }
+ }
+}
+
+/// ":next", and commands that behave like it.
+void ex_next(exarg_T *eap)
+{
+ int i;
+
+ // check for changed buffer now, if this fails the argument list is not
+ // redefined.
+ if (buf_hide(curbuf)
+ || eap->cmdidx == CMD_snext
+ || !check_changed(curbuf, CCGD_AW
+ | (eap->forceit ? CCGD_FORCEIT : 0)
+ | CCGD_EXCMD)) {
+ if (*eap->arg != NUL) { // redefine file list
+ if (do_arglist(eap->arg, AL_SET, 0, true) == FAIL) {
+ return;
+ }
+ i = 0;
+ } else {
+ i = curwin->w_arg_idx + (int)eap->line2;
+ }
+ do_argfile(eap, i);
+ }
+}
+
+/// ":argdedupe"
+void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED)
+{
+ for (int i = 0; i < ARGCOUNT; i++) {
+ for (int j = i + 1; j < ARGCOUNT; j++) {
+ if (FNAMECMP(ARGLIST[i].ae_fname, ARGLIST[j].ae_fname) == 0) {
+ xfree(ARGLIST[j].ae_fname);
+ memmove(ARGLIST + j, ARGLIST + j + 1,
+ (size_t)(ARGCOUNT - j - 1) * sizeof(aentry_T));
+ ARGCOUNT--;
+
+ if (curwin->w_arg_idx == j) {
+ curwin->w_arg_idx = i;
+ } else if (curwin->w_arg_idx > j) {
+ curwin->w_arg_idx--;
+ }
+
+ j--;
+ }
+ }
+ }
+}
+
+/// ":argedit"
+void ex_argedit(exarg_T *eap)
+{
+ int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
+ // Whether curbuf will be reused, curbuf->b_ffname will be set.
+ bool curbuf_is_reusable = curbuf_reusable();
+
+ if (do_arglist(eap->arg, AL_ADD, i, true) == FAIL) {
+ return;
+ }
+ maketitle();
+
+ if (curwin->w_arg_idx == 0
+ && (curbuf->b_ml.ml_flags & ML_EMPTY)
+ && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
+ i = 0;
+ }
+ // Edit the argument.
+ if (i < ARGCOUNT) {
+ do_argfile(eap, i);
+ }
+}
+
+/// ":argadd"
+void ex_argadd(exarg_T *eap)
+{
+ do_arglist(eap->arg, AL_ADD,
+ eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
+ false);
+ maketitle();
+}
+
+/// ":argdelete"
+void ex_argdelete(exarg_T *eap)
+{
+ if (eap->addr_count > 0 || *eap->arg == NUL) {
+ // ":argdel" works like ":.argdel"
+ if (eap->addr_count == 0) {
+ if (curwin->w_arg_idx >= ARGCOUNT) {
+ emsg(_("E610: No argument to delete"));
+ return;
+ }
+ eap->line1 = eap->line2 = curwin->w_arg_idx + 1;
+ } else if (eap->line2 > ARGCOUNT) {
+ // ":1,4argdel": Delete all arguments in the range.
+ eap->line2 = ARGCOUNT;
+ }
+ linenr_T n = eap->line2 - eap->line1 + 1;
+ if (*eap->arg != NUL) {
+ // Can't have both a range and an argument.
+ emsg(_(e_invarg));
+ } else if (n <= 0) {
+ // Don't give an error for ":%argdel" if the list is empty.
+ if (eap->line1 != 1 || eap->line2 != 0) {
+ emsg(_(e_invrange));
+ }
+ } else {
+ for (linenr_T i = eap->line1; i <= eap->line2; i++) {
+ xfree(ARGLIST[i - 1].ae_fname);
+ }
+ memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
+ (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len -= (int)n;
+ if (curwin->w_arg_idx >= eap->line2) {
+ curwin->w_arg_idx -= (int)n;
+ } else if (curwin->w_arg_idx > eap->line1) {
+ curwin->w_arg_idx = (int)eap->line1;
+ }
+ if (ARGCOUNT == 0) {
+ curwin->w_arg_idx = 0;
+ } else if (curwin->w_arg_idx >= ARGCOUNT) {
+ curwin->w_arg_idx = ARGCOUNT - 1;
+ }
+ }
+ } else {
+ do_arglist(eap->arg, AL_DEL, 0, false);
+ }
+ maketitle();
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// argedit and argdelete commands.
+char *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx >= ARGCOUNT) {
+ return NULL;
+ }
+ return alist_name(&ARGLIST[idx]);
+}
+
+/// Get the file name for an argument list entry.
+char *alist_name(aentry_T *aep)
+{
+ buf_T *bp;
+
+ // Use the name from the associated buffer if it exists.
+ bp = buflist_findnr(aep->ae_fnum);
+ if (bp == NULL || bp->b_fname == NULL) {
+ return aep->ae_fname;
+ }
+ return bp->b_fname;
+}
+
+/// do_arg_all(): Open up to 'count' windows, one for each argument.
+///
+/// @param forceit hide buffers in current windows
+/// @param keep_tabs keep current tabs, for ":tab drop file"
+static void do_arg_all(int count, int forceit, int keep_tabs)
+{
+ uint8_t *opened; // Array of weight for which args are open:
+ // 0: not opened
+ // 1: opened in other tab
+ // 2: opened in curtab
+ // 3: opened in curtab and curwin
+
+ int opened_len; // length of opened[]
+ int use_firstwin = false; // use first window for arglist
+ bool tab_drop_empty_window = false;
+ int split_ret = OK;
+ bool p_ea_save;
+ alist_T *alist; // argument list to be used
+ buf_T *buf;
+ tabpage_T *tpnext;
+ int had_tab = cmdmod.cmod_tab;
+ win_T *old_curwin, *last_curwin;
+ tabpage_T *old_curtab, *last_curtab;
+ win_T *new_curwin = NULL;
+ tabpage_T *new_curtab = NULL;
+
+ assert(firstwin != NULL); // satisfy coverity
+
+ if (ARGCOUNT <= 0) {
+ // Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
+ return;
+ }
+ setpcmark();
+
+ opened_len = ARGCOUNT;
+ opened = xcalloc((size_t)opened_len, 1);
+
+ // Autocommands may do anything to the argument list. Make sure it's not
+ // freed while we are working here by "locking" it. We still have to
+ // watch out for its size to be changed.
+ alist = curwin->w_alist;
+ alist->al_refcount++;
+
+ old_curwin = curwin;
+ old_curtab = curtab;
+
+ // Try closing all windows that are not in the argument list.
+ // Also close windows that are not full width;
+ // When 'hidden' or "forceit" set the buffer becomes hidden.
+ // Windows that have a changed buffer and can't be hidden won't be closed.
+ // When the ":tab" modifier was used do this for all tab pages.
+ if (had_tab > 0) {
+ goto_tabpage_tp(first_tabpage, true, true);
+ }
+ for (;;) {
+ win_T *wpnext = NULL;
+ tpnext = curtab->tp_next;
+ for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
+ int i;
+ wpnext = wp->w_next;
+ buf = wp->w_buffer;
+ if (buf->b_ffname == NULL
+ || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
+ i = opened_len;
+ } else {
+ // check if the buffer in this window is in the arglist
+ for (i = 0; i < opened_len; i++) {
+ if (i < alist->al_ga.ga_len
+ && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
+ || path_full_compare(alist_name(&AARGLIST(alist)[i]),
+ buf->b_ffname,
+ true, true) & kEqualFiles)) {
+ int weight = 1;
+
+ if (old_curtab == curtab) {
+ weight++;
+ if (old_curwin == wp) {
+ weight++;
+ }
+ }
+
+ if (weight > (int)opened[i]) {
+ opened[i] = (uint8_t)weight;
+ if (i == 0) {
+ if (new_curwin != NULL) {
+ new_curwin->w_arg_idx = opened_len;
+ }
+ new_curwin = wp;
+ new_curtab = curtab;
+ }
+ } else if (keep_tabs) {
+ i = opened_len;
+ }
+
+ if (wp->w_alist != alist) {
+ // Use the current argument list for all windows containing a file from it.
+ alist_unlink(wp->w_alist);
+ wp->w_alist = alist;
+ wp->w_alist->al_refcount++;
+ }
+ break;
+ }
+ }
+ }
+ wp->w_arg_idx = i;
+
+ if (i == opened_len && !keep_tabs) { // close this window
+ if (buf_hide(buf) || forceit || buf->b_nwindows > 1
+ || !bufIsChanged(buf)) {
+ // If the buffer was changed, and we would like to hide it, try autowriting.
+ if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
+ bufref_T bufref;
+ set_bufref(&bufref, buf);
+ (void)autowrite(buf, false);
+ // Check if autocommands removed the window.
+ if (!win_valid(wp) || !bufref_valid(&bufref)) {
+ wpnext = firstwin; // Start all over...
+ continue;
+ }
+ }
+ // don't close last window
+ if (ONE_WINDOW
+ && (first_tabpage->tp_next == NULL || !had_tab)) {
+ use_firstwin = true;
+ } else {
+ win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
+ // check if autocommands removed the next window
+ if (!win_valid(wpnext)) {
+ // start all over...
+ wpnext = firstwin;
+ }
+ }
+ }
+ }
+ }
+
+ // Without the ":tab" modifier only do the current tab page.
+ if (had_tab == 0 || tpnext == NULL) {
+ break;
+ }
+
+ // check if autocommands removed the next tab page
+ if (!valid_tabpage(tpnext)) {
+ tpnext = first_tabpage; // start all over...
+ }
+ goto_tabpage_tp(tpnext, true, true);
+ }
+
+ // Open a window for files in the argument list that don't have one.
+ // ARGCOUNT may change while doing this, because of autocommands.
+ if (count > opened_len || count <= 0) {
+ count = opened_len;
+ }
+
+ // Don't execute Win/Buf Enter/Leave autocommands here.
+ autocmd_no_enter++;
+ autocmd_no_leave++;
+ last_curwin = curwin;
+ last_curtab = curtab;
+ win_enter(lastwin, false);
+ // ":tab drop file" should re-use an empty window to avoid "--remote-tab"
+ // leaving an empty tab page when executed locally.
+ if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
+ && curbuf->b_ffname == NULL && !curbuf->b_changed) {
+ use_firstwin = true;
+ tab_drop_empty_window = true;
+ }
+
+ for (int i = 0; i < count && !got_int; i++) {
+ if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
+ arg_had_last = true;
+ }
+ if (opened[i] > 0) {
+ // Move the already present window to below the current window
+ if (curwin->w_arg_idx != i) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_arg_idx == i) {
+ if (keep_tabs) {
+ new_curwin = wp;
+ new_curtab = curtab;
+ } else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
+ emsg(_("E249: window layout changed unexpectedly"));
+ i = count;
+ break;
+ } else {
+ win_move_after(wp, curwin);
+ }
+ break;
+ }
+ }
+ }
+ } else if (split_ret == OK) {
+ // trigger events for tab drop
+ if (tab_drop_empty_window && i == count - 1) {
+ autocmd_no_enter--;
+ }
+ if (!use_firstwin) { // split current window
+ p_ea_save = p_ea;
+ p_ea = true; // use space from all windows
+ split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
+ p_ea = p_ea_save;
+ if (split_ret == FAIL) {
+ continue;
+ }
+ } else { // first window: do autocmd for leaving this buffer
+ autocmd_no_leave--;
+ }
+
+ // edit file "i"
+ curwin->w_arg_idx = i;
+ if (i == 0) {
+ new_curwin = curwin;
+ new_curtab = curtab;
+ }
+ (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
+ ((buf_hide(curwin->w_buffer)
+ || bufIsChanged(curwin->w_buffer))
+ ? ECMD_HIDE : 0) + ECMD_OLDBUF,
+ curwin);
+ if (tab_drop_empty_window && i == count - 1) {
+ autocmd_no_enter++;
+ }
+ if (use_firstwin) {
+ autocmd_no_leave++;
+ }
+ use_firstwin = false;
+ }
+ os_breakcheck();
+
+ // When ":tab" was used open a new tab for a new window repeatedly.
+ if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
+ cmdmod.cmod_tab = 9999;
+ }
+ }
+
+ // Remove the "lock" on the argument list.
+ alist_unlink(alist);
+
+ autocmd_no_enter--;
+ // restore last referenced tabpage's curwin
+ if (last_curtab != new_curtab) {
+ if (valid_tabpage(last_curtab)) {
+ goto_tabpage_tp(last_curtab, true, true);
+ }
+ if (win_valid(last_curwin)) {
+ win_enter(last_curwin, false);
+ }
+ }
+ // to window with first arg
+ if (valid_tabpage(new_curtab)) {
+ goto_tabpage_tp(new_curtab, true, true);
+ }
+ if (win_valid(new_curwin)) {
+ win_enter(new_curwin, false);
+ }
+
+ autocmd_no_leave--;
+ xfree(opened);
+}
+
+/// ":all" and ":sall".
+/// Also used for ":tab drop file ..." after setting the argument list.
+void ex_all(exarg_T *eap)
+{
+ if (eap->addr_count == 0) {
+ eap->line2 = 9999;
+ }
+ do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop);
+}
+
+/// Concatenate all files in the argument list, separated by spaces, and return
+/// it in one allocated string.
+/// Spaces and backslashes in the file names are escaped with a backslash.
+char *arg_all(void)
+{
+ char *retval = NULL;
+
+ // Do this loop two times:
+ // first time: compute the total length
+ // second time: concatenate the names
+ for (;;) {
+ int len = 0;
+ for (int idx = 0; idx < ARGCOUNT; idx++) {
+ char *p = alist_name(&ARGLIST[idx]);
+ if (p == NULL) {
+ continue;
+ }
+ if (len > 0) {
+ // insert a space in between names
+ if (retval != NULL) {
+ retval[len] = ' ';
+ }
+ len++;
+ }
+ for (; *p != NUL; p++) {
+ if (*p == ' '
+#ifndef BACKSLASH_IN_FILENAME
+ || *p == '\\'
+#endif
+ || *p == '`') {
+ // insert a backslash
+ if (retval != NULL) {
+ retval[len] = '\\';
+ }
+ len++;
+ }
+ if (retval != NULL) {
+ retval[len] = *p;
+ }
+ len++;
+ }
+ }
+
+ // second time: break here
+ if (retval != NULL) {
+ retval[len] = NUL;
+ break;
+ }
+
+ // allocate memory
+ retval = xmalloc((size_t)len + 1);
+ }
+
+ return retval;
+}
+
+/// "argc([window id])" function
+void f_argc(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // use the current window
+ rettv->vval.v_number = ARGCOUNT;
+ } else if (argvars[0].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[0]) == -1) {
+ // use the global argument list
+ rettv->vval.v_number = GARGCOUNT;
+ } else {
+ // use the argument list of the specified window
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ if (wp != NULL) {
+ rettv->vval.v_number = WARGCOUNT(wp);
+ } else {
+ rettv->vval.v_number = -1;
+ }
+ }
+}
+
+/// "argidx()" function
+void f_argidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = curwin->w_arg_idx;
+}
+
+/// "arglistid()" function
+void f_arglistid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+ win_T *wp = find_tabwin(&argvars[0], &argvars[1]);
+ if (wp != NULL) {
+ rettv->vval.v_number = wp->w_alist->id;
+ }
+}
+
+/// Get the argument list for a given window
+static void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
+{
+ tv_list_alloc_ret(rettv, argcount);
+ if (arglist != NULL) {
+ for (int idx = 0; idx < argcount; idx++) {
+ tv_list_append_string(rettv->vval.v_list,
+ (const char *)alist_name(&arglist[idx]), -1);
+ }
+ }
+}
+
+/// "argv(nr)" function
+void f_argv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ aentry_T *arglist = NULL;
+ int argcount = -1;
+
+ if (argvars[0].v_type != VAR_UNKNOWN) {
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ arglist = ARGLIST;
+ argcount = ARGCOUNT;
+ } else if (argvars[1].v_type == VAR_NUMBER
+ && tv_get_number(&argvars[1]) == -1) {
+ arglist = GARGLIST;
+ argcount = GARGCOUNT;
+ } else {
+ win_T *wp = find_win_by_nr_or_id(&argvars[1]);
+ if (wp != NULL) {
+ // Use the argument list of the specified window
+ arglist = WARGLIST(wp);
+ argcount = WARGCOUNT(wp);
+ }
+ }
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ int idx = (int)tv_get_number_chk(&argvars[0], NULL);
+ if (arglist != NULL && idx >= 0 && idx < argcount) {
+ rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
+ } else if (idx == -1) {
+ get_arglist_as_rettv(arglist, argcount, rettv);
+ }
+ } else {
+ get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
+ }
+}
diff --git a/src/nvim/arglist.h b/src/nvim/arglist.h
new file mode 100644
index 0000000000..b2e0f411d4
--- /dev/null
+++ b/src/nvim/arglist.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_ARGLIST_H
+#define NVIM_ARGLIST_H
+
+#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "arglist.h.generated.h"
+#endif
+
+#endif // NVIM_ARGLIST_H
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index bbb044fba3..1c1de214cd 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -11,20 +11,25 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
#include "nvim/map.h"
-#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/ui_compositor.h"
@@ -681,7 +686,7 @@ const char *event_nr2name(event_T event)
static bool event_ignored(event_T event)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- char *p = (char *)p_ei;
+ char *p = p_ei;
while (*p != NUL) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
@@ -698,7 +703,7 @@ static bool event_ignored(event_T event)
// Return OK when the contents of p_ei is valid, FAIL otherwise.
int check_ei(void)
{
- char *p = (char *)p_ei;
+ char *p = p_ei;
while (*p) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
@@ -719,8 +724,8 @@ int check_ei(void)
// Returns the old value of 'eventignore' in allocated memory.
char *au_event_disable(char *what)
{
- char *save_ei = (char *)vim_strsave(p_ei);
- char *new_ei = (char *)vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
+ char *save_ei = xstrdup(p_ei);
+ char *new_ei = xstrnsave(p_ei, STRLEN(p_ei) + STRLEN(what));
if (*what == ',' && *p_ei == NUL) {
STRCPY(new_ei, what + 1);
} else {
@@ -1114,6 +1119,7 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
if (event == EVENT_WINSCROLLED && !has_event(EVENT_WINSCROLLED)) {
curwin->w_last_topline = curwin->w_topline;
curwin->w_last_leftcol = curwin->w_leftcol;
+ curwin->w_last_skipcol = curwin->w_skipcol;
curwin->w_last_width = curwin->w_width;
curwin->w_last_height = curwin->w_height;
}
@@ -1141,7 +1147,7 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
ac->id = id;
ac->exec = aucmd_exec_copy(aucmd);
ac->script_ctx = current_sctx;
- ac->script_ctx.sc_lnum += sourcing_lnum;
+ ac->script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&ac->script_ctx);
ac->next = NULL;
ac->once = once;
@@ -1715,9 +1721,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
fname = NULL;
} else {
if (event == EVENT_SYNTAX) {
- fname = (char *)buf->b_p_syn;
+ fname = buf->b_p_syn;
} else if (event == EVENT_FILETYPE) {
- fname = (char *)buf->b_p_ft;
+ fname = buf->b_p_ft;
} else {
if (buf->b_sfname != NULL) {
sfname = xstrdup(buf->b_sfname);
@@ -1769,10 +1775,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Don't redraw while doing autocommands.
RedrawingDisabled++;
- char *save_sourcing_name = sourcing_name;
- sourcing_name = NULL; // don't free this one
- linenr_T save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 0; // no line number here
+
+ // name and lnum are filled in later
+ estack_push(ETYPE_AUCMD, NULL, 0);
const sctx_T save_current_sctx = current_sctx;
@@ -1807,16 +1812,15 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
char *tail = path_tail(fname);
// Find first autocommand that matches
- AutoPatCmd patcmd;
- patcmd.curpat = first_autopat[(int)event];
- patcmd.nextcmd = NULL;
- patcmd.group = group;
- patcmd.fname = fname;
- patcmd.sfname = sfname;
- patcmd.tail = tail;
- patcmd.event = event;
- patcmd.arg_bufnr = autocmd_bufnr;
- patcmd.next = NULL;
+ AutoPatCmd patcmd = {
+ .curpat = first_autopat[(int)event],
+ .group = group,
+ .fname = fname,
+ .sfname = sfname,
+ .tail = tail,
+ .event = event,
+ .arg_bufnr = autocmd_bufnr,
+ };
auto_next_pat(&patcmd, false);
// found one, start executing the autocommands
@@ -1876,9 +1880,8 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
autocmd_busy = save_autocmd_busy;
filechangeshell_busy = false;
autocmd_nested = save_autocmd_nested;
- xfree(sourcing_name);
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ xfree(SOURCING_NAME);
+ estack_pop();
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
autocmd_bufnr = save_autocmd_bufnr;
@@ -1982,7 +1985,11 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
AutoCmd *cp;
char *s;
- XFREE_CLEAR(sourcing_name);
+ estack_T *const entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+
+ // Clear the exestack entry for this ETYPE_AUCMD entry.
+ XFREE_CLEAR(entry->es_name);
+ entry->es_info.aucmd = NULL;
for (ap = apc->curpat; ap != NULL && !got_int; ap = ap->next) {
apc->curpat = NULL;
@@ -2007,14 +2014,18 @@ void auto_next_pat(AutoPatCmd *apc, int stop_at_last)
const size_t sourcing_name_len
= (STRLEN(s) + strlen(name) + (size_t)ap->patlen + 1);
- sourcing_name = xmalloc(sourcing_name_len);
- snprintf(sourcing_name, sourcing_name_len, s, name, ap->pat);
+ char *const namep = xmalloc(sourcing_name_len);
+ snprintf(namep, sourcing_name_len, s, name, ap->pat);
if (p_verbose >= 8) {
verbose_enter();
- smsg(_("Executing %s"), sourcing_name);
+ smsg(_("Executing %s"), namep);
verbose_leave();
}
+ // Update the exestack entry for this autocmd.
+ entry->es_name = namep;
+ entry->es_info.aucmd = apc;
+
apc->curpat = ap;
apc->nextcmd = ap->cmds;
// mark last command
@@ -2047,7 +2058,7 @@ static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
PUT(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
- PUT(data, "data", copy_object(*apc->data));
+ PUT(data, "data", copy_object(*apc->data, NULL));
}
int group = apc->curpat->group;
@@ -2146,6 +2157,7 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
// lua code, so that it works properly
autocmd_nested = ac->nested;
current_sctx = ac->script_ctx;
+ acp->script_ctx = current_sctx;
if (ac->exec.type == CALLABLE_CB) {
if (call_autocmd_callback(ac, acp)) {
diff --git a/src/nvim/autocmd.h b/src/nvim/autocmd.h
index a085a03455..75a8a7aaa1 100644
--- a/src/nvim/autocmd.h
+++ b/src/nvim/autocmd.h
@@ -22,19 +22,21 @@ typedef struct {
bool save_VIsual_active; ///< saved VIsual_active
} aco_save_T;
-typedef struct AutoCmd {
+typedef struct AutoCmd_S AutoCmd;
+struct AutoCmd_S {
AucmdExecutable exec;
bool once; // "One shot": removed after execution
bool nested; // If autocommands nest here
bool last; // last command in list
int64_t id; // ID used for uniquely tracking an autocmd.
- sctx_T script_ctx; // script context where defined
+ sctx_T script_ctx; // script context where it is defined
char *desc; // Description for the autocmd.
- struct AutoCmd *next; // Next AutoCmd in list
-} AutoCmd;
+ AutoCmd *next; // Next AutoCmd in list
+};
-typedef struct AutoPat {
- struct AutoPat *next; // next AutoPat in AutoPat list; MUST
+typedef struct AutoPat_S AutoPat;
+struct AutoPat_S {
+ AutoPat *next; // next AutoPat in AutoPat list; MUST
// be the first entry
char *pat; // pattern as typed (NULL when pattern
// has been removed)
@@ -45,10 +47,11 @@ typedef struct AutoPat {
int buflocal_nr; // !=0 for buffer-local AutoPat
char allow_dirs; // Pattern may match whole path
char last; // last pattern for apply_autocmds()
-} AutoPat;
+};
/// Struct used to keep status while executing autocommands for an event.
-typedef struct AutoPatCmd {
+typedef struct AutoPatCmd_S AutoPatCmd;
+struct AutoPatCmd_S {
AutoPat *curpat; // next AutoPat to examine
AutoCmd *nextcmd; // next AutoCmd to execute
int group; // group being used
@@ -56,10 +59,11 @@ typedef struct AutoPatCmd {
char *sfname; // sfname to match with
char *tail; // tail of fname
event_T event; // current event
+ sctx_T script_ctx; // script context where it is defined
int arg_bufnr; // initially equal to <abuf>, set to zero when buf is deleted
Object *data; // arbitrary data
- struct AutoPatCmd *next; // chain of active apc-s for auto-invalidation
-} AutoPatCmd;
+ AutoPatCmd *next; // chain of active apc-s for auto-invalidation
+};
// Set by the apply_autocmds_group function if the given event is equal to
// EVENT_FILETYPE. Used by the readfile function in order to determine if
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index f23a1caf8b..514be4c56b 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -25,17 +25,21 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
@@ -50,6 +54,7 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -62,6 +67,7 @@
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
@@ -69,9 +75,10 @@
#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -85,11 +92,6 @@
# include "buffer.c.generated.h"
#endif
-// Determines how deeply nested %{} blocks will be evaluated in statusline.
-#define MAX_STL_EVAL_DEPTH 100
-
-static char *msg_loclist = N_("[Location List]");
-static char *msg_qflist = N_("[Quickfix List]");
static char *e_auabort = N_("E855: Autocommands caused command to abort");
static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
@@ -163,11 +165,12 @@ static int read_buffer(int read_stdin, exarg_T *eap, int flags)
///
/// @param read_stdin read file from stdin
/// @param eap for forced 'ff' and 'fenc' or NULL
-/// @param flags extra flags for readfile()
+/// @param flags_arg extra flags for readfile()
///
/// @return FAIL for failure, OK otherwise.
-int open_buffer(int read_stdin, exarg_T *eap, int flags)
+int open_buffer(int read_stdin, exarg_T *eap, int flags_arg)
{
+ int flags = flags_arg;
int retval = OK;
bufref_T old_curbuf;
long old_tw = curbuf->b_p_tw;
@@ -222,6 +225,13 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags)
// mark cursor position as being invalid
curwin->w_valid = 0;
+ // A buffer without an actual file should not use the buffer name to read a
+ // file.
+ if (bt_nofileread(curbuf)) {
+ flags |= READ_NOFILE;
+ }
+
+ // Read the file if there is one.
if (curbuf->b_ffname != NULL) {
#ifdef UNIX
int save_bin = curbuf->b_p_bin;
@@ -793,8 +803,8 @@ static void free_buffer(buf_T *buf)
if (autocmd_busy) {
// Do not free the buffer structure while autocommands are executing,
// it's still needed. Free it when autocmd_busy is reset.
- memset(&buf->b_namedm[0], 0, sizeof(buf->b_namedm));
- memset(&buf->b_changelist[0], 0, sizeof(buf->b_changelist));
+ CLEAR_FIELD(buf->b_namedm);
+ CLEAR_FIELD(buf->b_changelist);
buf->b_next = au_pending_free_buf;
au_pending_free_buf = buf;
} else {
@@ -802,6 +812,18 @@ static void free_buffer(buf_T *buf)
}
}
+/// Free the b_wininfo list for buffer "buf".
+static void clear_wininfo(buf_T *buf)
+{
+ wininfo_T *wip;
+
+ while (buf->b_wininfo != NULL) {
+ wip = buf->b_wininfo;
+ buf->b_wininfo = wip->wi_next;
+ free_wininfo(wip, buf);
+ }
+}
+
/// Free stuff in the buffer for ":bdel" and when wiping out the buffer.
///
/// @param buf Buffer pointer
@@ -836,18 +858,6 @@ static void free_buffer_stuff(buf_T *buf, int free_flags)
buf_updates_unload(buf, false);
}
-/// Free the b_wininfo list for buffer "buf".
-static void clear_wininfo(buf_T *buf)
-{
- wininfo_T *wip;
-
- while (buf->b_wininfo != NULL) {
- wip = buf->b_wininfo;
- buf->b_wininfo = wip->wi_next;
- free_wininfo(wip, buf);
- }
-}
-
/// Go to another buffer. Handles the result of the ATTENTION dialog.
void goto_buffer(exarg_T *eap, int start, int dir, int count)
{
@@ -1532,6 +1542,15 @@ void set_curbuf(buf_T *buf, int action)
/// be pointing to freed memory.
void enter_buffer(buf_T *buf)
{
+ // when closing the current buffer stop Visual mode
+ if (VIsual_active
+#if defined(EXITFREE)
+ && !entered_free_all_mem
+#endif
+ ) {
+ end_visual_mode();
+ }
+
// Get the buffer in the current window.
curwin->w_buffer = buf;
curbuf = buf;
@@ -1612,7 +1631,7 @@ void enter_buffer(buf_T *buf)
}
curbuf->b_last_used = time(NULL);
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/// Change to the directory of the current buffer.
@@ -2595,7 +2614,7 @@ void get_winopts(buf_T *buf)
}
if (curwin->w_float_config.style == kWinStyleMinimal) {
- didset_window_options(curwin);
+ didset_window_options(curwin, false);
win_set_minimal_style(curwin);
}
@@ -2603,7 +2622,7 @@ void get_winopts(buf_T *buf)
if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;
}
- didset_window_options(curwin);
+ didset_window_options(curwin, false);
}
/// Find the mark for the buffer 'buf' for the current window.
@@ -3155,14 +3174,14 @@ void maketitle(void)
use_sandbox = was_set_insecurely(curwin, "titlestring", 0);
build_stl_str_hl(curwin, buf, sizeof(buf),
- (char *)p_titlestring, use_sandbox,
+ p_titlestring, use_sandbox,
0, maxlen, NULL, NULL);
title_str = buf;
if (called_emsg > called_emsg_before) {
set_string_option_direct("titlestring", -1, "", OPT_FREE, SID_ERROR);
}
} else {
- title_str = (char *)p_titlestring;
+ title_str = p_titlestring;
}
} else {
// Format: "fname + (path) (1 of 2) - VIM".
@@ -3269,13 +3288,13 @@ void maketitle(void)
use_sandbox = was_set_insecurely(curwin, "iconstring", 0);
build_stl_str_hl(curwin, icon_str, sizeof(buf),
- (char *)p_iconstring, use_sandbox,
+ p_iconstring, use_sandbox,
0, 0, NULL, NULL);
if (called_emsg > called_emsg_before) {
set_string_option_direct("iconstring", -1, "", OPT_FREE, SID_ERROR);
}
} else {
- icon_str = (char *)p_iconstring;
+ icon_str = p_iconstring;
}
} else {
char *buf_p;
@@ -3346,1192 +3365,6 @@ void free_titles(void)
#endif
-/// Enumeration specifying the valid numeric bases that can
-/// be used when printing numbers in the status line.
-typedef enum {
- kNumBaseDecimal = 10,
- kNumBaseHexadecimal = 16,
-} NumberBase;
-
-/// Build a string from the status line items in "fmt".
-/// Return length of string in screen cells.
-///
-/// Normally works for window "wp", except when working for 'tabline' then it
-/// is "curwin".
-///
-/// Items are drawn interspersed with the text that surrounds it
-/// Specials: %-<wid>(xxx%) => group, %= => separation marker, %< => truncation
-/// Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
-///
-/// If maxwidth is not zero, the string will be filled at any middle marker
-/// or truncated if too long, fillchar is used for all whitespace.
-///
-/// @param wp The window to build a statusline for
-/// @param out The output buffer to write the statusline to
-/// Note: This should not be NameBuff
-/// @param outlen The length of the output buffer
-/// @param fmt The statusline format string
-/// @param use_sandbox Use a sandboxed environment when evaluating fmt
-/// @param fillchar Character to use when filling empty space in the statusline
-/// @param maxwidth The maximum width to make the statusline
-/// @param hltab HL attributes (can be NULL)
-/// @param tabtab Tab clicks definition (can be NULL).
-///
-/// @return The final width of the statusline
-int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_sandbox, int fillchar,
- int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
-{
- static size_t stl_items_len = 20; // Initial value, grows as needed.
- static stl_item_t *stl_items = NULL;
- static int *stl_groupitems = NULL;
- static stl_hlrec_t *stl_hltab = NULL;
- static StlClickRecord *stl_tabtab = NULL;
- static int *stl_separator_locations = NULL;
-
-#define TMPLEN 70
- char buf_tmp[TMPLEN];
- char win_tmp[TMPLEN];
- char *usefmt = fmt;
- const int save_must_redraw = must_redraw;
- const int save_redr_type = curwin->w_redr_type;
-
- if (stl_items == NULL) {
- stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
- stl_groupitems = xmalloc(sizeof(int) * stl_items_len);
-
- // Allocate one more, because the last element is used to indicate the
- // end of the list.
- stl_hltab = xmalloc(sizeof(stl_hlrec_t) * (stl_items_len + 1));
- stl_tabtab = xmalloc(sizeof(StlClickRecord) * (stl_items_len + 1));
-
- stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
- }
-
- // When the format starts with "%!" then evaluate it as an expression and
- // use the result as the actual format string.
- if (fmt[0] == '%' && fmt[1] == '!') {
- typval_T tv = {
- .v_type = VAR_NUMBER,
- .vval.v_number = wp->handle,
- };
- set_var(S_LEN("g:statusline_winid"), &tv, false);
-
- usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
- if (usefmt == NULL) {
- usefmt = fmt;
- }
-
- do_unlet(S_LEN("g:statusline_winid"), true);
- }
-
- if (fillchar == 0) {
- fillchar = ' ';
- }
-
- // The cursor in windows other than the current one isn't always
- // up-to-date, esp. because of autocommands and timers.
- linenr_T lnum = wp->w_cursor.lnum;
- if (lnum > wp->w_buffer->b_ml.ml_line_count) {
- lnum = wp->w_buffer->b_ml.ml_line_count;
- wp->w_cursor.lnum = lnum;
- }
-
- // Get line & check if empty (cursorpos will show "0-1").
- const char *line_ptr = (char *)ml_get_buf(wp->w_buffer, lnum, false);
- bool empty_line = (*line_ptr == NUL);
-
- // Get the byte value now, in case we need it below. This is more
- // efficient than making a copy of the line.
- int byteval;
- const size_t len = STRLEN(line_ptr);
- if (wp->w_cursor.col > (colnr_T)len) {
- // Line may have changed since checking the cursor column, or the lnum
- // was adjusted above.
- wp->w_cursor.col = (colnr_T)len;
- wp->w_cursor.coladd = 0;
- byteval = 0;
- } else {
- byteval = utf_ptr2char(line_ptr + wp->w_cursor.col);
- }
-
- int groupdepth = 0;
- int evaldepth = 0;
-
- int curitem = 0;
- bool prevchar_isflag = true;
- bool prevchar_isitem = false;
-
- // out_p is the current position in the output buffer
- char *out_p = out;
-
- // out_end_p is the last valid character in the output buffer
- // Note: The null termination character must occur here or earlier,
- // so any user-visible characters must occur before here.
- char *out_end_p = (out + outlen) - 1;
-
- // Proceed character by character through the statusline format string
- // fmt_p is the current position in the input buffer
- for (char *fmt_p = usefmt; *fmt_p != NUL;) {
- if (curitem == (int)stl_items_len) {
- size_t new_len = stl_items_len * 3 / 2;
-
- stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len);
- stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len);
- stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1));
- stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1));
- stl_separator_locations =
- xrealloc(stl_separator_locations, sizeof(int) * new_len);
-
- stl_items_len = new_len;
- }
-
- if (*fmt_p != '%') {
- prevchar_isflag = prevchar_isitem = false;
- }
-
- // Copy the formatting verbatim until we reach the end of the string
- // or find a formatting item (denoted by `%`)
- // or run out of room in our output buffer.
- while (*fmt_p != NUL && *fmt_p != '%' && out_p < out_end_p) {
- *out_p++ = *fmt_p++;
- }
-
- // If we have processed the entire format string or run out of
- // room in our output buffer, exit the loop.
- if (*fmt_p == NUL || out_p >= out_end_p) {
- break;
- }
-
- // The rest of this loop will handle a single `%` item.
- // Note: We increment here to skip over the `%` character we are currently
- // on so we can process the item's contents.
- fmt_p++;
-
- // Ignore `%` at the end of the format string
- if (*fmt_p == NUL) {
- break;
- }
-
- // Two `%` in a row is the escape sequence to print a
- // single `%` in the output buffer.
- if (*fmt_p == '%') {
- *out_p++ = *fmt_p++;
- prevchar_isflag = prevchar_isitem = false;
- continue;
- }
-
- // STL_SEPARATE: Separation place between left and right aligned items.
- if (*fmt_p == STL_SEPARATE) {
- fmt_p++;
- // Ignored when we are inside of a grouping
- if (groupdepth > 0) {
- continue;
- }
- stl_items[curitem].type = Separate;
- stl_items[curitem++].start = out_p;
- continue;
- }
-
- // STL_TRUNCMARK: Where to begin truncating if the statusline is too long.
- if (*fmt_p == STL_TRUNCMARK) {
- fmt_p++;
- stl_items[curitem].type = Trunc;
- stl_items[curitem++].start = out_p;
- continue;
- }
-
- // The end of a grouping
- if (*fmt_p == ')') {
- fmt_p++;
- // Ignore if we are not actually inside a group currently
- if (groupdepth < 1) {
- continue;
- }
- groupdepth--;
-
- // Determine how long the group is.
- // Note: We set the current output position to null
- // so `vim_strsize` will work.
- char *t = stl_items[stl_groupitems[groupdepth]].start;
- *out_p = NUL;
- long group_len = vim_strsize(t);
-
- // If the group contained internal items
- // and the group did not have a minimum width,
- // and if there were no normal items in the group,
- // move the output pointer back to where the group started.
- // Note: This erases any non-item characters that were in the group.
- // Otherwise there would be no reason to do this step.
- if (curitem > stl_groupitems[groupdepth] + 1
- && stl_items[stl_groupitems[groupdepth]].minwid == 0) {
- // remove group if all items are empty and highlight group
- // doesn't change
- int group_start_userhl = 0;
- int group_end_userhl = 0;
- int n;
- for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) {
- if (stl_items[n].type == Highlight) {
- group_start_userhl = group_end_userhl = stl_items[n].minwid;
- break;
- }
- }
- for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- if (stl_items[n].type == Normal) {
- break;
- }
- if (stl_items[n].type == Highlight) {
- group_end_userhl = stl_items[n].minwid;
- }
- }
- if (n == curitem && group_start_userhl == group_end_userhl) {
- // empty group
- out_p = t;
- group_len = 0;
- for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- // do not use the highlighting from the removed group
- if (stl_items[n].type == Highlight) {
- stl_items[n].type = Empty;
- }
- // adjust the start position of TabPage to the next
- // item position
- if (stl_items[n].type == TabPage) {
- stl_items[n].start = out_p;
- }
- }
- }
- }
-
- // If the group is longer than it is allowed to be
- // truncate by removing bytes from the start of the group text.
- if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) {
- // { Determine the number of bytes to remove
-
- // Find the first character that should be included.
- long n = 0;
- while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) {
- group_len -= ptr2cells(t + n);
- n += utfc_ptr2len(t + n);
- }
- // }
-
- // Prepend the `<` to indicate that the output was truncated.
- *t = '<';
-
- // { Move the truncated output
- memmove(t + 1, t + n, (size_t)(out_p - (t + n)));
- out_p = out_p - n + 1;
- // Fill up space left over by half a double-wide char.
- while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- // }
-
- // correct the start of the items for the truncation
- for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) {
- // Shift everything back by the number of removed bytes
- // Minus one for the leading '<' added above.
- stl_items[idx].start -= n - 1;
-
- // If the item was partially or completely truncated, set its
- // start to the start of the group
- if (stl_items[idx].start < t) {
- stl_items[idx].start = t;
- }
- }
- // If the group is shorter than the minimum width, add padding characters.
- } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
- long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
- // If the group is left-aligned, add characters to the right.
- if (min_group_width < 0) {
- min_group_width = 0 - min_group_width;
- while (group_len++ < min_group_width && out_p < out_end_p) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- // If the group is right-aligned, shift everything to the right and
- // prepend with filler characters.
- } else {
- // { Move the group to the right
- group_len = (min_group_width - group_len) * utf_char2len(fillchar);
- memmove(t + group_len, t, (size_t)(out_p - t));
- if (out_p + group_len >= (out_end_p + 1)) {
- group_len = (long)(out_end_p - out_p);
- }
- out_p += group_len;
- // }
-
- // Adjust item start positions
- for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- stl_items[n].start += group_len;
- }
-
- // Prepend the fill characters
- for (; group_len > 0; group_len--) {
- MB_CHAR2BYTES(fillchar, t);
- }
- }
- }
- continue;
- }
- int minwid = 0;
- int maxwid = 9999;
- bool left_align = false;
-
- // Denotes that numbers should be left-padded with zeros
- bool zeropad = (*fmt_p == '0');
- if (zeropad) {
- fmt_p++;
- }
-
- // Denotes that the item should be left-aligned.
- // This is tracked by using a negative length.
- if (*fmt_p == '-') {
- fmt_p++;
- left_align = true;
- }
-
- // The first digit group is the item's min width
- if (ascii_isdigit(*fmt_p)) {
- minwid = getdigits_int(&fmt_p, false, 0);
- }
-
- // User highlight groups override the min width field
- // to denote the styling to use.
- if (*fmt_p == STL_USER_HL) {
- stl_items[curitem].type = Highlight;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid > 9 ? 1 : minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // TABPAGE pairs are used to denote a region that when clicked will
- // either switch to or close a tab.
- //
- // Ex: tabline=%0Ttab\ zero%X
- // This tabline has a TABPAGENR item with minwid `0`,
- // which is then closed with a TABCLOSENR item.
- // Clicking on this region with mouse enabled will switch to tab 0.
- // Setting the minwid to a different value will switch
- // to that tab, if it exists
- //
- // Ex: tabline=%1Xtab\ one%X
- // This tabline has a TABCLOSENR item with minwid `1`,
- // which is then closed with a TABCLOSENR item.
- // Clicking on this region with mouse enabled will close tab 0.
- // This is determined by the following formula:
- // tab to close = (1 - minwid)
- // This is because for TABPAGENR we use `minwid` = `tab number`.
- // For TABCLOSENR we store the tab number as a negative value.
- // Because 0 is a valid TABPAGENR value, we have to
- // start our numbering at `-1`.
- // So, `-1` corresponds to us wanting to close tab `0`
- //
- // Note: These options are only valid when creating a tabline.
- if (*fmt_p == STL_TABPAGENR || *fmt_p == STL_TABCLOSENR) {
- if (*fmt_p == STL_TABCLOSENR) {
- if (minwid == 0) {
- // %X ends the close label, go back to the previous tab label nr.
- for (long n = curitem - 1; n >= 0; n--) {
- if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) {
- minwid = stl_items[n].minwid;
- break;
- }
- }
- } else {
- // close nrs are stored as negative values
- minwid = -minwid;
- }
- }
- stl_items[curitem].type = TabPage;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- if (*fmt_p == STL_CLICK_FUNC) {
- fmt_p++;
- char *t = fmt_p;
- while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
- fmt_p++;
- }
- if (*fmt_p != STL_CLICK_FUNC) {
- break;
- }
- stl_items[curitem].type = ClickFunc;
- stl_items[curitem].start = out_p;
- stl_items[curitem].cmd = xmemdupz(t, (size_t)(fmt_p - t));
- stl_items[curitem].minwid = minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // Denotes the end of the minwid
- // the maxwid may follow immediately after
- if (*fmt_p == '.') {
- fmt_p++;
- if (ascii_isdigit(*fmt_p)) {
- maxwid = getdigits_int(&fmt_p, false, 50);
- }
- }
-
- // Bound the minimum width at 50.
- // Make the number negative to denote left alignment of the item
- minwid = (minwid > 50 ? 50 : minwid) * (left_align ? -1 : 1);
-
- // Denotes the start of a new group
- if (*fmt_p == '(') {
- stl_groupitems[groupdepth++] = curitem;
- stl_items[curitem].type = Group;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid;
- stl_items[curitem].maxwid = maxwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // Denotes end of expanded %{} block
- if (*fmt_p == '}' && evaldepth > 0) {
- fmt_p++;
- evaldepth--;
- continue;
- }
-
- // An invalid item was specified.
- // Continue processing on the next character of the format string.
- if (vim_strchr(STL_ALL, *fmt_p) == NULL) {
- fmt_p++;
- continue;
- }
-
- // The status line item type
- char opt = *fmt_p++;
-
- // OK - now for the real work
- NumberBase base = kNumBaseDecimal;
- bool itemisflag = false;
- bool fillable = true;
- long num = -1;
- char *str = NULL;
- switch (opt) {
- case STL_FILEPATH:
- case STL_FULLPATH:
- case STL_FILENAME:
- // Set fillable to false so that ' ' in the filename will not
- // get replaced with the fillchar
- fillable = false;
- if (buf_spname(wp->w_buffer) != NULL) {
- STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
- } else {
- char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
- : wp->w_buffer->b_fname;
- home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
- }
- trans_characters((char *)NameBuff, MAXPATHL);
- if (opt != STL_FILENAME) {
- str = (char *)NameBuff;
- } else {
- str = path_tail((char *)NameBuff);
- }
- break;
- case STL_VIM_EXPR: // '{'
- {
- char *block_start = fmt_p - 1;
- int reevaluate = (*fmt_p == '%');
- itemisflag = true;
-
- if (reevaluate) {
- fmt_p++;
- }
-
- // Attempt to copy the expression to evaluate into
- // the output buffer as a null-terminated string.
- char *t = out_p;
- while ((*fmt_p != '}' || (reevaluate && fmt_p[-1] != '%'))
- && *fmt_p != NUL && out_p < out_end_p) {
- *out_p++ = *fmt_p++;
- }
- if (*fmt_p != '}') { // missing '}' or out of space
- break;
- }
- fmt_p++;
- if (reevaluate) {
- out_p[-1] = 0; // remove the % at the end of %{% expr %}
- } else {
- *out_p = 0;
- }
-
- // Move our position in the output buffer
- // to the beginning of the expression
- out_p = t;
-
- // { Evaluate the expression
-
- // Store the current buffer number as a string variable
- vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
- set_internal_string_var("g:actual_curbuf", buf_tmp);
- vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
- set_internal_string_var("g:actual_curwin", (char *)win_tmp);
-
- buf_T *const save_curbuf = curbuf;
- win_T *const save_curwin = curwin;
- const int save_VIsual_active = VIsual_active;
- curwin = wp;
- curbuf = wp->w_buffer;
- // Visual mode is only valid in the current window.
- if (curwin != save_curwin) {
- VIsual_active = false;
- }
-
- // Note: The result stored in `t` is unused.
- str = eval_to_string_safe(out_p, &t, use_sandbox);
-
- curwin = save_curwin;
- curbuf = save_curbuf;
- VIsual_active = save_VIsual_active;
-
- // Remove the variable we just stored
- do_unlet(S_LEN("g:actual_curbuf"), true);
- do_unlet(S_LEN("g:actual_curwin"), true);
-
- // }
-
- // Check if the evaluated result is a number.
- // If so, convert the number to an int and free the string.
- if (str != NULL && *str != 0) {
- if (*skipdigits(str) == NUL) {
- num = atoi(str);
- XFREE_CLEAR(str);
- itemisflag = false;
- }
- }
-
- // If the output of the expression needs to be evaluated
- // replace the %{} block with the result of evaluation
- if (reevaluate && str != NULL && *str != 0
- && strchr((const char *)str, '%') != NULL
- && evaldepth < MAX_STL_EVAL_DEPTH) {
- size_t parsed_usefmt = (size_t)(block_start - usefmt);
- size_t str_length = STRLEN(str);
- size_t fmt_length = STRLEN(fmt_p);
- size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3;
- char *new_fmt = xmalloc(new_fmt_len * sizeof(char));
- char *new_fmt_p = new_fmt;
-
- new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt;
- new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length;
- new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2;
- new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length;
- *new_fmt_p = 0;
- new_fmt_p = NULL;
-
- if (usefmt != fmt) {
- xfree(usefmt);
- }
- XFREE_CLEAR(str);
- usefmt = new_fmt;
- fmt_p = usefmt + parsed_usefmt;
- evaldepth++;
- continue;
- }
- break;
- }
-
- case STL_LINE:
- num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
- ? 0L : (long)(wp->w_cursor.lnum);
- break;
-
- case STL_NUMLINES:
- num = wp->w_buffer->b_ml.ml_line_count;
- break;
-
- case STL_COLUMN:
- num = (State & MODE_INSERT) == 0 && empty_line ? 0 : (int)wp->w_cursor.col + 1;
- break;
-
- case STL_VIRTCOL:
- case STL_VIRTCOL_ALT: {
- colnr_T virtcol = wp->w_virtcol + 1;
- // Don't display %V if it's the same as %c.
- if (opt == STL_VIRTCOL_ALT
- && (virtcol == (colnr_T)((State & MODE_INSERT) == 0 && empty_line
- ? 0 : (int)wp->w_cursor.col + 1))) {
- break;
- }
- num = (long)virtcol;
- break;
- }
-
- case STL_PERCENTAGE:
- num = (int)(((long)wp->w_cursor.lnum * 100L) /
- (long)wp->w_buffer->b_ml.ml_line_count);
- break;
-
- case STL_ALTPERCENT:
- // Store the position percentage in our temporary buffer.
- // Note: We cannot store the value in `num` because
- // `get_rel_pos` can return a named position. Ex: "Top"
- get_rel_pos(wp, buf_tmp, TMPLEN);
- str = buf_tmp;
- break;
-
- case STL_ARGLISTSTAT:
- fillable = false;
-
- // Note: This is important because `append_arg_number` starts appending
- // at the end of the null-terminated string.
- // Setting the first byte to null means it will place the argument
- // number string at the beginning of the buffer.
- buf_tmp[0] = 0;
-
- // Note: The call will only return true if it actually
- // appended data to the `buf_tmp` buffer.
- if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
- str = buf_tmp;
- }
- break;
-
- case STL_KEYMAP:
- fillable = false;
- if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
- str = buf_tmp;
- }
- break;
- case STL_PAGENUM:
- num = printer_page_num;
- break;
-
- case STL_BUFNO:
- num = wp->w_buffer->b_fnum;
- break;
-
- case STL_OFFSET_X:
- base = kNumBaseHexadecimal;
- FALLTHROUGH;
- case STL_OFFSET: {
- long l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL,
- false);
- num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
- 0L : l + 1 + ((State & MODE_INSERT) == 0 && empty_line ?
- 0 : (int)wp->w_cursor.col);
- break;
- }
- case STL_BYTEVAL_X:
- base = kNumBaseHexadecimal;
- FALLTHROUGH;
- case STL_BYTEVAL:
- num = byteval;
- if (num == NL) {
- num = 0;
- } else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) {
- num = NL;
- }
- break;
-
- case STL_ROFLAG:
- case STL_ROFLAG_ALT:
- itemisflag = true;
- if (wp->w_buffer->b_p_ro) {
- str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
- }
- break;
-
- case STL_HELPFLAG:
- case STL_HELPFLAG_ALT:
- itemisflag = true;
- if (wp->w_buffer->b_help) {
- str = (opt == STL_HELPFLAG_ALT) ? ",HLP" : _("[Help]");
- }
- break;
-
- case STL_FILETYPE:
- // Copy the filetype if it is not null and the formatted string will fit
- // in the temporary buffer
- // (including the brackets and null terminating character)
- if (*wp->w_buffer->b_p_ft != NUL
- && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
- vim_snprintf(buf_tmp, sizeof(buf_tmp), "[%s]",
- wp->w_buffer->b_p_ft);
- str = buf_tmp;
- }
- break;
-
- case STL_FILETYPE_ALT:
- itemisflag = true;
- // Copy the filetype if it is not null and the formatted string will fit
- // in the temporary buffer
- // (including the comma and null terminating character)
- if (*wp->w_buffer->b_p_ft != NUL
- && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
- vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft);
- // Uppercase the file extension
- for (char *t = buf_tmp; *t != 0; t++) {
- *t = (char)TOUPPER_LOC(*t);
- }
- str = buf_tmp;
- }
- break;
- case STL_PREVIEWFLAG:
- case STL_PREVIEWFLAG_ALT:
- itemisflag = true;
- if (wp->w_p_pvw) {
- str = (opt == STL_PREVIEWFLAG_ALT) ? ",PRV" : _("[Preview]");
- }
- break;
-
- case STL_QUICKFIX:
- if (bt_quickfix(wp->w_buffer)) {
- str = wp->w_llist_ref ? _(msg_loclist) : _(msg_qflist);
- }
- break;
-
- case STL_MODIFIED:
- case STL_MODIFIED_ALT:
- itemisflag = true;
- switch ((opt == STL_MODIFIED_ALT)
- + bufIsChanged(wp->w_buffer) * 2
- + (!MODIFIABLE(wp->w_buffer)) * 4) {
- case 2:
- str = "[+]"; break;
- case 3:
- str = ",+"; break;
- case 4:
- str = "[-]"; break;
- case 5:
- str = ",-"; break;
- case 6:
- str = "[+-]"; break;
- case 7:
- str = ",+-"; break;
- }
- break;
-
- case STL_HIGHLIGHT: {
- // { The name of the highlight is surrounded by `#`
- char *t = fmt_p;
- while (*fmt_p != '#' && *fmt_p != NUL) {
- fmt_p++;
- }
- // }
-
- // Create a highlight item based on the name
- if (*fmt_p == '#') {
- stl_items[curitem].type = Highlight;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = -syn_name2id_len(t, (size_t)(fmt_p - t));
- curitem++;
- fmt_p++;
- }
- continue;
- }
- }
-
- // If we made it this far, the item is normal and starts at
- // our current position in the output buffer.
- // Non-normal items would have `continued`.
- stl_items[curitem].start = out_p;
- stl_items[curitem].type = Normal;
-
- // Copy the item string into the output buffer
- if (str != NULL && *str) {
- // { Skip the leading `,` or ` ` if the item is a flag
- // and the proper conditions are met
- char *t = str;
- if (itemisflag) {
- if ((t[0] && t[1])
- && ((!prevchar_isitem && *t == ',')
- || (prevchar_isflag && *t == ' '))) {
- t++;
- }
- prevchar_isflag = true;
- }
- // }
-
- long l = vim_strsize(t);
-
- // If this item is non-empty, record that the last thing
- // we put in the output buffer was an item
- if (l > 0) {
- prevchar_isitem = true;
- }
-
- // If the item is too wide, truncate it from the beginning
- if (l > maxwid) {
- while (l >= maxwid) {
- l -= ptr2cells(t);
- t += utfc_ptr2len(t);
- }
-
- // Early out if there isn't enough room for the truncation marker
- if (out_p >= out_end_p) {
- break;
- }
-
- // Add the truncation marker
- *out_p++ = '<';
- }
-
- // If the item is right aligned and not wide enough,
- // pad with fill characters.
- if (minwid > 0) {
- for (; l < minwid && out_p < out_end_p; l++) {
- // Don't put a "-" in front of a digit.
- if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
- *out_p++ = ' ';
- } else {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- }
- minwid = 0;
- } else {
- // Note: The negative value denotes a left aligned item.
- // Here we switch the minimum width back to a positive value.
- minwid *= -1;
- }
-
- // { Copy the string text into the output buffer
- for (; *t && out_p < out_end_p; t++) {
- // Change a space by fillchar, unless fillchar is '-' and a
- // digit follows.
- if (fillable && *t == ' '
- && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
- MB_CHAR2BYTES(fillchar, out_p);
- } else {
- *out_p++ = *t;
- }
- }
- // }
-
- // For left-aligned items, fill any remaining space with the fillchar
- for (; l < minwid && out_p < out_end_p; l++) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
-
- // Otherwise if the item is a number, copy that to the output buffer.
- } else if (num >= 0) {
- if (out_p + 20 > out_end_p) {
- break; // not sufficient space
- }
- prevchar_isitem = true;
-
- // { Build the formatting string
- char nstr[20];
- char *t = nstr;
- if (opt == STL_VIRTCOL_ALT) {
- *t++ = '-';
- minwid--;
- }
- *t++ = '%';
- if (zeropad) {
- *t++ = '0';
- }
-
- // Note: The `*` means we take the width as one of the arguments
- *t++ = '*';
- *t++ = base == kNumBaseHexadecimal ? 'X' : 'd';
- *t = 0;
- // }
-
- // { Determine how many characters the number will take up when printed
- // Note: We have to cast the base because the compiler uses
- // unsigned ints for the enum values.
- long num_chars = 1;
- for (long n = num; n >= (int)base; n /= (int)base) {
- num_chars++;
- }
-
- // VIRTCOL_ALT takes up an extra character because
- // of the `-` we added above.
- if (opt == STL_VIRTCOL_ALT) {
- num_chars++;
- }
- // }
-
- assert(out_end_p >= out_p);
- size_t remaining_buf_len = (size_t)(out_end_p - out_p) + 1;
-
- // If the number is going to take up too much room
- // Figure out the approximate number in "scientific" type notation.
- // Ex: 14532 with maxwid of 4 -> '14>3'
- if (num_chars > maxwid) {
- // Add two to the width because the power piece will take
- // two extra characters
- num_chars += 2;
-
- // How many extra characters there are
- long n = num_chars - maxwid;
-
- // { Reduce the number by base^n
- while (num_chars-- > maxwid) {
- num /= (long)base;
- }
- // }
-
- // { Add the format string for the exponent bit
- *t++ = '>';
- *t++ = '%';
- // Use the same base as the first number
- *t = t[-3];
- *++t = 0;
- // }
-
- vim_snprintf(out_p, remaining_buf_len, nstr, 0, num, n);
- } else {
- vim_snprintf(out_p, remaining_buf_len, nstr, minwid, num);
- }
-
- // Advance the output buffer position to the end of the
- // number we just printed
- out_p += STRLEN(out_p);
-
- // Otherwise, there was nothing to print so mark the item as empty
- } else {
- stl_items[curitem].type = Empty;
- }
-
- // Only free the string buffer if we allocated it.
- // Note: This is not needed if `str` is pointing at `tmp`
- if (opt == STL_VIM_EXPR) {
- XFREE_CLEAR(str);
- }
-
- if (num >= 0 || (!itemisflag && str && *str)) {
- prevchar_isflag = false; // Item not NULL, but not a flag
- }
-
- // Item processed, move to the next
- curitem++;
- }
-
- *out_p = NUL;
- int itemcnt = curitem;
-
- // Free the format buffer if we allocated it internally
- if (usefmt != fmt) {
- xfree(usefmt);
- }
-
- // We have now processed the entire statusline format string.
- // What follows is post-processing to handle alignment and highlighting.
-
- int width = vim_strsize(out);
- if (maxwidth > 0 && width > maxwidth) {
- // Result is too long, must truncate somewhere.
- int item_idx = 0;
- char *trunc_p;
-
- // If there are no items, truncate from beginning
- if (itemcnt == 0) {
- trunc_p = out;
-
- // Otherwise, look for the truncation item
- } else {
- // Default to truncating at the first item
- trunc_p = stl_items[0].start;
- item_idx = 0;
-
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].type == Trunc) {
- // Truncate at %< stl_items.
- trunc_p = stl_items[i].start;
- item_idx = i;
- break;
- }
- }
- }
-
- // If the truncation point we found is beyond the maximum
- // length of the string, truncate the end of the string.
- if (width - vim_strsize(trunc_p) >= maxwidth) {
- // Walk from the beginning of the
- // string to find the last character that will fit.
- trunc_p = out;
- width = 0;
- for (;;) {
- width += ptr2cells(trunc_p);
- if (width >= maxwidth) {
- break;
- }
-
- // Note: Only advance the pointer if the next
- // character will fit in the available output space
- trunc_p += utfc_ptr2len(trunc_p);
- }
-
- // Ignore any items in the statusline that occur after
- // the truncation point
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].start > trunc_p) {
- itemcnt = i;
- break;
- }
- }
-
- // Truncate the output
- *trunc_p++ = '>';
- *trunc_p = 0;
-
- // Truncate at the truncation point we found
- } else {
- // { Determine how many bytes to remove
- long trunc_len = 0;
- while (width >= maxwidth) {
- width -= ptr2cells(trunc_p + trunc_len);
- trunc_len += utfc_ptr2len(trunc_p + trunc_len);
- }
- // }
-
- // { Truncate the string
- char *trunc_end_p = trunc_p + trunc_len;
- STRMOVE(trunc_p + 1, trunc_end_p);
-
- // Put a `<` to mark where we truncated at
- *trunc_p = '<';
-
- if (width + 1 < maxwidth) {
- // Advance the pointer to the end of the string
- trunc_p = trunc_p + STRLEN(trunc_p);
- }
-
- // Fill up for half a double-wide character.
- while (++width < maxwidth) {
- MB_CHAR2BYTES(fillchar, trunc_p);
- *trunc_p = NUL;
- }
- // }
-
- // { Change the start point for items based on
- // their position relative to our truncation point
-
- // Note: The offset is one less than the truncation length because
- // the truncation marker `<` is not counted.
- long item_offset = trunc_len - 1;
-
- for (int i = item_idx; i < itemcnt; i++) {
- // Items starting at or after the end of the truncated section need
- // to be moved backwards.
- if (stl_items[i].start >= trunc_end_p) {
- stl_items[i].start -= item_offset;
- // Anything inside the truncated area is set to start
- // at the `<` truncation character.
- } else {
- stl_items[i].start = trunc_p;
- }
- }
- // }
- }
- width = maxwidth;
-
- // If there is room left in our statusline, and room left in our buffer,
- // add characters at the separate marker (if there is one) to
- // fill up the available space.
- } else if (width < maxwidth
- && STRLEN(out) + (size_t)(maxwidth - width) + 1 < outlen) {
- // Find how many separators there are, which we will use when
- // figuring out how many groups there are.
- int num_separators = 0;
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].type == Separate) {
- // Create an array of the start location for each
- // separator mark.
- stl_separator_locations[num_separators] = i;
- num_separators++;
- }
- }
-
- // If we have separated groups, then we deal with it now
- if (num_separators) {
- int standard_spaces = (maxwidth - width) / num_separators;
- int final_spaces = (maxwidth - width) -
- standard_spaces * (num_separators - 1);
-
- for (int i = 0; i < num_separators; i++) {
- int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces;
- dislocation *= utf_char2len(fillchar);
- char *start = stl_items[stl_separator_locations[i]].start;
- char *seploc = start + dislocation;
- STRMOVE(seploc, start);
- for (char *s = start; s < seploc;) {
- MB_CHAR2BYTES(fillchar, s);
- }
-
- for (int item_idx = stl_separator_locations[i] + 1;
- item_idx < itemcnt;
- item_idx++) {
- stl_items[item_idx].start += dislocation;
- }
- }
-
- width = maxwidth;
- }
- }
-
- // Store the info about highlighting.
- if (hltab != NULL) {
- *hltab = stl_hltab;
- stl_hlrec_t *sp = stl_hltab;
- for (long l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == Highlight) {
- sp->start = stl_items[l].start;
- sp->userhl = stl_items[l].minwid;
- sp++;
- }
- }
- sp->start = NULL;
- sp->userhl = 0;
- }
-
- // Store the info about tab pages labels.
- if (tabtab != NULL) {
- *tabtab = stl_tabtab;
- StlClickRecord *cur_tab_rec = stl_tabtab;
- for (long l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == TabPage) {
- cur_tab_rec->start = stl_items[l].start;
- if (stl_items[l].minwid == 0) {
- cur_tab_rec->def.type = kStlClickDisabled;
- cur_tab_rec->def.tabnr = 0;
- } else {
- int tabnr = stl_items[l].minwid;
- if (stl_items[l].minwid > 0) {
- cur_tab_rec->def.type = kStlClickTabSwitch;
- } else {
- cur_tab_rec->def.type = kStlClickTabClose;
- tabnr = -tabnr;
- }
- cur_tab_rec->def.tabnr = tabnr;
- }
- cur_tab_rec->def.func = NULL;
- cur_tab_rec++;
- } else if (stl_items[l].type == ClickFunc) {
- cur_tab_rec->start = stl_items[l].start;
- cur_tab_rec->def.type = kStlClickFuncRun;
- cur_tab_rec->def.tabnr = stl_items[l].minwid;
- cur_tab_rec->def.func = stl_items[l].cmd;
- cur_tab_rec++;
- }
- }
- cur_tab_rec->start = NULL;
- cur_tab_rec->def.type = kStlClickDisabled;
- cur_tab_rec->def.tabnr = 0;
- cur_tab_rec->def.func = NULL;
- }
-
- // When inside update_screen we do not want redrawing a statusline, ruler,
- // title, etc. to trigger another redraw, it may cause an endless loop.
- if (updating_screen) {
- must_redraw = save_must_redraw;
- curwin->w_redr_type = save_redr_type;
- }
-
- return width;
-}
-
/// Get relative cursor position in window into "buf[buflen]", in the form 99%,
/// using "Top", "Bot" or "All" when appropriate.
void get_rel_pos(win_T *wp, char *buf, int buflen)
@@ -4571,7 +3404,7 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
/// @param add_file if true, add "file" before the arg number
///
/// @return true if it was appended.
-static bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
+bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
FUNC_ATTR_NONNULL_ALL
{
// Nothing to do
@@ -4628,279 +3461,6 @@ void fname_expand(buf_T *buf, char **ffname, char **sfname)
#endif
}
-/// Get the file name for an argument list entry.
-char *alist_name(aentry_T *aep)
-{
- buf_T *bp;
-
- // Use the name from the associated buffer if it exists.
- bp = buflist_findnr(aep->ae_fnum);
- if (bp == NULL || bp->b_fname == NULL) {
- return (char *)aep->ae_fname;
- }
- return bp->b_fname;
-}
-
-/// do_arg_all(): Open up to 'count' windows, one for each argument.
-///
-/// @param forceit hide buffers in current windows
-/// @param keep_tabs keep current tabs, for ":tab drop file"
-void do_arg_all(int count, int forceit, int keep_tabs)
-{
- uint8_t *opened; // Array of weight for which args are open:
- // 0: not opened
- // 1: opened in other tab
- // 2: opened in curtab
- // 3: opened in curtab and curwin
-
- int opened_len; // length of opened[]
- int use_firstwin = false; // use first window for arglist
- bool tab_drop_empty_window = false;
- int split_ret = OK;
- bool p_ea_save;
- alist_T *alist; // argument list to be used
- buf_T *buf;
- tabpage_T *tpnext;
- int had_tab = cmdmod.cmod_tab;
- win_T *old_curwin, *last_curwin;
- tabpage_T *old_curtab, *last_curtab;
- win_T *new_curwin = NULL;
- tabpage_T *new_curtab = NULL;
-
- assert(firstwin != NULL); // satisfy coverity
-
- if (ARGCOUNT <= 0) {
- // Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
- return;
- }
- setpcmark();
-
- opened_len = ARGCOUNT;
- opened = xcalloc((size_t)opened_len, 1);
-
- // Autocommands may do anything to the argument list. Make sure it's not
- // freed while we are working here by "locking" it. We still have to
- // watch out for its size to be changed.
- alist = curwin->w_alist;
- alist->al_refcount++;
-
- old_curwin = curwin;
- old_curtab = curtab;
-
- // Try closing all windows that are not in the argument list.
- // Also close windows that are not full width;
- // When 'hidden' or "forceit" set the buffer becomes hidden.
- // Windows that have a changed buffer and can't be hidden won't be closed.
- // When the ":tab" modifier was used do this for all tab pages.
- if (had_tab > 0) {
- goto_tabpage_tp(first_tabpage, true, true);
- }
- for (;;) {
- win_T *wpnext = NULL;
- tpnext = curtab->tp_next;
- for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
- int i;
- wpnext = wp->w_next;
- buf = wp->w_buffer;
- if (buf->b_ffname == NULL
- || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
- i = opened_len;
- } else {
- // check if the buffer in this window is in the arglist
- for (i = 0; i < opened_len; i++) {
- if (i < alist->al_ga.ga_len
- && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
- || path_full_compare(alist_name(&AARGLIST(alist)[i]),
- buf->b_ffname,
- true, true) & kEqualFiles)) {
- int weight = 1;
-
- if (old_curtab == curtab) {
- weight++;
- if (old_curwin == wp) {
- weight++;
- }
- }
-
- if (weight > (int)opened[i]) {
- opened[i] = (uint8_t)weight;
- if (i == 0) {
- if (new_curwin != NULL) {
- new_curwin->w_arg_idx = opened_len;
- }
- new_curwin = wp;
- new_curtab = curtab;
- }
- } else if (keep_tabs) {
- i = opened_len;
- }
-
- if (wp->w_alist != alist) {
- // Use the current argument list for all windows containing a file from it.
- alist_unlink(wp->w_alist);
- wp->w_alist = alist;
- wp->w_alist->al_refcount++;
- }
- break;
- }
- }
- }
- wp->w_arg_idx = i;
-
- if (i == opened_len && !keep_tabs) { // close this window
- if (buf_hide(buf) || forceit || buf->b_nwindows > 1
- || !bufIsChanged(buf)) {
- // If the buffer was changed, and we would like to hide it, try autowriting.
- if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
- bufref_T bufref;
- set_bufref(&bufref, buf);
- (void)autowrite(buf, false);
- // Check if autocommands removed the window.
- if (!win_valid(wp) || !bufref_valid(&bufref)) {
- wpnext = firstwin; // Start all over...
- continue;
- }
- }
- // don't close last window
- if (ONE_WINDOW
- && (first_tabpage->tp_next == NULL || !had_tab)) {
- use_firstwin = true;
- } else {
- win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
- // check if autocommands removed the next window
- if (!win_valid(wpnext)) {
- // start all over...
- wpnext = firstwin;
- }
- }
- }
- }
- }
-
- // Without the ":tab" modifier only do the current tab page.
- if (had_tab == 0 || tpnext == NULL) {
- break;
- }
-
- // check if autocommands removed the next tab page
- if (!valid_tabpage(tpnext)) {
- tpnext = first_tabpage; // start all over...
- }
- goto_tabpage_tp(tpnext, true, true);
- }
-
- // Open a window for files in the argument list that don't have one.
- // ARGCOUNT may change while doing this, because of autocommands.
- if (count > opened_len || count <= 0) {
- count = opened_len;
- }
-
- // Don't execute Win/Buf Enter/Leave autocommands here.
- autocmd_no_enter++;
- autocmd_no_leave++;
- last_curwin = curwin;
- last_curtab = curtab;
- win_enter(lastwin, false);
- // ":tab drop file" should re-use an empty window to avoid "--remote-tab"
- // leaving an empty tab page when executed locally.
- if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
- && curbuf->b_ffname == NULL && !curbuf->b_changed) {
- use_firstwin = true;
- tab_drop_empty_window = true;
- }
-
- for (int i = 0; i < count && !got_int; i++) {
- if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
- arg_had_last = true;
- }
- if (opened[i] > 0) {
- // Move the already present window to below the current window
- if (curwin->w_arg_idx != i) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_arg_idx == i) {
- if (keep_tabs) {
- new_curwin = wp;
- new_curtab = curtab;
- } else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
- emsg(_("E249: window layout changed unexpectedly"));
- i = count;
- break;
- } else {
- win_move_after(wp, curwin);
- }
- break;
- }
- }
- }
- } else if (split_ret == OK) {
- // trigger events for tab drop
- if (tab_drop_empty_window && i == count - 1) {
- autocmd_no_enter--;
- }
- if (!use_firstwin) { // split current window
- p_ea_save = p_ea;
- p_ea = true; // use space from all windows
- split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
- p_ea = p_ea_save;
- if (split_ret == FAIL) {
- continue;
- }
- } else { // first window: do autocmd for leaving this buffer
- autocmd_no_leave--;
- }
-
- // edit file "i"
- curwin->w_arg_idx = i;
- if (i == 0) {
- new_curwin = curwin;
- new_curtab = curtab;
- }
- (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
- ((buf_hide(curwin->w_buffer)
- || bufIsChanged(curwin->w_buffer))
- ? ECMD_HIDE : 0) + ECMD_OLDBUF,
- curwin);
- if (tab_drop_empty_window && i == count - 1) {
- autocmd_no_enter++;
- }
- if (use_firstwin) {
- autocmd_no_leave++;
- }
- use_firstwin = false;
- }
- os_breakcheck();
-
- // When ":tab" was used open a new tab for a new window repeatedly.
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
- cmdmod.cmod_tab = 9999;
- }
- }
-
- // Remove the "lock" on the argument list.
- alist_unlink(alist);
-
- autocmd_no_enter--;
- // restore last referenced tabpage's curwin
- if (last_curtab != new_curtab) {
- if (valid_tabpage(last_curtab)) {
- goto_tabpage_tp(last_curtab, true, true);
- }
- if (win_valid(last_curwin)) {
- win_enter(last_curwin, false);
- }
- }
- // to window with first arg
- if (valid_tabpage(new_curtab)) {
- goto_tabpage_tp(new_curtab, true, true);
- }
- if (win_valid(new_curwin)) {
- win_enter(new_curwin, false);
- }
-
- autocmd_no_leave--;
- xfree(opened);
-}
-
/// @return true if "buf" is a prompt buffer.
bool bt_prompt(buf_T *buf)
FUNC_ATTR_PURE
@@ -5139,8 +3699,6 @@ static int chk_modeline(linenr_T lnum, int flags)
intmax_t vers;
int end;
int retval = OK;
- char *save_sourcing_name;
- linenr_T save_sourcing_lnum;
prev = -1;
for (s = (char *)ml_get(lnum); *s != NUL; s++) {
@@ -5185,10 +3743,8 @@ static int chk_modeline(linenr_T lnum, int flags)
s = linecopy = xstrdup(s); // copy the line, it will change
- save_sourcing_lnum = sourcing_lnum;
- save_sourcing_name = sourcing_name;
- sourcing_lnum = lnum; // prepare for emsg()
- sourcing_name = "modelines";
+ // prepare for emsg()
+ estack_push(ETYPE_MODELINE, "modelines", lnum);
end = false;
while (end == false) {
@@ -5228,7 +3784,7 @@ static int chk_modeline(linenr_T lnum, int flags)
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_MODELINE;
current_sctx.sc_seq = 0;
- current_sctx.sc_lnum = 0;
+ current_sctx.sc_lnum = lnum;
// Make sure no risky things are executed as a side effect.
secure = 1;
@@ -5243,9 +3799,7 @@ static int chk_modeline(linenr_T lnum, int flags)
s = e + 1; // advance to next part
}
- sourcing_lnum = save_sourcing_lnum;
- sourcing_name = save_sourcing_name;
-
+ estack_pop();
xfree(linecopy);
return retval;
@@ -5280,7 +3834,8 @@ bool bt_terminal(const buf_T *const buf)
}
/// @return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
-/// buffer. This means the buffer name is not a file name.
+/// buffer. This means the buffer name may not be a file name,
+/// at least not for writing the buffer.
bool bt_nofilename(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5290,6 +3845,17 @@ bool bt_nofilename(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p');
}
+/// @return true if "buf" is a "nofile", "quickfix", "terminal" or "prompt"
+/// buffer. This means the buffer is not to be read from a file.
+static bool bt_nofileread(const buf_T *const buf)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
+ || buf->b_p_bt[0] == 't'
+ || buf->b_p_bt[0] == 'q'
+ || buf->b_p_bt[0] == 'p');
+}
+
/// @return true if "buf" has 'buftype' set to "nofile".
bool bt_nofile(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
@@ -5488,7 +4054,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
buf->b_signcols.max++;
}
buf->b_signcols.size++;
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
return;
}
@@ -5509,7 +4075,7 @@ void buf_signcols_add_check(buf_T *buf, sign_entry_T *added)
buf->b_signcols.size = linesum;
buf->b_signcols.max = linesum;
buf->b_signcols.sentinel = added->se_lnum;
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
}
@@ -5528,7 +4094,7 @@ int buf_signcols(buf_T *buf, int maximum)
if (signcols != buf->b_signcols.size) {
buf->b_signcols.size = signcols;
buf->b_signcols.max = maximum;
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
buf->b_signcols.valid = true;
@@ -5637,14 +4203,19 @@ void wipe_buffer(buf_T *buf, bool aucmd)
/// @param bufnr Buffer to switch to, or 0 to create a new buffer.
///
/// @see curbufIsChanged()
-void buf_open_scratch(handle_T bufnr, char *bufname)
+///
+/// @return FAIL for failure, OK otherwise
+int buf_open_scratch(handle_T bufnr, char *bufname)
{
- (void)do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
+ if (do_ecmd((int)bufnr, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL) == FAIL) {
+ return FAIL;
+ }
apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
(void)setfname(curbuf, bufname, NULL, true);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
- set_option_value("bh", 0L, "hide", OPT_LOCAL);
- set_option_value("bt", 0L, "nofile", OPT_LOCAL);
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value_give_err("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL);
RESET_BINDING(curwin);
+ return OK;
}
diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h
index b452eb227e..d56a70dc7e 100644
--- a/src/nvim/buffer.h
+++ b/src/nvim/buffer.h
@@ -1,12 +1,13 @@
#ifndef NVIM_BUFFER_H
#define NVIM_BUFFER_H
-#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/func_attr.h"
+#include "nvim/grid_defs.h" // for StlClickRecord
#include "nvim/macros.h"
#include "nvim/memline.h"
#include "nvim/pos.h" // for linenr_T
-#include "nvim/screen.h" // for StlClickRecord
// Values for buflist_getfile()
enum getf_values {
@@ -61,6 +62,9 @@ enum bfa_values {
BFA_IGNORE_ABORT = 8, // do not abort for aborting()
};
+EXTERN char *msg_loclist INIT(= N_("[Location List]"));
+EXTERN char *msg_qflist INIT(= N_("[Quickfix List]"));
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "buffer.h.generated.h"
#endif
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 9457051947..748e94f0a1 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -30,14 +30,12 @@ typedef struct {
#include "nvim/option_defs.h"
// for jump list and tag stack sizes in a buffer and mark types
#include "nvim/mark_defs.h"
-// for u_header_T; needs buf_T.
+// for u_header_T
#include "nvim/undo_defs.h"
// for hashtab_T
#include "nvim/hashtab.h"
// for dict_T
#include "nvim/eval/typval.h"
-// for proftime_T
-#include "nvim/profile.h"
// for String
#include "nvim/api/private/defs.h"
// for Map(K, V)
@@ -118,11 +116,11 @@ typedef uint64_t disptick_T; // display tick type
* The taggy struct is used to store the information about a :tag command.
*/
typedef struct taggy {
- char_u *tagname; // tag name
+ char *tagname; // tag name
fmark_T fmark; // cursor position BEFORE ":tag"
int cur_match; // match number
int cur_fnum; // buffer number used for cur_match
- char_u *user_data; // used with tagfunc
+ char *user_data; // used with tagfunc
} taggy_T;
typedef struct buffblock buffblock_T;
@@ -133,7 +131,7 @@ typedef struct buffheader buffheader_T;
*/
struct buffblock {
buffblock_T *b_next; // pointer to next buffblock
- char_u b_str[1]; // contents (actually longer)
+ char b_str[1]; // contents (actually longer)
};
/*
@@ -161,39 +159,39 @@ typedef struct {
#define w_p_arab w_onebuf_opt.wo_arab // 'arabic'
int wo_bri;
#define w_p_bri w_onebuf_opt.wo_bri // 'breakindent'
- char_u *wo_briopt;
+ char *wo_briopt;
#define w_p_briopt w_onebuf_opt.wo_briopt // 'breakindentopt'
int wo_diff;
#define w_p_diff w_onebuf_opt.wo_diff // 'diff'
- char_u *wo_fdc;
+ char *wo_fdc;
#define w_p_fdc w_onebuf_opt.wo_fdc // 'foldcolumn'
- char_u *wo_fdc_save;
+ char *wo_fdc_save;
#define w_p_fdc_save w_onebuf_opt.wo_fdc_save // 'fdc' saved for diff mode
int wo_fen;
#define w_p_fen w_onebuf_opt.wo_fen // 'foldenable'
int wo_fen_save;
// 'foldenable' saved for diff mode
#define w_p_fen_save w_onebuf_opt.wo_fen_save
- char_u *wo_fdi;
+ char *wo_fdi;
#define w_p_fdi w_onebuf_opt.wo_fdi // 'foldignore'
long wo_fdl;
#define w_p_fdl w_onebuf_opt.wo_fdl // 'foldlevel'
long wo_fdl_save;
// 'foldlevel' state saved for diff mode
#define w_p_fdl_save w_onebuf_opt.wo_fdl_save
- char_u *wo_fdm;
+ char *wo_fdm;
#define w_p_fdm w_onebuf_opt.wo_fdm // 'foldmethod'
- char_u *wo_fdm_save;
+ char *wo_fdm_save;
#define w_p_fdm_save w_onebuf_opt.wo_fdm_save // 'fdm' saved for diff mode
long wo_fml;
#define w_p_fml w_onebuf_opt.wo_fml // 'foldminlines'
long wo_fdn;
#define w_p_fdn w_onebuf_opt.wo_fdn // 'foldnestmax'
- char_u *wo_fde;
+ char *wo_fde;
#define w_p_fde w_onebuf_opt.wo_fde // 'foldexpr'
- char_u *wo_fdt;
+ char *wo_fdt;
#define w_p_fdt w_onebuf_opt.wo_fdt // 'foldtext'
- char_u *wo_fmr;
+ char *wo_fmr;
#define w_p_fmr w_onebuf_opt.wo_fmr // 'foldmarker'
int wo_lbr;
#define w_p_lbr w_onebuf_opt.wo_lbr // 'linebreak'
@@ -203,7 +201,7 @@ typedef struct {
#define w_p_nu w_onebuf_opt.wo_nu // 'number'
int wo_rnu;
#define w_p_rnu w_onebuf_opt.wo_rnu // 'relativenumber'
- char_u *wo_ve;
+ char *wo_ve;
#define w_p_ve w_onebuf_opt.wo_ve // 'virtualedit'
unsigned wo_ve_flags;
#define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit'
@@ -217,7 +215,7 @@ typedef struct {
#define w_p_pvw w_onebuf_opt.wo_pvw // 'previewwindow'
int wo_rl;
#define w_p_rl w_onebuf_opt.wo_rl // 'rightleft'
- char_u *wo_rlc;
+ char *wo_rlc;
#define w_p_rlc w_onebuf_opt.wo_rlc // 'rightleftcmd'
long wo_scr;
#define w_p_scr w_onebuf_opt.wo_scr // 'scroll'
@@ -227,13 +225,13 @@ typedef struct {
#define w_p_cuc w_onebuf_opt.wo_cuc // 'cursorcolumn'
int wo_cul;
#define w_p_cul w_onebuf_opt.wo_cul // 'cursorline'
- char_u *wo_culopt;
+ char *wo_culopt;
#define w_p_culopt w_onebuf_opt.wo_culopt // 'cursorlineopt'
- char_u *wo_cc;
+ char *wo_cc;
#define w_p_cc w_onebuf_opt.wo_cc // 'colorcolumn'
- char_u *wo_sbr;
+ char *wo_sbr;
#define w_p_sbr w_onebuf_opt.wo_sbr // 'showbreak'
- char_u *wo_stl;
+ char *wo_stl;
#define w_p_stl w_onebuf_opt.wo_stl // 'statusline'
char *wo_wbr;
#define w_p_wbr w_onebuf_opt.wo_wbr // 'winbar'
@@ -247,7 +245,7 @@ typedef struct {
#define w_p_wrap w_onebuf_opt.wo_wrap // 'wrap'
int wo_wrap_save; // 'wrap' state saved for diff mode
#define w_p_wrap_save w_onebuf_opt.wo_wrap_save
- char_u *wo_cocu; // 'concealcursor'
+ char *wo_cocu; // 'concealcursor'
#define w_p_cocu w_onebuf_opt.wo_cocu
long wo_cole; // 'conceallevel'
#define w_p_cole w_onebuf_opt.wo_cole
@@ -255,14 +253,14 @@ typedef struct {
#define w_p_crb w_onebuf_opt.wo_crb // 'cursorbind'
int wo_crb_save; // 'cursorbind' state saved for diff mode
#define w_p_crb_save w_onebuf_opt.wo_crb_save
- char_u *wo_scl;
+ char *wo_scl;
#define w_p_scl w_onebuf_opt.wo_scl // 'signcolumn'
- char_u *wo_winhl;
+ char *wo_winhl;
#define w_p_winhl w_onebuf_opt.wo_winhl // 'winhighlight'
- char_u *wo_fcs;
-#define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars'
- char_u *wo_lcs;
+ char *wo_lcs;
#define w_p_lcs w_onebuf_opt.wo_lcs // 'listchars'
+ char *wo_fcs;
+#define w_p_fcs w_onebuf_opt.wo_fcs // 'fillchars'
long wo_winbl;
#define w_p_winbl w_onebuf_opt.wo_winbl // 'winblend'
@@ -310,8 +308,8 @@ typedef struct arglist {
//
// TODO(Felipe): move aentry_T to another header
typedef struct argentry {
- char_u *ae_fname; // file name as specified
- int ae_fnum; // buffer number with expanded file name
+ char *ae_fname; // file name as specified
+ int ae_fnum; // buffer number with expanded file name
} aentry_T;
#define ALIST(win) (win)->w_alist
@@ -327,8 +325,8 @@ typedef struct argentry {
* Used for the typeahead buffer: typebuf.
*/
typedef struct {
- char_u *tb_buf; // buffer for typed characters
- char_u *tb_noremap; // mapping flags for characters in tb_buf[]
+ uint8_t *tb_buf; // buffer for typed characters
+ uint8_t *tb_noremap; // mapping flags for characters in tb_buf[]
int tb_buflen; // size of tb_buf[]
int tb_off; // current position in tb_buf[]
int tb_len; // number of valid bytes in tb_buf[]
@@ -354,11 +352,11 @@ typedef struct {
*/
typedef struct mapblock mapblock_T;
struct mapblock {
- mapblock_T *m_next; // next mapblock in list
- char_u *m_keys; // mapped from, lhs
- char_u *m_str; // mapped to, rhs
- char_u *m_orig_str; // rhs as entered by the user
- LuaRef m_luaref; // lua function reference as rhs
+ mapblock_T *m_next; // next mapblock in list
+ uint8_t *m_keys; // mapped from, lhs
+ char *m_str; // mapped to, rhs
+ char *m_orig_str; // rhs as entered by the user
+ LuaRef m_luaref; // lua function reference as rhs
int m_keylen; // strlen(m_keys)
int m_mode; // valid mode
int m_simplified; // m_keys was simplified, do no use this map
@@ -368,8 +366,8 @@ struct mapblock {
char m_nowait; // <nowait> used
char m_expr; // <expr> used, m_str is an expression
sctx_T m_script_ctx; // SCTX where map was defined
- char *m_desc; // description of keymap
- bool m_replace_keycodes; // replace termcodes in lua function
+ char *m_desc; // description of mapping
+ bool m_replace_keycodes; // replace keycodes in result of expression
};
/// Used for highlighting in the status line.
@@ -433,7 +431,7 @@ typedef struct {
typedef struct {
hashtab_T b_keywtab; // syntax keywords hash table
hashtab_T b_keywtab_ic; // idem, ignore case
- int b_syn_error; // TRUE when error occurred in HL
+ bool b_syn_error; // true when error occurred in HL
bool b_syn_slow; // true when 'redrawtime' reached
int b_syn_ic; // ignore case for :syn cmds
int b_syn_foldlevel; // how to compute foldlevel on a line
@@ -442,14 +440,14 @@ typedef struct {
garray_T b_syn_clusters; // table for syntax clusters
int b_spell_cluster_id; // @Spell cluster ID or 0
int b_nospell_cluster_id; // @NoSpell cluster ID or 0
- int b_syn_containedin; // TRUE when there is an item with a
+ int b_syn_containedin; // true when there is an item with a
// "containedin" argument
int b_syn_sync_flags; // flags about how to sync
int16_t b_syn_sync_id; // group to sync on
linenr_T b_syn_sync_minlines; // minimal sync lines offset
linenr_T b_syn_sync_maxlines; // maximal sync lines offset
linenr_T b_syn_sync_linebreaks; // offset for multi-line pattern
- char_u *b_syn_linecont_pat; // line continuation pattern
+ char *b_syn_linecont_pat; // line continuation pattern
regprog_T *b_syn_linecont_prog; // line continuation program
syn_time_T b_syn_linecont_time;
int b_syn_linecont_ic; // ignore-case flag for above
@@ -478,17 +476,17 @@ typedef struct {
disptick_T b_sst_lasttick; // last display tick
// for spell checking
- garray_T b_langp; // list of pointers to slang_T, see spell.c
- bool b_spell_ismw[256]; // flags: is midword char
- char_u *b_spell_ismw_mb; // multi-byte midword chars
- char_u *b_p_spc; // 'spellcapcheck'
+ garray_T b_langp; // list of pointers to slang_T, see spell.c
+ bool b_spell_ismw[256]; // flags: is midword char
+ char *b_spell_ismw_mb; // multi-byte midword chars
+ char *b_p_spc; // 'spellcapcheck'
regprog_T *b_cap_prog; // program for 'spellcapcheck'
- char_u *b_p_spf; // 'spellfile'
- char_u *b_p_spl; // 'spelllang'
- char_u *b_p_spo; // 'spelloptions'
- int b_cjk; // all CJK letters as OK
- char_u b_syn_chartab[32]; // syntax iskeyword option
- char_u *b_syn_isk; // iskeyword option
+ char *b_p_spf; // 'spellfile'
+ char *b_p_spl; // 'spelllang'
+ char *b_p_spo; // 'spelloptions'
+ int b_cjk; // all CJK letters as OK
+ uint8_t b_syn_chartab[32]; // syntax iskeyword option
+ char *b_syn_isk; // iskeyword option
} synblock_T;
/// Type used for changedtick_di member in buf_T
@@ -602,7 +600,7 @@ struct file_buffer {
fmark_T b_namedm[NMARKS]; // current named marks (mark.c)
- // These variables are set when VIsual_active becomes FALSE
+ // These variables are set when VIsual_active becomes false
visualinfo_T b_visual;
int b_visual_mode_eval; // b_visual.vi_mode for visualmode()
@@ -655,10 +653,8 @@ struct file_buffer {
time_t b_u_time_cur; // uh_time of header below which we are now
long b_u_save_nr_cur; // file write nr after which we are now
- /*
- * variables for "U" command in undo.c
- */
- char_u *b_u_line_ptr; // saved line for "U" command
+ // variables for "U" command in undo.c
+ char *b_u_line_ptr; // saved line for "U" command
linenr_T b_u_line_lnum; // line number of line in u_line
colnr_T b_u_line_colnr; // optional column number
@@ -688,73 +684,73 @@ struct file_buffer {
int b_p_ai; ///< 'autoindent'
int b_p_ai_nopaste; ///< b_p_ai saved for paste mode
- char_u *b_p_bkc; ///< 'backupco
+ char *b_p_bkc; ///< 'backupco
unsigned int b_bkc_flags; ///< flags for 'backupco
int b_p_ci; ///< 'copyindent'
int b_p_bin; ///< 'binary'
int b_p_bomb; ///< 'bomb'
- char_u *b_p_bh; ///< 'bufhidden'
- char_u *b_p_bt; ///< 'buftype'
+ char *b_p_bh; ///< 'bufhidden'
+ char *b_p_bt; ///< 'buftype'
int b_has_qf_entry; ///< quickfix exists for buffer
int b_p_bl; ///< 'buflisted'
long b_p_channel; ///< 'channel'
int b_p_cin; ///< 'cindent'
- char_u *b_p_cino; ///< 'cinoptions'
- char_u *b_p_cink; ///< 'cinkeys'
- char_u *b_p_cinw; ///< 'cinwords'
- char_u *b_p_cinsd; ///< 'cinscopedecls'
- char_u *b_p_com; ///< 'comments'
- char_u *b_p_cms; ///< 'commentstring'
- char_u *b_p_cpt; ///< 'complete'
+ char *b_p_cino; ///< 'cinoptions'
+ char *b_p_cink; ///< 'cinkeys'
+ char *b_p_cinw; ///< 'cinwords'
+ char *b_p_cinsd; ///< 'cinscopedecls'
+ char *b_p_com; ///< 'comments'
+ char *b_p_cms; ///< 'commentstring'
+ char *b_p_cpt; ///< 'complete'
#ifdef BACKSLASH_IN_FILENAME
- char_u *b_p_csl; ///< 'completeslash'
+ char *b_p_csl; ///< 'completeslash'
#endif
- char_u *b_p_cfu; ///< 'completefunc'
- char_u *b_p_ofu; ///< 'omnifunc'
- char_u *b_p_tfu; ///< 'tagfunc'
- char_u *b_p_umf; ///< 'usermarkfunc'
+ char *b_p_cfu; ///< 'completefunc'
+ char *b_p_ofu; ///< 'omnifunc'
+ char *b_p_tfu; ///< 'tagfunc'
+ char *b_p_umf; ///< 'usermarkfunc'
int b_p_eol; ///< 'endofline'
int b_p_fixeol; ///< 'fixendofline'
int b_p_et; ///< 'expandtab'
int b_p_et_nobin; ///< b_p_et saved for binary mode
int b_p_et_nopaste; ///< b_p_et saved for paste mode
- char_u *b_p_fenc; ///< 'fileencoding'
- char_u *b_p_ff; ///< 'fileformat'
- char_u *b_p_ft; ///< 'filetype'
- char_u *b_p_fo; ///< 'formatoptions'
- char_u *b_p_flp; ///< 'formatlistpat'
+ char *b_p_fenc; ///< 'fileencoding'
+ char *b_p_ff; ///< 'fileformat'
+ char *b_p_ft; ///< 'filetype'
+ char *b_p_fo; ///< 'formatoptions'
+ char *b_p_flp; ///< 'formatlistpat'
int b_p_inf; ///< 'infercase'
- char_u *b_p_isk; ///< 'iskeyword'
- char_u *b_p_def; ///< 'define' local value
- char_u *b_p_inc; ///< 'include'
- char_u *b_p_inex; ///< 'includeexpr'
+ char *b_p_isk; ///< 'iskeyword'
+ char *b_p_def; ///< 'define' local value
+ char *b_p_inc; ///< 'include'
+ char *b_p_inex; ///< 'includeexpr'
uint32_t b_p_inex_flags; ///< flags for 'includeexpr'
- char_u *b_p_inde; ///< 'indentexpr'
+ char *b_p_inde; ///< 'indentexpr'
uint32_t b_p_inde_flags; ///< flags for 'indentexpr'
- char_u *b_p_indk; ///< 'indentkeys'
- char_u *b_p_fp; ///< 'formatprg'
- char_u *b_p_fex; ///< 'formatexpr'
+ char *b_p_indk; ///< 'indentkeys'
+ char *b_p_fp; ///< 'formatprg'
+ char *b_p_fex; ///< 'formatexpr'
uint32_t b_p_fex_flags; ///< flags for 'formatexpr'
- char_u *b_p_kp; ///< 'keywordprg'
+ char *b_p_kp; ///< 'keywordprg'
int b_p_lisp; ///< 'lisp'
- char_u *b_p_menc; ///< 'makeencoding'
- char_u *b_p_mps; ///< 'matchpairs'
+ char *b_p_menc; ///< 'makeencoding'
+ char *b_p_mps; ///< 'matchpairs'
int b_p_ml; ///< 'modeline'
int b_p_ml_nobin; ///< b_p_ml saved for binary mode
int b_p_ma; ///< 'modifiable'
- char_u *b_p_nf; ///< 'nrformats'
+ char *b_p_nf; ///< 'nrformats'
int b_p_pi; ///< 'preserveindent'
- char_u *b_p_qe; ///< 'quoteescape'
+ char *b_p_qe; ///< 'quoteescape'
int b_p_ro; ///< 'readonly'
long b_p_sw; ///< 'shiftwidth'
long b_p_scbk; ///< 'scrollback'
int b_p_si; ///< 'smartindent'
long b_p_sts; ///< 'softtabstop'
long b_p_sts_nopaste; ///< b_p_sts saved for paste mode
- char_u *b_p_sua; ///< 'suffixesadd'
+ char *b_p_sua; ///< 'suffixesadd'
int b_p_swf; ///< 'swapfile'
long b_p_smc; ///< 'synmaxcol'
- char_u *b_p_syn; ///< 'syntax'
+ char *b_p_syn; ///< 'syntax'
long b_p_ts; ///< 'tabstop'
long b_p_tw; ///< 'textwidth'
long b_p_tw_nobin; ///< b_p_tw saved for binary mode
@@ -762,29 +758,29 @@ struct file_buffer {
long b_p_wm; ///< 'wrapmargin'
long b_p_wm_nobin; ///< b_p_wm saved for binary mode
long b_p_wm_nopaste; ///< b_p_wm saved for paste mode
- char_u *b_p_vsts; ///< 'varsofttabstop'
- long *b_p_vsts_array; ///< 'varsofttabstop' in internal format
- char_u *b_p_vsts_nopaste; ///< b_p_vsts saved for paste mode
- char_u *b_p_vts; ///< 'vartabstop'
- long *b_p_vts_array; ///< 'vartabstop' in internal format
- char_u *b_p_keymap; ///< 'keymap'
+ char *b_p_vsts; ///< 'varsofttabstop'
+ long *b_p_vsts_array; ///< 'varsofttabstop' in internal format
+ char *b_p_vsts_nopaste; ///< b_p_vsts saved for paste mode
+ char *b_p_vts; ///< 'vartabstop'
+ long *b_p_vts_array; ///< 'vartabstop' in internal format
+ char *b_p_keymap; ///< 'keymap'
// local values for options which are normally global
- char_u *b_p_gp; ///< 'grepprg' local value
- char_u *b_p_mp; ///< 'makeprg' local value
- char_u *b_p_efm; ///< 'errorformat' local value
- char_u *b_p_ep; ///< 'equalprg' local value
- char_u *b_p_path; ///< 'path' local value
+ char *b_p_gp; ///< 'grepprg' local value
+ char *b_p_mp; ///< 'makeprg' local value
+ char *b_p_efm; ///< 'errorformat' local value
+ char *b_p_ep; ///< 'equalprg' local value
+ char *b_p_path; ///< 'path' local value
int b_p_ar; ///< 'autoread' local value
- char_u *b_p_tags; ///< 'tags' local value
- char_u *b_p_tc; ///< 'tagcase' local value
+ char *b_p_tags; ///< 'tags' local value
+ char *b_p_tc; ///< 'tagcase' local value
unsigned b_tc_flags; ///< flags for 'tagcase'
- char_u *b_p_dict; ///< 'dictionary' local value
- char_u *b_p_tsr; ///< 'thesaurus' local value
- char_u *b_p_tsrfu; ///< 'thesaurusfunc' local value
+ char *b_p_dict; ///< 'dictionary' local value
+ char *b_p_tsr; ///< 'thesaurus' local value
+ char *b_p_tsrfu; ///< 'thesaurusfunc' local value
long b_p_ul; ///< 'undolevels' local value
int b_p_udf; ///< 'undofile'
- char_u *b_p_lw; ///< 'lispwords' local value
+ char *b_p_lw; ///< 'lispwords' local value
// end of buffer options
@@ -852,7 +848,7 @@ struct file_buffer {
* spell buffer - used for spell info, never displayed and doesn't have a
* file name.
*/
- bool b_help; // TRUE for help file buffer (when set b_p_bt
+ bool b_help; // true for help file buffer (when set b_p_bt
// is "help")
bool b_spell; // True for a spell file buffer, most fields
// are not used! Use the B_SPELL macro to
@@ -977,15 +973,15 @@ struct tabpage_S {
* When the display is changed (e.g., when clearing the screen) w_lines_valid
* is changed to exclude invalid entries.
* When making changes to the buffer, wl_valid is reset to indicate wl_size
- * may not reflect what is actually in the buffer. When wl_valid is FALSE,
+ * may not reflect what is actually in the buffer. When wl_valid is false,
* the entries can only be used to count the number of displayed lines used.
* wl_lnum and wl_lastlnum are invalid too.
*/
typedef struct w_line {
linenr_T wl_lnum; // buffer line number for logical line
uint16_t wl_size; // height in screen lines
- char wl_valid; // TRUE values are valid for text in buffer
- char wl_folded; // TRUE when this is a range of folded lines
+ char wl_valid; // true values are valid for text in buffer
+ char wl_folded; // true when this is a range of folded lines
linenr_T wl_lastlnum; // last buffer line number for logical line
} wline_T;
@@ -1135,43 +1131,6 @@ typedef struct {
pos_T w_cursor_corr; // corrected cursor position
} pos_save_T;
-/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
-/// \addtogroup MENU_INDEX
-/// @{
-enum {
- MENU_INDEX_INVALID = -1,
- MENU_INDEX_NORMAL = 0,
- MENU_INDEX_VISUAL = 1,
- MENU_INDEX_SELECT = 2,
- MENU_INDEX_OP_PENDING = 3,
- MENU_INDEX_INSERT = 4,
- MENU_INDEX_CMDLINE = 5,
- MENU_INDEX_TERMINAL = 6,
- MENU_INDEX_TIP = 7,
- MENU_MODES = 8,
-};
-
-typedef struct VimMenu vimmenu_T;
-
-struct VimMenu {
- int modes; ///< Which modes is this menu visible for
- int enabled; ///< for which modes the menu is enabled
- char *name; ///< Name of menu, possibly translated
- char *dname; ///< Displayed Name ("name" without '&')
- char *en_name; ///< "name" untranslated, NULL when
- ///< was not translated
- char *en_dname; ///< NULL when "dname" untranslated
- int mnemonic; ///< mnemonic key (after '&')
- char *actext; ///< accelerator text (after TAB)
- long priority; ///< Menu order priority
- char *strings[MENU_MODES]; ///< Mapped string for each mode
- int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode
- bool silent[MENU_MODES]; ///< A silent flag for each mode
- vimmenu_T *children; ///< Children of sub-menu
- vimmenu_T *parent; ///< Parent of menu
- vimmenu_T *next; ///< Next item in menu
-};
-
/// Structure which contains all information that belongs to a window.
///
/// All row numbers are relative to the start of the window, except w_winrow.
@@ -1183,11 +1142,14 @@ struct window_S {
synblock_T *w_s; ///< for :ownsyntax
+ int w_ns_hl;
+ int w_ns_hl_winhl;
+ int w_ns_hl_active;
+ int *w_ns_hl_attr;
+
int w_hl_id_normal; ///< 'winhighlight' normal id
int w_hl_attr_normal; ///< 'winhighlight' normal final attrs
-
- int w_hl_ids[HLF_COUNT]; ///< 'winhighlight' id
- int w_hl_attrs[HLF_COUNT]; ///< 'winhighlight' final attrs
+ int w_hl_attr_normalnc; ///< 'winhighlight' NormalNC final attrs
int w_hl_needs_update; ///< attrs need to be recalculated
@@ -1266,8 +1228,8 @@ struct window_S {
*/
linenr_T w_topline; /* buffer line number of the line at the
top of the window */
- char w_topline_was_set; /* flag set to TRUE when topline is set,
- e.g. by winrestview() */
+ char w_topline_was_set; // flag set to true when topline is set,
+ // e.g. by winrestview()
int w_topfill; // number of filler lines above w_topline
int w_old_topfill; // w_topfill at last redraw
bool w_botfill; // true when filler lines are actually
@@ -1279,9 +1241,10 @@ struct window_S {
colnr_T w_skipcol; // starting column when a single line
// doesn't fit in the window
- // four fields that are only used when there is a WinScrolled autocommand
+ // five fields that are only used when there is a WinScrolled autocommand
linenr_T w_last_topline; ///< last known value for w_topline
colnr_T w_last_leftcol; ///< last known value for w_leftcol
+ colnr_T w_last_skipcol; ///< last known value for w_skipcol
int w_last_width; ///< last known value for w_width
int w_last_height; ///< last known value for w_height
@@ -1390,7 +1353,7 @@ struct window_S {
int w_redr_type; // type of redraw to be performed on win
int w_upd_rows; // number of window lines to update when
- // w_redr_type is REDRAW_TOP
+ // w_redr_type is UPD_REDRAW_TOP
linenr_T w_redraw_top; // when != 0: first line needing redraw
linenr_T w_redraw_bot; // when != 0: last line needing redraw
bool w_redr_status; // if true statusline/winbar must be redrawn
@@ -1402,7 +1365,7 @@ struct window_S {
linenr_T w_ru_topline; // topline shown in ruler
linenr_T w_ru_line_count; // line count used for ruler
int w_ru_topfill; // topfill shown in ruler
- char w_ru_empty; // TRUE if ruler shows 0-1 (empty line)
+ char w_ru_empty; // true if ruler shows 0-1 (empty line)
int w_alt_fnum; // alternate file (for # and CTRL-^)
@@ -1509,11 +1472,6 @@ struct window_S {
size_t w_winbar_click_defs_size;
};
-static inline int win_hl_attr(win_T *wp, int hlf)
-{
- return wp->w_hl_attrs[hlf];
-}
-
/// Macros defined in Vim, but not in Neovim
#define CHANGEDTICK(buf) \
(=== Include buffer.h & use buf_(get|set|inc) _changedtick ===)
diff --git a/src/nvim/change.c b/src/nvim/change.c
index c063ece907..85ab92e49b 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -10,6 +10,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/extmark.h"
@@ -23,9 +24,9 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
+#include "nvim/textformat.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
@@ -96,8 +97,7 @@ void changed(void)
// Create a swap file if that is wanted.
// Don't do this for "nofile" and "nowrite" buffer types.
- if (curbuf->b_may_swap
- && !bt_dontwrite(curbuf)) {
+ if (curbuf->b_may_swap && !bt_dontwrite(curbuf)) {
bool save_need_wait_return = need_wait_return;
need_wait_return = false;
@@ -223,8 +223,8 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == curbuf) {
// Mark this window to be redrawn later.
- if (wp->w_redr_type < VALID) {
- wp->w_redr_type = VALID;
+ if (wp->w_redr_type < UPD_VALID) {
+ wp->w_redr_type = UPD_VALID;
}
// Check if a change in the buffer has invalidated the cached
@@ -301,17 +301,17 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
// requires a redraw.
if (wp->w_p_rnu && xtra != 0) {
wp->w_last_cursor_lnum_rnu = 0;
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
// Cursor line highlighting probably need to be updated with
- // "VALID" if it's below the change.
+ // "UPD_VALID" if it's below the change.
// If the cursor line is inside the change we need to redraw more.
if (wp->w_p_cul) {
if (xtra == 0) {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
} else if (lnum <= wp->w_last_cursorline) {
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
}
}
}
@@ -319,8 +319,8 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T
// Call update_screen() later, which checks out what needs to be redrawn,
// since it notices b_mod_set and then uses b_mod_*.
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
// when the cursor line is changed always trigger CursorMoved
@@ -364,7 +364,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
if (curwin->w_p_diff) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
linenr_T wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changedOneline(wp->w_buffer, wlnum);
@@ -421,12 +421,12 @@ void deleted_lines(linenr_T lnum, linenr_T count)
/// be triggered to display the cursor.
void deleted_lines_mark(linenr_T lnum, long count)
{
- // if we deleted the entire buffer, we need to implicitly add a new empty line
bool made_empty = (count > 0) && curbuf->b_ml.ml_flags & ML_EMPTY;
- mark_adjust(lnum, (linenr_T)(lnum + count - 1), (long)MAXLNUM,
- -(linenr_T)count + (made_empty?1:0),
- kExtmarkUndo);
+ mark_adjust(lnum, (linenr_T)(lnum + count - 1), MAXLNUM, -(linenr_T)count, kExtmarkNOOP);
+ // if we deleted the entire buffer, we need to implicitly add a new empty line
+ extmark_adjust(curbuf, lnum, (linenr_T)(lnum + count - 1), MAXLNUM,
+ -(linenr_T)count + (made_empty ? 1 : 0), kExtmarkUndo);
changed_lines(lnum, 0, lnum + (linenr_T)count, (linenr_T)(-count), true);
}
@@ -493,7 +493,7 @@ void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, linenr_T xtra, bo
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_p_diff && wp != curwin) {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
wlnum = diff_lnum_win(lnum, wp);
if (wlnum > 0) {
changed_lines_buf(wp->w_buffer, wlnum,
@@ -534,21 +534,72 @@ void unchanged(buf_T *buf, int ff, bool always_inc_changedtick)
}
}
+/// Save the current values of 'fileformat' and 'fileencoding', so that we know
+/// the file must be considered changed when the value is different.
+void save_file_ff(buf_T *buf)
+{
+ buf->b_start_ffc = (unsigned char)(*buf->b_p_ff);
+ buf->b_start_eol = buf->b_p_eol;
+ buf->b_start_bomb = buf->b_p_bomb;
+
+ // Only use free/alloc when necessary, they take time.
+ if (buf->b_start_fenc == NULL
+ || STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
+ xfree(buf->b_start_fenc);
+ buf->b_start_fenc = xstrdup(buf->b_p_fenc);
+ }
+}
+
+/// Return true if 'fileformat' and/or 'fileencoding' has a different value
+/// from when editing started (save_file_ff() called).
+/// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
+/// changed and 'binary' is not set.
+/// Also when 'endofline' was changed and 'fixeol' is not set.
+/// When "ignore_empty" is true don't consider a new, empty buffer to be
+/// changed.
+bool file_ff_differs(buf_T *buf, bool ignore_empty)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // In a buffer that was never loaded the options are not valid.
+ if (buf->b_flags & BF_NEVERLOADED) {
+ return false;
+ }
+ if (ignore_empty
+ && (buf->b_flags & BF_NEW)
+ && buf->b_ml.ml_line_count == 1
+ && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
+ return false;
+ }
+ if (buf->b_start_ffc != *buf->b_p_ff) {
+ return true;
+ }
+ if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
+ return true;
+ }
+ if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
+ return true;
+ }
+ if (buf->b_start_fenc == NULL) {
+ return *buf->b_p_fenc != NUL;
+ }
+ return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
+}
+
/// Insert string "p" at the cursor position. Stops at a NUL byte.
/// Handles Replace mode and multi-byte characters.
-void ins_bytes(char_u *p)
+void ins_bytes(char *p)
{
ins_bytes_len(p, STRLEN(p));
}
/// Insert string "p" with length "len" at the cursor position.
/// Handles Replace mode and multi-byte characters.
-void ins_bytes_len(char_u *p, size_t len)
+void ins_bytes_len(char *p, size_t len)
{
size_t n;
for (size_t i = 0; i < len; i += n) {
// avoid reading past p[len]
- n = (size_t)utfc_ptr2len_len(p + i, (int)(len - i));
+ n = (size_t)utfc_ptr2len_len((char_u *)p + i, (int)(len - i));
ins_char_bytes(p + i, n);
}
}
@@ -560,18 +611,18 @@ void ins_bytes_len(char_u *p, size_t len)
/// convert bytes to a character.
void ins_char(int c)
{
- char_u buf[MB_MAXBYTES + 1];
- size_t n = (size_t)utf_char2bytes(c, (char *)buf);
+ char buf[MB_MAXBYTES + 1];
+ size_t n = (size_t)utf_char2bytes(c, buf);
// When "c" is 0x100, 0x200, etc. we don't want to insert a NUL byte.
// Happens for CTRL-Vu9900.
if (buf[0] == 0) {
buf[0] = '\n';
}
- ins_char_bytes((char_u *)buf, n);
+ ins_char_bytes(buf, n);
}
-void ins_char_bytes(char_u *buf, size_t charlen)
+void ins_char_bytes(char *buf, size_t charlen)
{
// Break tabs if needed.
if (virtual_active() && curwin->w_cursor.coladd > 0) {
@@ -580,7 +631,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
size_t col = (size_t)curwin->w_cursor.col;
linenr_T lnum = curwin->w_cursor.lnum;
- char_u *oldp = ml_get(lnum);
+ char *oldp = (char *)ml_get(lnum);
size_t linelen = STRLEN(oldp) + 1; // length of old line including NUL
// The lengths default to the values for when not replacing.
@@ -610,7 +661,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
if (vcol > new_vcol && oldp[col + oldlen] == TAB) {
break;
}
- oldlen += (size_t)utfc_ptr2len((char *)oldp + col + oldlen);
+ oldlen += (size_t)utfc_ptr2len(oldp + col + oldlen);
// Deleted a bit too much, insert spaces.
if (vcol > new_vcol) {
newlen += (size_t)(vcol - new_vcol);
@@ -619,7 +670,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
curwin->w_p_list = old_list;
} else if (oldp[col] != NUL) {
// normal replace
- oldlen = (size_t)utfc_ptr2len((char *)oldp + col);
+ oldlen = (size_t)utfc_ptr2len(oldp + col);
}
// Push the replaced bytes onto the replace stack, so that they can be
@@ -632,7 +683,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
}
- char_u *newp = xmalloc(linelen + newlen - oldlen);
+ char *newp = xmalloc(linelen + newlen - oldlen);
// Copy bytes before the cursor.
if (col > 0) {
@@ -640,7 +691,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
// Copy bytes after the changed character(s).
- char_u *p = newp + col;
+ char *p = newp + col;
if (linelen > col + oldlen) {
memmove(p + newlen, oldp + col + oldlen,
(size_t)(linelen - col - oldlen));
@@ -655,7 +706,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
}
// Replace the line in the buffer.
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
// mark the buffer as changed and prepare for displaying
inserted_bytes(lnum, (colnr_T)col, (int)oldlen, (int)newlen);
@@ -665,7 +716,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
if (p_sm && (State & MODE_INSERT)
&& msg_silent == 0
&& !ins_compl_active()) {
- showmatch(utf_ptr2char((char *)buf));
+ showmatch(utf_ptr2char(buf));
}
if (!p_ri || (State & REPLACE_FLAG)) {
@@ -678,7 +729,7 @@ void ins_char_bytes(char_u *buf, size_t charlen)
/// Insert a string at the cursor position.
/// Note: Does NOT handle Replace mode.
/// Caller must have prepared for undo.
-void ins_str(char_u *s)
+void ins_str(char *s)
{
int newlen = (int)STRLEN(s);
linenr_T lnum = curwin->w_cursor.lnum;
@@ -688,10 +739,10 @@ void ins_str(char_u *s)
}
colnr_T col = curwin->w_cursor.col;
- char_u *oldp = ml_get(lnum);
+ char *oldp = (char *)ml_get(lnum);
int oldlen = (int)STRLEN(oldp);
- char_u *newp = (char_u *)xmalloc((size_t)oldlen + (size_t)newlen + 1);
+ char *newp = xmalloc((size_t)oldlen + (size_t)newlen + 1);
if (col > 0) {
memmove(newp, oldp, (size_t)col);
}
@@ -699,7 +750,7 @@ void ins_str(char_u *s)
int bytes = oldlen - col + 1;
assert(bytes >= 0);
memmove(newp + col + newlen, oldp + col, (size_t)bytes);
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
inserted_bytes(lnum, col, 0, newlen);
curwin->w_cursor.col += newlen;
}
@@ -723,9 +774,9 @@ int del_char(bool fixpos)
int del_chars(long count, int fixpos)
{
int bytes = 0;
- char_u *p = get_cursor_pos_ptr();
+ char *p = (char *)get_cursor_pos_ptr();
for (long i = 0; i < count && *p != NUL; i++) {
- int l = utfc_ptr2len((char *)p);
+ int l = utfc_ptr2len(p);
bytes += l;
p += l;
}
@@ -746,7 +797,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
bool fixpos = fixpos_arg;
- char_u *oldp = ml_get(lnum);
+ char *oldp = (char *)ml_get(lnum);
colnr_T oldlen = (colnr_T)STRLEN(oldp);
// Can't do anything when the cursor is on the NUL after the line.
@@ -766,7 +817,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// If 'delcombine' is set and deleting (less than) one character, only
// delete the last combining character.
if (p_deco && use_delcombine
- && utfc_ptr2len((char *)oldp + col) >= count) {
+ && utfc_ptr2len(oldp + col) >= count) {
int cc[MAX_MCO];
(void)utfc_ptr2char(oldp + col, cc);
@@ -775,9 +826,9 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
int n = col;
do {
col = n;
- count = utf_ptr2len((char *)oldp + n);
+ count = utf_ptr2len(oldp + n);
n += count;
- } while (utf_composinglike(oldp + col, oldp + n));
+ } while (utf_composinglike((char_u *)oldp + col, (char_u *)oldp + n));
fixpos = false;
}
}
@@ -792,7 +843,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
&& (get_ve_flags() & VE_ONEMORE) == 0) {
curwin->w_cursor.col--;
curwin->w_cursor.coladd = 0;
- curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
+ curwin->w_cursor.col -= utf_head_off((char_u *)oldp, (char_u *)oldp + curwin->w_cursor.col);
}
count = oldlen - col;
movelen = 1;
@@ -801,7 +852,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// If the old line has been allocated the deletion can be done in the
// existing line. Otherwise a new line has to be allocated.
bool was_alloced = ml_line_alloced(); // check if oldp was allocated
- char_u *newp;
+ char *newp;
if (was_alloced) {
ml_add_deleted_len(curbuf->b_ml.ml_line_ptr, oldlen);
newp = oldp; // use same allocated memory
@@ -811,7 +862,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
}
memmove(newp + col, oldp + col + count, (size_t)movelen);
if (!was_alloced) {
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
}
// mark the buffer as changed and prepare for displaying
@@ -823,10 +874,10 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
/// 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.
-int copy_indent(int size, char_u *src)
+int copy_indent(int size, char *src)
{
- char_u *p = NULL;
- char_u *line = NULL;
+ char *p = NULL;
+ char *line = NULL;
int ind_len;
int line_len = 0;
int tab_pad;
@@ -838,7 +889,7 @@ int copy_indent(int size, char_u *src)
ind_len = 0;
int ind_done = 0;
int ind_col = 0;
- char_u *s = src;
+ char *s = src;
// Count/copy the usable portion of the source line.
while (todo > 0 && ascii_iswhite(*s)) {
@@ -924,7 +975,7 @@ int copy_indent(int size, char_u *src)
memmove(p, get_cursor_line_ptr(), (size_t)line_len);
// Replace the line
- ml_replace(curwin->w_cursor.lnum, (char *)line, false);
+ ml_replace(curwin->w_cursor.lnum, line, false);
// Put the cursor after the indent.
curwin->w_cursor.col = ind_len;
@@ -955,8 +1006,8 @@ int copy_indent(int size, char_u *src)
/// @return true on success, false on failure
int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
{
- char_u *next_line = NULL; // copy of the next line
- char_u *p_extra = NULL; // what goes to next line
+ char *next_line = NULL; // copy of the next line
+ char *p_extra = NULL; // what goes to next line
colnr_T less_cols = 0; // less columns for mark in new line
colnr_T less_cols_off = 0; // columns to skip for mark adjust
pos_T old_cursor; // old cursor position
@@ -969,9 +1020,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
int comment_start = 0; // start index of the comment leader
char *lead_flags; // position in 'comments' for comment leader
char *leader = NULL; // copy of comment leader
- char_u *allocated = NULL; // allocated memory
+ char *allocated = NULL; // allocated memory
char *p;
- char_u saved_char = NUL; // init for GCC
+ char saved_char = NUL; // init for GCC
pos_T *pos;
bool do_si = may_do_si();
bool do_cindent;
@@ -985,7 +1036,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
colnr_T mincol = curwin->w_cursor.col + 1;
// make a copy of the current line so we can mess with it
- char_u *saved_line = vim_strsave(get_cursor_line_ptr());
+ char *saved_line = (char *)vim_strsave(get_cursor_line_ptr());
if (State & VREPLACE_FLAG) {
// With MODE_VREPLACE we make a copy of the next line, which we will be
@@ -996,9 +1047,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// the line, replacing what was there before and pushing the right
// stuff onto the replace stack. -- webb.
if (curwin->w_cursor.lnum < orig_line_count) {
- next_line = vim_strsave(ml_get(curwin->w_cursor.lnum + 1));
+ next_line = (char *)vim_strsave(ml_get(curwin->w_cursor.lnum + 1));
} else {
- next_line = vim_strsave((char_u *)"");
+ next_line = xstrdup("");
}
// In MODE_VREPLACE state, a NL replaces the rest of the line, and
@@ -1008,9 +1059,9 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// autoindent etc) a bit later.
replace_push(NUL); // Call twice because BS over NL expects it
replace_push(NUL);
- p = (char *)saved_line + curwin->w_cursor.col;
+ p = saved_line + curwin->w_cursor.col;
while (*p != NUL) {
- p += replace_push_mb((char_u *)p);
+ p += replace_push_mb(p);
}
saved_line[curwin->w_cursor.col] = NUL;
}
@@ -1018,7 +1069,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if ((State & MODE_INSERT) && (State & VREPLACE_FLAG) == 0) {
p_extra = saved_line + curwin->w_cursor.col;
if (do_si) { // need first char after new line break
- p = skipwhite((char *)p_extra);
+ p = skipwhite(p_extra);
first_char = (unsigned char)(*p);
}
extra_len = (int)STRLEN(p_extra);
@@ -1058,7 +1109,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
char *ptr;
old_cursor = curwin->w_cursor;
- ptr = (char *)saved_line;
+ ptr = saved_line;
if (flags & OPENLINE_DO_COM) {
lead_len = get_leader_len(ptr, NULL, false, true);
} else {
@@ -1142,7 +1193,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Don't do this if the previous line ended in ';' or
// '}'.
} else if (last_char != ';' && last_char != '}'
- && cin_is_cinword((char_u *)ptr)) {
+ && cin_is_cinword(ptr)) {
did_si = true;
}
}
@@ -1192,13 +1243,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// This may then be inserted in front of the new line.
end_comment_pending = NUL;
if (flags & OPENLINE_DO_COM) {
- lead_len = get_leader_len((char *)saved_line, &lead_flags, dir == BACKWARD, true);
+ lead_len = get_leader_len(saved_line, &lead_flags, dir == BACKWARD, true);
if (lead_len == 0 && curbuf->b_p_cin && do_cindent && dir == FORWARD
&& (!has_format_option(FO_NO_OPEN_COMS) || (flags & OPENLINE_FORMAT))) {
// Check for a line comment after code.
comment_start = check_linecomment(saved_line);
if (comment_start != MAXCOL) {
- lead_len = get_leader_len((char *)saved_line + comment_start, &lead_flags, false, true);
+ lead_len = get_leader_len(saved_line + comment_start, &lead_flags, false, true);
if (lead_len != 0) {
lead_len += comment_start;
if (did_do_comment != NULL) {
@@ -1213,13 +1264,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (lead_len > 0) {
char *lead_repl = NULL; // replaces comment leader
int lead_repl_len = 0; // length of *lead_repl
- char_u lead_middle[COM_MAX_LEN]; // middle-comment string
- char_u lead_end[COM_MAX_LEN]; // end-comment string
- char_u *comment_end = NULL; // where lead_end has been found
+ char lead_middle[COM_MAX_LEN]; // middle-comment string
+ char lead_end[COM_MAX_LEN]; // end-comment string
+ char *comment_end = NULL; // where lead_end has been found
int extra_space = false; // append extra space
int current_flag;
int require_blank = false; // requires blank after middle
- char_u *p2;
+ char *p2;
// If the comment leader has the start, middle or end flag, it may not
// be used or may be replaced with the middle leader.
@@ -1261,15 +1312,15 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
size_t n = copy_option_part(&p, (char *)lead_end, COM_MAX_LEN, ",");
if (end_comment_pending == -1) { // we can set it now
- end_comment_pending = lead_end[n - 1];
+ end_comment_pending = (unsigned char)lead_end[n - 1];
}
// If the end of the comment is in the same line, don't use
// the comment leader.
if (dir == FORWARD) {
- for (p = (char *)saved_line + lead_len; *p; p++) {
+ for (p = saved_line + lead_len; *p; p++) {
if (STRNCMP(p, lead_end, n) == 0) {
- comment_end = (char_u *)p;
+ comment_end = p;
lead_len = 0;
break;
}
@@ -1302,17 +1353,17 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Remember where the end is, might want to use it to find the
// start (for C-comments).
if (dir == FORWARD) {
- comment_end = (char_u *)skipwhite((char *)saved_line);
+ comment_end = skipwhite(saved_line);
lead_len = 0;
break;
}
// Doing "O" on the end of a comment inserts the middle leader.
// Find the string for the middle leader, searching backwards.
- while (p > (char *)curbuf->b_p_com && *p != ',') {
+ while (p > curbuf->b_p_com && *p != ',') {
p--;
}
- for (lead_repl = p; lead_repl > (char *)curbuf->b_p_com
+ for (lead_repl = p; lead_repl > curbuf->b_p_com
&& lead_repl[-1] != ':'; lead_repl--) {}
lead_repl_len = (int)(p - lead_repl);
@@ -1321,7 +1372,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
extra_space = true;
// Check whether we allow automatic ending of comments
- for (p2 = (char_u *)p; *p2 && *p2 != ':'; p2++) {
+ for (p2 = p; *p2 && *p2 != ':'; p2++) {
if (*p2 == COM_AUTO_END) {
end_comment_pending = -1; // means we want to set it
}
@@ -1331,7 +1382,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
while (*p2 && *p2 != ',') {
p2++;
}
- end_comment_pending = p2[-1];
+ end_comment_pending = (unsigned char)p2[-1];
}
break;
}
@@ -1357,7 +1408,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
+ 1;
assert(bytes >= 0);
leader = xmalloc((size_t)bytes);
- allocated = (char_u *)leader; // remember to free it later
+ allocated = leader; // remember to free it later
STRLCPY(leader, saved_line, lead_len + 1);
@@ -1391,7 +1442,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Compute the length of the replaced characters in
// screen characters, not bytes.
{
- int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len);
+ int repl_size = vim_strnsize(lead_repl, lead_repl_len);
int old_size = 0;
char *endp = p;
int l;
@@ -1436,13 +1487,13 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// screen characters, not bytes. Move the part that is
// not to be overwritten.
{
- int repl_size = vim_strnsize((char_u *)lead_repl, lead_repl_len);
+ int repl_size = vim_strnsize(lead_repl, lead_repl_len);
int i;
int l;
for (i = 0; i < lead_len && p[i] != NUL; i += l) {
l = utfc_ptr2len(p + i);
- if (vim_strnsize((char_u *)p, i + l) > repl_size) {
+ if (vim_strnsize(p, i + l) > repl_size) {
break;
}
}
@@ -1485,7 +1536,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// Recompute the indent, it may have changed.
if (curbuf->b_p_ai || do_si) {
- newindent = get_indent_str_vtab((char_u *)leader,
+ newindent = get_indent_str_vtab(leader,
curbuf->b_p_ts,
curbuf->b_p_vts_array, false);
}
@@ -1568,7 +1619,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
if (curbuf->b_p_ai || (flags & OPENLINE_DELSPACES)) {
while ((*p_extra == ' ' || *p_extra == '\t')
- && !utf_iscomposing(utf_ptr2char((char *)p_extra + 1))) {
+ && !utf_iscomposing(utf_ptr2char(p_extra + 1))) {
if (REPLACE_NORMAL(State)) {
replace_push(*p_extra);
}
@@ -1582,7 +1633,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
if (p_extra == NULL) {
- p_extra = (char_u *)""; // append empty line
+ p_extra = ""; // append empty line
}
// concatenate leader and p_extra, if there is a leader
@@ -1602,7 +1653,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
}
}
STRCAT(leader, p_extra);
- p_extra = (char_u *)leader;
+ p_extra = leader;
did_ai = true; // So truncating blanks works with comments
less_cols -= lead_len;
} else {
@@ -1615,7 +1666,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
curwin->w_cursor.lnum--;
}
if ((State & VREPLACE_FLAG) == 0 || old_cursor.lnum >= orig_line_count) {
- if (ml_append(curwin->w_cursor.lnum, (char *)p_extra, (colnr_T)0, false) == FAIL) {
+ if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) {
goto theend;
}
// Postpone calling changed_lines(), because it would mess up folding
@@ -1637,7 +1688,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
(void)u_save_cursor(); // errors are ignored!
vr_lines_changed++;
}
- ml_replace(curwin->w_cursor.lnum, (char *)p_extra, true);
+ ml_replace(curwin->w_cursor.lnum, p_extra, true);
changed_bytes(curwin->w_cursor.lnum, 0);
// TODO(vigoux): extmark_splice_cols here??
curwin->w_cursor.lnum--;
@@ -1702,7 +1753,7 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
if (trunc_line && !(flags & OPENLINE_KEEPTRAIL)) {
truncate_spaces(saved_line);
}
- ml_replace(curwin->w_cursor.lnum, (char *)saved_line, false);
+ ml_replace(curwin->w_cursor.lnum, saved_line, false);
int new_len = (int)STRLEN(saved_line);
@@ -1787,10 +1838,10 @@ int open_line(int dir, int flags, int second_line_indent, bool *did_do_comment)
// stuff onto the replace stack (via ins_char()).
if (State & VREPLACE_FLAG) {
// Put new line in p_extra
- p_extra = vim_strsave(get_cursor_line_ptr());
+ p_extra = (char *)vim_strsave(get_cursor_line_ptr());
// Put back original line
- ml_replace(curwin->w_cursor.lnum, (char *)next_line, false);
+ ml_replace(curwin->w_cursor.lnum, next_line, false);
// Insert new stuff into line again
curwin->w_cursor.col = 0;
@@ -1814,16 +1865,16 @@ theend:
/// If "fixpos" is true fix the cursor position when done.
void truncate_line(int fixpos)
{
- char_u *newp;
+ char *newp;
linenr_T lnum = curwin->w_cursor.lnum;
colnr_T col = curwin->w_cursor.col;
if (col == 0) {
- newp = vim_strsave((char_u *)"");
+ newp = xstrdup("");
} else {
- newp = vim_strnsave(ml_get(lnum), (size_t)col);
+ newp = (char *)vim_strnsave(ml_get(lnum), (size_t)col);
}
- ml_replace(lnum, (char *)newp, false);
+ ml_replace(lnum, newp, false);
// mark the buffer as changed and prepare for displaying
changed_bytes(lnum, curwin->w_cursor.col);
@@ -1900,7 +1951,7 @@ int get_leader_len(char *line, char **flags, bool backward, bool include_space)
while (line[i] != NUL) {
// scan through the 'comments' option for a match
int found_one = false;
- for (list = (char *)curbuf->b_p_com; *list;) {
+ for (list = curbuf->b_p_com; *list;) {
// Get one option part into part_buf[]. Advance "list" to next
// one. Put "string" at start of string.
if (!got_com && flags != NULL) {
@@ -2037,7 +2088,7 @@ int get_last_leader_offset(char *line, char **flags)
while (--i >= lower_check_bound) {
// scan through the 'comments' option for a match
int found_one = false;
- for (list = (char *)curbuf->b_p_com; *list;) {
+ for (list = curbuf->b_p_com; *list;) {
char *flags_save = list;
// Get one option part into part_buf[]. Advance list to next one.
@@ -2122,7 +2173,7 @@ int get_last_leader_offset(char *line, char **flags)
}
len1 = (int)STRLEN(com_leader);
- for (list = (char *)curbuf->b_p_com; *list;) {
+ for (list = curbuf->b_p_com; *list;) {
char *flags_save = list;
(void)copy_option_part(&list, part_buf2, COM_MAX_LEN, ",");
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 20fae3a206..5910053025 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -4,14 +4,15 @@
#include "nvim/api/private/converter.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/ui.h"
+#include "nvim/autocmd.h"
#include "nvim/channel.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/event/socket.h"
-#include "nvim/fileio.h"
#include "nvim/lua/executor.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
+#include "nvim/os/fs.h"
#include "nvim/os/shell.h"
#ifdef WIN32
# include "nvim/os/os_win_console.h"
@@ -314,8 +315,6 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, CallbackReader
ChannelStdinMode stdin_mode, const char *cwd, uint16_t pty_width,
uint16_t pty_height, dict_T *env, varnumber_T *status_out)
{
- assert(cwd == NULL || os_isdir_executable(cwd));
-
Channel *chan = channel_alloc(kChannelStreamProc);
chan->on_data = on_stdout;
chan->on_stderr = on_stderr;
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index a26a7f6aaf..4efd8a4ad1 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -126,7 +126,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
// Init word char flags all to false
- memset(buf->b_chartab, 0, (size_t)32);
+ CLEAR_FIELD(buf->b_chartab);
// In lisp mode the '-' character is included in keywords.
if (buf->b_p_lisp) {
@@ -140,16 +140,16 @@ int buf_init_chartab(buf_T *buf, int global)
const char_u *p;
if (i == 0) {
// first round: 'isident'
- p = p_isi;
+ p = (char_u *)p_isi;
} else if (i == 1) {
// second round: 'isprint'
- p = p_isp;
+ p = (char_u *)p_isp;
} else if (i == 2) {
// third round: 'isfname'
- p = p_isf;
+ p = (char_u *)p_isf;
} else { // i == 3
// fourth round: 'iskeyword'
- p = buf->b_p_isk;
+ p = (char_u *)buf->b_p_isk;
}
while (*p) {
@@ -246,7 +246,7 @@ int buf_init_chartab(buf_T *buf, int global)
}
c = *p;
- p = skip_to_option_part(p);
+ p = (char_u *)skip_to_option_part((char *)p);
if ((c == ',') && (*p == NUL)) {
// Trailing comma is not allowed.
@@ -313,7 +313,7 @@ size_t transstr_len(const char *const s, bool untab)
const size_t l = (size_t)utfc_ptr2len(p);
if (l > 1) {
int pcc[MAX_MCO + 1];
- pcc[0] = utfc_ptr2char((const char_u *)p, &pcc[1]);
+ pcc[0] = utfc_ptr2char(p, &pcc[1]);
if (vim_isprintc(pcc[0])) {
len += l;
@@ -359,7 +359,7 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool
break; // Exceeded `buf` size.
}
int pcc[MAX_MCO + 1];
- pcc[0] = utfc_ptr2char((const char_u *)p, &pcc[1]);
+ pcc[0] = utfc_ptr2char(p, &pcc[1]);
if (vim_isprintc(pcc[0])) {
memmove(buf_p, p, l);
@@ -709,7 +709,7 @@ int ptr2cells(const char *p_in)
/// @return number of character cells.
int vim_strsize(char *s)
{
- return vim_strnsize((char_u *)s, MAXCOL);
+ return vim_strnsize(s, MAXCOL);
}
/// Return the number of character cells string "s[len]" will take on the
@@ -721,13 +721,13 @@ int vim_strsize(char *s)
/// @param len
///
/// @return Number of character cells.
-int vim_strnsize(char_u *s, int len)
+int vim_strnsize(char *s, int len)
{
assert(s != NULL);
int size = 0;
while (*s != NUL && --len >= 0) {
- int l = utfc_ptr2len((char *)s);
- size += ptr2cells((char *)s);
+ int l = utfc_ptr2len(s);
+ size += ptr2cells(s);
s += l;
len -= l - 1;
}
@@ -930,14 +930,18 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
posptr -= utf_head_off(line, posptr);
}
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line);
+
// This function is used very often, do some speed optimizations.
// When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set
- // use a simple loop.
+ // and there are no virtual text use a simple loop.
// Also use this when 'list' is set but tabs take their normal size.
if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL))
&& !wp->w_p_lbr
&& *get_showbreak_value(wp) == NUL
- && !wp->w_p_bri) {
+ && !wp->w_p_bri
+ && !cts.cts_has_virt_text) {
for (;;) {
head = 0;
int c = *ptr;
@@ -984,25 +988,29 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
} else {
for (;;) {
// A tab gets expanded, depending on the current column
+ // Other things also take up space.
head = 0;
- incr = win_lbr_chartabsize(wp, line, ptr, vcol, &head);
+ incr = win_lbr_chartabsize(&cts, &head);
// make sure we don't go past the end of the line
- if (*ptr == NUL) {
+ if (*cts.cts_ptr == NUL) {
// NUL at end of line only takes one column
incr = 1;
break;
}
- if ((posptr != NULL) && (ptr >= posptr)) {
+ if ((posptr != NULL) && ((char_u *)cts.cts_ptr >= posptr)) {
// character at pos->col
break;
}
- vcol += incr;
- MB_PTR_ADV(ptr);
+ cts.cts_vcol += incr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ vcol = cts.cts_vcol;
+ ptr = (char_u *)cts.cts_ptr;
}
+ clear_chartabsize_arg(&cts);
if (start != NULL) {
*start = vcol + head;
@@ -1013,6 +1021,8 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
}
if (cursor != NULL) {
+ // cursor is after inserted text
+ vcol += cts.cts_cur_text_width;
if ((*ptr == TAB)
&& (State & MODE_NORMAL)
&& !wp->w_p_list
@@ -1147,7 +1157,7 @@ char *skipwhite(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- return (char *)skipwhite_len((char_u *)p, STRLEN(p));
+ return skipwhite_len(p, STRLEN(p));
}
/// Like `skipwhite`, but skip up to `len` characters.
@@ -1158,27 +1168,27 @@ char *skipwhite(const char *const p)
///
/// @return Pointer to character after the skipped whitespace, or the `len`-th
/// character in the string.
-char_u *skipwhite_len(const char_u *p, size_t len)
+char *skipwhite_len(const char *p, size_t len)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
for (; len > 0 && ascii_iswhite(*p); len--) {
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
// getwhitecols: return the number of whitespace
// columns (bytes) at the start of a given line
intptr_t getwhitecols_curline(void)
{
- return getwhitecols(get_cursor_line_ptr());
+ return getwhitecols((char *)get_cursor_line_ptr());
}
-intptr_t getwhitecols(const char_u *p)
+intptr_t getwhitecols(const char *p)
FUNC_ATTR_PURE
{
- return (char_u *)skipwhite((char *)p) - p;
+ return skipwhite(p) - p;
}
/// Skip over digits
@@ -1222,10 +1232,10 @@ const char *skipbin(const char *q)
///
/// @return Pointer to the character after the skipped digits and hex
/// characters.
-char_u *skiphex(char_u *q)
+char *skiphex(char *q)
FUNC_ATTR_PURE
{
- char_u *p = q;
+ char *p = q;
while (ascii_isxdigit(*p)) {
// skip to next non-digit
p++;
@@ -1238,10 +1248,10 @@ char_u *skiphex(char_u *q)
/// @param q
///
/// @return Pointer to the digit or (NUL after the string).
-char_u *skiptodigit(char_u *q)
+char *skiptodigit(char *q)
FUNC_ATTR_PURE
{
- char_u *p = q;
+ char *p = q;
while (*p != NUL && !ascii_isdigit(*p)) {
// skip to next digit
p++;
@@ -1272,10 +1282,10 @@ const char *skiptobin(const char *q)
/// @param q
///
/// @return Pointer to the hex character or (NUL after the string).
-char_u *skiptohex(char_u *q)
+char *skiptohex(char *q)
FUNC_ATTR_PURE
{
- char_u *p = q;
+ char *p = q;
while (*p != NUL && !ascii_isxdigit(*p)) {
// skip to next digit
p++;
@@ -1288,13 +1298,13 @@ char_u *skiptohex(char_u *q)
/// @param[in] p Text to skip over.
///
/// @return Pointer to the next whitespace or NUL character.
-char_u *skiptowhite(const char_u *p)
+char *skiptowhite(const char *p)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
while (*p != ' ' && *p != '\t' && *p != NUL) {
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
/// skiptowhite_esc: Like skiptowhite(), but also skip escaped chars
@@ -1319,11 +1329,11 @@ char *skiptowhite_esc(char *p)
/// @param[in] p Text to skip over.
///
/// @return Pointer to the next '\n' or NUL character.
-char_u *skip_to_newline(const char_u *const p)
+char *skip_to_newline(const char *const p)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
FUNC_ATTR_NONNULL_RET
{
- return (char_u *)xstrchrnul((const char *)p, NL);
+ return xstrchrnul(p, NL);
}
/// Gets a number from a string and skips over it, signalling overflow.
@@ -1412,10 +1422,10 @@ int32_t getdigits_int32(char **pp, bool strict, long def)
/// Check that "lbuf" is empty or only contains blanks.
///
/// @param lbuf line buffer to check
-bool vim_isblankline(char_u *lbuf)
+bool vim_isblankline(char *lbuf)
FUNC_ATTR_PURE
{
- char_u *p = (char_u *)skipwhite((char *)lbuf);
+ char *p = skipwhite(lbuf);
return *p == NUL || *p == '\r' || *p == '\n';
}
@@ -1654,8 +1664,9 @@ int hex2nr(int c)
}
/// Convert two hex characters to a byte.
-/// Return -1 if one of the characters is not hex.
-int hexhex2nr(char_u *p)
+///
+/// @return -1 if one of the characters is not hex.
+int hexhex2nr(const char *p)
FUNC_ATTR_PURE
{
if (!ascii_isxdigit(p[0]) || !ascii_isxdigit(p[1])) {
@@ -1677,12 +1688,12 @@ int hexhex2nr(char_u *p)
/// characters.
///
/// @param str file path string to check
-bool rem_backslash(const char_u *str)
+bool rem_backslash(const char *str)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
#ifdef BACKSLASH_IN_FILENAME
return str[0] == '\\'
- && str[1] < 0x80
+ && (uint8_t)str[1] < 0x80
&& (str[1] == ' '
|| (str[1] != NUL
&& str[1] != '*'
@@ -1697,7 +1708,7 @@ bool rem_backslash(const char_u *str)
/// Halve the number of backslashes in a file name argument.
///
/// @param p
-void backslash_halve(char_u *p)
+void backslash_halve(char *p)
{
for (; *p; p++) {
if (rem_backslash(p)) {
@@ -1711,11 +1722,11 @@ void backslash_halve(char_u *p)
/// @param p
///
/// @return String with the number of backslashes halved.
-char_u *backslash_halve_save(const char_u *p)
+char *backslash_halve_save(const char *p)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
// TODO(philix): simplify and improve backslash_halve_save algorithm
- char_u *res = vim_strsave(p);
+ char *res = xstrdup(p);
backslash_halve(res);
return res;
}
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
new file mode 100644
index 0000000000..5c54404aab
--- /dev/null
+++ b/src/nvim/cmdexpand.c
@@ -0,0 +1,2908 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// cmdexpand.c: functions for command-line completion
+
+#include "nvim/api/private/helpers.h"
+#include "nvim/arglist.h"
+#include "nvim/ascii.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdhist.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_getln.h"
+#include "nvim/garray.h"
+#include "nvim/getchar.h"
+#include "nvim/help.h"
+#include "nvim/highlight_group.h"
+#include "nvim/if_cscope.h"
+#include "nvim/locale.h"
+#include "nvim/lua/executor.h"
+#include "nvim/mapping.h"
+#include "nvim/menu.h"
+#include "nvim/option.h"
+#include "nvim/os/os.h"
+#include "nvim/popupmenu.h"
+#include "nvim/profile.h"
+#include "nvim/regexp.h"
+#include "nvim/screen.h"
+#include "nvim/search.h"
+#include "nvim/sign.h"
+#include "nvim/strings.h"
+#include "nvim/syntax.h"
+#include "nvim/tag.h"
+#include "nvim/types.h"
+#include "nvim/ui.h"
+#include "nvim/usercmd.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+/// Type used by ExpandGeneric()
+typedef char *(*CompleteListItemGetter)(expand_T *, int);
+
+/// Type used by call_user_expand_func
+typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *);
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "cmdexpand.c.generated.h"
+#endif
+
+static int cmd_showtail; ///< Only show path tail in lists ?
+
+/// "compl_match_array" points the currently displayed list of entries in the
+/// popup menu. It is NULL when there is no popup menu.
+static pumitem_T *compl_match_array = NULL;
+static int compl_match_arraysize;
+/// First column in cmdline of the matched item for completion.
+static int compl_startcol;
+static int compl_selected;
+
+static int sort_func_compare(const void *s1, const void *s2)
+{
+ char_u *p1 = *(char_u **)s1;
+ char_u *p2 = *(char_u **)s2;
+
+ if (*p1 != '<' && *p2 == '<') {
+ return -1;
+ }
+ if (*p1 == '<' && *p2 != '<') {
+ return 1;
+ }
+ return STRCMP(p1, p2);
+}
+
+static void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char **files, int options)
+{
+ int i;
+ char_u *p;
+ const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE;
+
+ // May change home directory back to "~"
+ if (options & WILD_HOME_REPLACE) {
+ tilde_replace(str, numfiles, files);
+ }
+
+ if (options & WILD_ESCAPE) {
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_FILES_IN_PATH
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS
+ || xp->xp_context == EXPAND_DIRECTORIES) {
+ // Insert a backslash into a file name before a space, \, %, #
+ // and wildmatch characters, except '~'.
+ for (i = 0; i < numfiles; i++) {
+ // for ":set path=" we need to escape spaces twice
+ if (xp->xp_backslash == XP_BS_THREE) {
+ p = vim_strsave_escaped((char_u *)files[i], (char_u *)" ");
+ xfree(files[i]);
+ files[i] = (char *)p;
+#if defined(BACKSLASH_IN_FILENAME)
+ p = vim_strsave_escaped(files[i], (char_u *)" ");
+ xfree(files[i]);
+ files[i] = p;
+#endif
+ }
+#ifdef BACKSLASH_IN_FILENAME
+ p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what);
+#else
+ p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
+ xp->xp_shell ? VSE_SHELL : vse_what);
+#endif
+ xfree(files[i]);
+ files[i] = (char *)p;
+
+ // If 'str' starts with "\~", replace "~" at start of
+ // files[i] with "\~".
+ if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') {
+ escape_fname(&files[i]);
+ }
+ }
+ xp->xp_backslash = XP_BS_NONE;
+
+ // If the first file starts with a '+' escape it. Otherwise it
+ // could be seen as "+cmd".
+ if (*files[0] == '+') {
+ escape_fname(&files[0]);
+ }
+ } else if (xp->xp_context == EXPAND_TAGS) {
+ // Insert a backslash before characters in a tag name that
+ // would terminate the ":tag" command.
+ for (i = 0; i < numfiles; i++) {
+ p = vim_strsave_escaped((char_u *)files[i], (char_u *)"\\|\"");
+ xfree(files[i]);
+ files[i] = (char *)p;
+ }
+ }
+ }
+}
+
+/// Return FAIL if this is not an appropriate context in which to do
+/// completion of anything, return OK if it is (even if there are no matches).
+/// For the caller, this means that the character is just passed through like a
+/// normal character (instead of being expanded). This allows :s/^I^D etc.
+///
+/// @param options extra options for ExpandOne()
+/// @param escape if true, escape the returned matches
+int nextwild(expand_T *xp, int type, int options, bool escape)
+{
+ CmdlineInfo *const ccline = get_cmdline_info();
+ int i, j;
+ char_u *p1;
+ char_u *p2;
+ int difflen;
+
+ if (xp->xp_numfiles == -1) {
+ set_expand_context(xp);
+ cmd_showtail = expand_showtail(xp);
+ }
+
+ if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
+ beep_flush();
+ return OK; // Something illegal on command line
+ }
+ if (xp->xp_context == EXPAND_NOTHING) {
+ // Caller can use the character as a normal char instead
+ return FAIL;
+ }
+
+ if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
+ msg_puts("..."); // show that we are busy
+ ui_flush();
+ }
+
+ i = (int)((char_u *)xp->xp_pattern - ccline->cmdbuff);
+ assert(ccline->cmdpos >= i);
+ xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i;
+
+ if (type == WILD_NEXT || type == WILD_PREV) {
+ // Get next/previous match for a previous expanded pattern.
+ p2 = ExpandOne(xp, NULL, NULL, 0, type);
+ } else {
+ // Translate string into pattern and expand it.
+ p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+ const int use_options = (options
+ | WILD_HOME_REPLACE
+ | WILD_ADD_SLASH
+ | WILD_SILENT
+ | (escape ? WILD_ESCAPE : 0)
+ | (p_wic ? WILD_ICASE : 0));
+ p2 = ExpandOne(xp, p1, vim_strnsave(&ccline->cmdbuff[i], xp->xp_pattern_len),
+ use_options, type);
+ xfree(p1);
+
+ // xp->xp_pattern might have been modified by ExpandOne (for example,
+ // in lua completion), so recompute the pattern index and length
+ i = (int)((char_u *)xp->xp_pattern - ccline->cmdbuff);
+ xp->xp_pattern_len = (size_t)ccline->cmdpos - (size_t)i;
+
+ // Longest match: make sure it is not shorter, happens with :help.
+ if (p2 != NULL && type == WILD_LONGEST) {
+ for (j = 0; (size_t)j < xp->xp_pattern_len; j++) {
+ if (ccline->cmdbuff[i + j] == '*'
+ || ccline->cmdbuff[i + j] == '?') {
+ break;
+ }
+ }
+ if ((int)STRLEN(p2) < j) {
+ XFREE_CLEAR(p2);
+ }
+ }
+ }
+
+ if (p2 != NULL && !got_int) {
+ difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len);
+ if (ccline->cmdlen + difflen + 4 > ccline->cmdbufflen) {
+ realloc_cmdbuff(ccline->cmdlen + difflen + 4);
+ xp->xp_pattern = (char *)ccline->cmdbuff + i;
+ }
+ assert(ccline->cmdpos <= ccline->cmdlen);
+ memmove(&ccline->cmdbuff[ccline->cmdpos + difflen],
+ &ccline->cmdbuff[ccline->cmdpos],
+ (size_t)ccline->cmdlen - (size_t)ccline->cmdpos + 1);
+ memmove(&ccline->cmdbuff[i], p2, STRLEN(p2));
+ ccline->cmdlen += difflen;
+ ccline->cmdpos += difflen;
+ }
+ xfree(p2);
+
+ redrawcmd();
+ cursorcmd();
+
+ // When expanding a ":map" command and no matches are found, assume that
+ // the key is supposed to be inserted literally
+ if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL) {
+ return FAIL;
+ }
+
+ if (xp->xp_numfiles <= 0 && p2 == NULL) {
+ beep_flush();
+ } else if (xp->xp_numfiles == 1) {
+ // free expanded pattern
+ (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
+ }
+
+ return OK;
+}
+
+void cmdline_pum_display(bool changed_array)
+{
+ pum_display(compl_match_array, compl_match_arraysize, compl_selected,
+ changed_array, compl_startcol);
+}
+
+bool cmdline_pum_active(void)
+{
+ // compl_match_array != NULL should already imply pum_visible() in Nvim.
+ return compl_match_array != NULL;
+}
+
+/// Remove the cmdline completion popup menu
+void cmdline_pum_remove(void)
+{
+ pum_undisplay(true);
+ XFREE_CLEAR(compl_match_array);
+}
+
+void cmdline_pum_cleanup(CmdlineInfo *cclp)
+{
+ cmdline_pum_remove();
+ wildmenu_cleanup(cclp);
+}
+
+/// Get the next or prev cmdline completion match. The index of the match is set
+/// in "p_findex"
+static char_u *get_next_or_prev_match(int mode, expand_T *xp, int *p_findex, char_u *orig_save)
+{
+ if (xp->xp_numfiles <= 0) {
+ return NULL;
+ }
+
+ int findex = *p_findex;
+
+ if (mode == WILD_PREV) {
+ if (findex == -1) {
+ findex = xp->xp_numfiles;
+ }
+ findex--;
+ } else { // mode == WILD_NEXT
+ findex++;
+ }
+
+ // When wrapping around, return the original string, set findex to -1.
+ if (findex < 0) {
+ if (orig_save == NULL) {
+ findex = xp->xp_numfiles - 1;
+ } else {
+ findex = -1;
+ }
+ }
+ if (findex >= xp->xp_numfiles) {
+ if (orig_save == NULL) {
+ findex = 0;
+ } else {
+ findex = -1;
+ }
+ }
+ if (compl_match_array) {
+ compl_selected = findex;
+ cmdline_pum_display(false);
+ } else if (p_wmnu) {
+ redraw_wildmenu(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail);
+ }
+ *p_findex = findex;
+
+ return vim_strsave(findex == -1 ? orig_save : (char_u *)xp->xp_files[findex]);
+}
+
+/// Start the command-line expansion and get the matches.
+static char_u *ExpandOne_start(int mode, expand_T *xp, char_u *str, int options)
+{
+ int non_suf_match; // number without matching suffix
+ char_u *ss = NULL;
+
+ // Do the expansion.
+ if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) {
+#ifdef FNAME_ILLEGAL
+ // Illegal file name has been silently skipped. But when there
+ // are wildcards, the real problem is that there was no match,
+ // causing the pattern to be added, which has illegal characters.
+ if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) {
+ semsg(_(e_nomatch2), str);
+ }
+#endif
+ } else if (xp->xp_numfiles == 0) {
+ if (!(options & WILD_SILENT)) {
+ semsg(_(e_nomatch2), str);
+ }
+ } else {
+ // Escape the matches for use on the command line.
+ ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
+
+ // Check for matching suffixes in file names.
+ if (mode != WILD_ALL && mode != WILD_ALL_KEEP
+ && mode != WILD_LONGEST) {
+ if (xp->xp_numfiles) {
+ non_suf_match = xp->xp_numfiles;
+ } else {
+ non_suf_match = 1;
+ }
+ if ((xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_DIRECTORIES)
+ && xp->xp_numfiles > 1) {
+ // More than one match; check suffix.
+ // The files will have been sorted on matching suffix in
+ // expand_wildcards, only need to check the first two.
+ non_suf_match = 0;
+ for (int i = 0; i < 2; i++) {
+ if (match_suffix((char_u *)xp->xp_files[i])) {
+ non_suf_match++;
+ }
+ }
+ }
+ if (non_suf_match != 1) {
+ // Can we ever get here unless it's while expanding
+ // interactively? If not, we can get rid of this all
+ // together. Don't really want to wait for this message
+ // (and possibly have to hit return to continue!).
+ if (!(options & WILD_SILENT)) {
+ emsg(_(e_toomany));
+ } else if (!(options & WILD_NO_BEEP)) {
+ beep_flush();
+ }
+ }
+ if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
+ ss = vim_strsave((char_u *)xp->xp_files[0]);
+ }
+ }
+ }
+
+ return ss;
+}
+
+/// Return the longest common part in the list of cmdline completion matches.
+static char *find_longest_match(expand_T *xp, int options)
+{
+ size_t len = 0;
+
+ for (size_t mb_len; xp->xp_files[0][len]; len += mb_len) {
+ mb_len = (size_t)utfc_ptr2len(&xp->xp_files[0][len]);
+ int c0 = utf_ptr2char(&xp->xp_files[0][len]);
+ int i;
+ for (i = 1; i < xp->xp_numfiles; i++) {
+ int ci = utf_ptr2char(&xp->xp_files[i][len]);
+
+ if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
+ || xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS)) {
+ if (mb_tolower(c0) != mb_tolower(ci)) {
+ break;
+ }
+ } else if (c0 != ci) {
+ break;
+ }
+ }
+ if (i < xp->xp_numfiles) {
+ if (!(options & WILD_NO_BEEP)) {
+ vim_beep(BO_WILD);
+ }
+ break;
+ }
+ }
+
+ return xstrndup(xp->xp_files[0], len);
+}
+
+/// Do wildcard expansion on the string 'str'.
+/// Chars that should not be expanded must be preceded with a backslash.
+/// Return a pointer to allocated memory containing the new string.
+/// Return NULL for failure.
+///
+/// "orig" is the originally expanded string, copied to allocated memory. It
+/// should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
+/// WILD_PREV "orig" should be NULL.
+///
+/// Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
+/// is WILD_EXPAND_FREE or WILD_ALL.
+///
+/// mode = WILD_FREE: just free previously expanded matches
+/// mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
+/// mode = WILD_EXPAND_KEEP: normal expansion, keep matches
+/// mode = WILD_NEXT: use next match in multiple match, wrap to first
+/// mode = WILD_PREV: use previous match in multiple match, wrap to first
+/// mode = WILD_ALL: return all matches concatenated
+/// mode = WILD_LONGEST: return longest matched part
+/// mode = WILD_ALL_KEEP: get all matches, keep matches
+/// mode = WILD_APPLY: apply the item selected in the cmdline completion
+/// popup menu and close the menu.
+/// mode = WILD_CANCEL: cancel and close the cmdline completion popup and
+/// use the original text.
+///
+/// options = WILD_LIST_NOTFOUND: list entries without a match
+/// options = WILD_HOME_REPLACE: do home_replace() for buffer names
+/// options = WILD_USE_NL: Use '\n' for WILD_ALL
+/// options = WILD_NO_BEEP: Don't beep for multiple matches
+/// options = WILD_ADD_SLASH: add a slash after directory names
+/// options = WILD_KEEP_ALL: don't remove 'wildignore' entries
+/// options = WILD_SILENT: don't print warning messages
+/// options = WILD_ESCAPE: put backslash before special chars
+/// options = WILD_ICASE: ignore case for files
+///
+/// The variables xp->xp_context and xp->xp_backslash must have been set!
+///
+/// @param orig allocated copy of original of expanded string
+char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode)
+{
+ char_u *ss = NULL;
+ static int findex;
+ static char_u *orig_save = NULL; // kept value of orig
+ int orig_saved = false;
+ int i;
+
+ // first handle the case of using an old match
+ if (mode == WILD_NEXT || mode == WILD_PREV) {
+ return get_next_or_prev_match(mode, xp, &findex, orig_save);
+ }
+
+ if (mode == WILD_CANCEL) {
+ ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
+ } else if (mode == WILD_APPLY) {
+ ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"")
+ : (char_u *)xp->xp_files[findex]);
+ }
+
+ // free old names
+ if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) {
+ FreeWild(xp->xp_numfiles, xp->xp_files);
+ xp->xp_numfiles = -1;
+ XFREE_CLEAR(orig_save);
+ }
+ findex = 0;
+
+ if (mode == WILD_FREE) { // only release file name
+ return NULL;
+ }
+
+ if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL) {
+ xfree(orig_save);
+ orig_save = orig;
+ orig_saved = true;
+
+ ss = ExpandOne_start(mode, xp, str, options);
+ }
+
+ // Find longest common part
+ if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
+ ss = (char_u *)find_longest_match(xp, options);
+ findex = -1; // next p_wc gets first one
+ }
+
+ // Concatenate all matching names. Unless interrupted, this can be slow
+ // and the result probably won't be used.
+ // TODO(philix): use xstpcpy instead of strcat in a loop (ExpandOne)
+ if (mode == WILD_ALL && xp->xp_numfiles > 0 && !got_int) {
+ size_t len = 0;
+ for (i = 0; i < xp->xp_numfiles; i++) {
+ len += STRLEN(xp->xp_files[i]) + 1;
+ }
+ ss = xmalloc(len);
+ *ss = NUL;
+ for (i = 0; i < xp->xp_numfiles; i++) {
+ STRCAT(ss, xp->xp_files[i]);
+ if (i != xp->xp_numfiles - 1) {
+ STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
+ }
+ }
+ }
+
+ if (mode == WILD_EXPAND_FREE || mode == WILD_ALL) {
+ ExpandCleanup(xp);
+ }
+
+ // Free "orig" if it wasn't stored in "orig_save".
+ if (!orig_saved) {
+ xfree(orig);
+ }
+
+ return ss;
+}
+
+/// Prepare an expand structure for use.
+void ExpandInit(expand_T *xp)
+ FUNC_ATTR_NONNULL_ALL
+{
+ CLEAR_POINTER(xp);
+ xp->xp_backslash = XP_BS_NONE;
+ xp->xp_numfiles = -1;
+}
+
+/// Cleanup an expand structure after use.
+void ExpandCleanup(expand_T *xp)
+{
+ if (xp->xp_numfiles >= 0) {
+ FreeWild(xp->xp_numfiles, xp->xp_files);
+ xp->xp_numfiles = -1;
+ }
+}
+
+/// Show all matches for completion on the command line.
+/// Returns EXPAND_NOTHING when the character that triggered expansion should
+/// be inserted like a normal character.
+int showmatches(expand_T *xp, int wildmenu)
+{
+ CmdlineInfo *const ccline = get_cmdline_info();
+#define L_SHOWFILE(m) (showtail \
+ ? sm_gettail(files_found[m], false) : files_found[m])
+ int num_files;
+ char **files_found;
+ int i, j, k;
+ int maxlen;
+ int lines;
+ int columns;
+ char_u *p;
+ int lastlen;
+ int attr;
+ int showtail;
+
+ if (xp->xp_numfiles == -1) {
+ set_expand_context(xp);
+ i = expand_cmdline(xp, ccline->cmdbuff, ccline->cmdpos,
+ &num_files, &files_found);
+ showtail = expand_showtail(xp);
+ if (i != EXPAND_OK) {
+ return i;
+ }
+ } else {
+ num_files = xp->xp_numfiles;
+ files_found = xp->xp_files;
+ showtail = cmd_showtail;
+ }
+
+ bool compl_use_pum = (ui_has(kUICmdline)
+ ? ui_has(kUIPopupmenu)
+ : wildmenu && (wop_flags & WOP_PUM))
+ || ui_has(kUIWildmenu);
+
+ if (compl_use_pum) {
+ assert(num_files >= 0);
+ compl_match_arraysize = num_files;
+ compl_match_array = xmalloc(sizeof(pumitem_T) * (size_t)compl_match_arraysize);
+ for (i = 0; i < num_files; i++) {
+ compl_match_array[i] = (pumitem_T){
+ .pum_text = (char_u *)L_SHOWFILE(i),
+ .pum_info = NULL,
+ .pum_extra = NULL,
+ .pum_kind = NULL,
+ };
+ }
+ char_u *endpos = (char_u *)(showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
+ if (ui_has(kUICmdline)) {
+ compl_startcol = (int)(endpos - ccline->cmdbuff);
+ } else {
+ compl_startcol = cmd_screencol((int)(endpos - ccline->cmdbuff));
+ }
+ compl_selected = -1;
+ cmdline_pum_display(true);
+ return EXPAND_OK;
+ }
+
+ if (!wildmenu) {
+ msg_didany = false; // lines_left will be set
+ msg_start(); // prepare for paging
+ msg_putchar('\n');
+ ui_flush();
+ cmdline_row = msg_row;
+ msg_didany = false; // lines_left will be set again
+ msg_start(); // prepare for paging
+ }
+
+ if (got_int) {
+ got_int = false; // only int. the completion, not the cmd line
+ } else if (wildmenu) {
+ redraw_wildmenu(xp, num_files, files_found, -1, showtail);
+ } else {
+ // find the length of the longest file name
+ maxlen = 0;
+ for (i = 0; i < num_files; i++) {
+ if (!showtail && (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS)) {
+ home_replace(NULL, files_found[i], (char *)NameBuff, MAXPATHL, true);
+ j = vim_strsize((char *)NameBuff);
+ } else {
+ j = vim_strsize(L_SHOWFILE(i));
+ }
+ if (j > maxlen) {
+ maxlen = j;
+ }
+ }
+
+ if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
+ lines = num_files;
+ } else {
+ // compute the number of columns and lines for the listing
+ maxlen += 2; // two spaces between file names
+ columns = (Columns + 2) / maxlen;
+ if (columns < 1) {
+ columns = 1;
+ }
+ lines = (num_files + columns - 1) / columns;
+ }
+
+ attr = HL_ATTR(HLF_D); // find out highlighting for directories
+
+ if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
+ msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
+ msg_clr_eos();
+ msg_advance(maxlen - 3);
+ msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
+ }
+
+ // list the files line by line
+ for (i = 0; i < lines; i++) {
+ lastlen = 999;
+ for (k = i; k < num_files; k += lines) {
+ if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
+ msg_outtrans_attr(files_found[k], HL_ATTR(HLF_D));
+ p = (char_u *)files_found[k] + STRLEN(files_found[k]) + 1;
+ msg_advance(maxlen + 1);
+ msg_puts((const char *)p);
+ msg_advance(maxlen + 3);
+ msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
+ break;
+ }
+ for (j = maxlen - lastlen; --j >= 0;) {
+ msg_putchar(' ');
+ }
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_SHELLCMD
+ || xp->xp_context == EXPAND_BUFFERS) {
+ // highlight directories
+ if (xp->xp_numfiles != -1) {
+ // Expansion was done before and special characters
+ // were escaped, need to halve backslashes. Also
+ // $HOME has been replaced with ~/.
+ char_u *exp_path = expand_env_save_opt((char_u *)files_found[k], true);
+ char_u *path = exp_path != NULL ? exp_path : (char_u *)files_found[k];
+ char_u *halved_slash = (char_u *)backslash_halve_save((char *)path);
+ j = os_isdir((char *)halved_slash);
+ xfree(exp_path);
+ if (halved_slash != path) {
+ xfree(halved_slash);
+ }
+ } else {
+ // Expansion was done here, file names are literal.
+ j = os_isdir(files_found[k]);
+ }
+ if (showtail) {
+ p = (char_u *)L_SHOWFILE(k);
+ } else {
+ home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true);
+ p = (char_u *)NameBuff;
+ }
+ } else {
+ j = false;
+ p = (char_u *)L_SHOWFILE(k);
+ }
+ lastlen = msg_outtrans_attr((char *)p, j ? attr : 0);
+ }
+ if (msg_col > 0) { // when not wrapped around
+ msg_clr_eos();
+ msg_putchar('\n');
+ }
+ ui_flush(); // show one line at a time
+ if (got_int) {
+ got_int = false;
+ break;
+ }
+ }
+
+ // we redraw the command below the lines that we have just listed
+ // This is a bit tricky, but it saves a lot of screen updating.
+ cmdline_row = msg_row; // will put it back later
+ }
+
+ if (xp->xp_numfiles == -1) {
+ FreeWild(num_files, files_found);
+ }
+
+ return EXPAND_OK;
+}
+
+/// Private path_tail for showmatches() (and redraw_wildmenu()):
+/// Find tail of file name path, but ignore trailing "/".
+char *sm_gettail(char *s, bool eager)
+{
+ char_u *p;
+ char_u *t = (char_u *)s;
+ bool had_sep = false;
+
+ for (p = (char_u *)s; *p != NUL;) {
+ if (vim_ispathsep(*p)
+#ifdef BACKSLASH_IN_FILENAME
+ && !rem_backslash(p)
+#endif
+ ) {
+ if (eager) {
+ t = p + 1;
+ } else {
+ had_sep = true;
+ }
+ } else if (had_sep) {
+ t = p;
+ had_sep = false;
+ }
+ MB_PTR_ADV(p);
+ }
+ return (char *)t;
+}
+
+/// Return true if we only need to show the tail of completion matches.
+/// When not completing file names or there is a wildcard in the path false is
+/// returned.
+static bool expand_showtail(expand_T *xp)
+{
+ char_u *s;
+ char_u *end;
+
+ // When not completing file names a "/" may mean something different.
+ if (xp->xp_context != EXPAND_FILES
+ && xp->xp_context != EXPAND_SHELLCMD
+ && xp->xp_context != EXPAND_DIRECTORIES) {
+ return false;
+ }
+
+ end = (char_u *)path_tail(xp->xp_pattern);
+ if (end == (char_u *)xp->xp_pattern) { // there is no path separator
+ return false;
+ }
+
+ for (s = (char_u *)xp->xp_pattern; s < end; s++) {
+ // Skip escaped wildcards. Only when the backslash is not a path
+ // separator, on DOS the '*' "path\*\file" must not be skipped.
+ if (rem_backslash((char *)s)) {
+ s++;
+ } else if (vim_strchr("*?[", *s) != NULL) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/// Prepare a string for expansion.
+///
+/// When expanding file names: The string will be used with expand_wildcards().
+/// Copy "fname[len]" into allocated memory and add a '*' at the end.
+/// When expanding other names: The string will be used with regcomp(). Copy
+/// the name into allocated memory and prepend "^".
+///
+/// @param context EXPAND_FILES etc.
+char_u *addstar(char_u *fname, size_t len, int context)
+ FUNC_ATTR_NONNULL_RET
+{
+ char_u *retval;
+ size_t i, j;
+ size_t new_len;
+ char_u *tail;
+ int ends_in_star;
+
+ if (context != EXPAND_FILES
+ && context != EXPAND_FILES_IN_PATH
+ && context != EXPAND_SHELLCMD
+ && context != EXPAND_DIRECTORIES) {
+ // Matching will be done internally (on something other than files).
+ // So we convert the file-matching-type wildcards into our kind for
+ // use with vim_regcomp(). First work out how long it will be:
+
+ // For help tags the translation is done in find_help_tags().
+ // For a tag pattern starting with "/" no translation is needed.
+ if (context == EXPAND_HELP
+ || context == EXPAND_CHECKHEALTH
+ || context == EXPAND_COLORS
+ || context == EXPAND_COMPILER
+ || context == EXPAND_OWNSYNTAX
+ || context == EXPAND_FILETYPE
+ || context == EXPAND_PACKADD
+ || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS)
+ && fname[0] == '/')) {
+ retval = vim_strnsave(fname, len);
+ } else {
+ new_len = len + 2; // +2 for '^' at start, NUL at end
+ for (i = 0; i < len; i++) {
+ if (fname[i] == '*' || fname[i] == '~') {
+ new_len++; // '*' needs to be replaced by ".*"
+ // '~' needs to be replaced by "\~"
+ }
+ // Buffer names are like file names. "." should be literal
+ if (context == EXPAND_BUFFERS && fname[i] == '.') {
+ new_len++; // "." becomes "\."
+ }
+ // Custom expansion takes care of special things, match
+ // backslashes literally (perhaps also for other types?)
+ if ((context == EXPAND_USER_DEFINED
+ || context == EXPAND_USER_LIST) && fname[i] == '\\') {
+ new_len++; // '\' becomes "\\"
+ }
+ }
+ retval = xmalloc(new_len);
+ {
+ retval[0] = '^';
+ j = 1;
+ for (i = 0; i < len; i++, j++) {
+ // Skip backslash. But why? At least keep it for custom
+ // expansion.
+ if (context != EXPAND_USER_DEFINED
+ && context != EXPAND_USER_LIST
+ && fname[i] == '\\'
+ && ++i == len) {
+ break;
+ }
+
+ switch (fname[i]) {
+ case '*':
+ retval[j++] = '.';
+ break;
+ case '~':
+ retval[j++] = '\\';
+ break;
+ case '?':
+ retval[j] = '.';
+ continue;
+ case '.':
+ if (context == EXPAND_BUFFERS) {
+ retval[j++] = '\\';
+ }
+ break;
+ case '\\':
+ if (context == EXPAND_USER_DEFINED
+ || context == EXPAND_USER_LIST) {
+ retval[j++] = '\\';
+ }
+ break;
+ }
+ retval[j] = fname[i];
+ }
+ retval[j] = NUL;
+ }
+ }
+ } else {
+ retval = xmalloc(len + 4);
+ STRLCPY(retval, fname, len + 1);
+
+ // Don't add a star to *, ~, ~user, $var or `cmd`.
+ // * would become **, which walks the whole tree.
+ // ~ would be at the start of the file name, but not the tail.
+ // $ could be anywhere in the tail.
+ // ` could be anywhere in the file name.
+ // When the name ends in '$' don't add a star, remove the '$'.
+ tail = (char_u *)path_tail((char *)retval);
+ ends_in_star = (len > 0 && retval[len - 1] == '*');
+#ifndef BACKSLASH_IN_FILENAME
+ for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) {
+ if (retval[k] != '\\') {
+ break;
+ }
+ ends_in_star = !ends_in_star;
+ }
+#endif
+ if ((*retval != '~' || tail != retval)
+ && !ends_in_star
+ && vim_strchr((char *)tail, '$') == NULL
+ && vim_strchr((char *)retval, '`') == NULL) {
+ retval[len++] = '*';
+ } else if (len > 0 && retval[len - 1] == '$') {
+ len--;
+ }
+ retval[len] = NUL;
+ }
+ return retval;
+}
+
+/// Must parse the command line so far to work out what context we are in.
+/// Completion can then be done based on that context.
+/// This routine sets the variables:
+/// xp->xp_pattern The start of the pattern to be expanded within
+/// the command line (ends at the cursor).
+/// xp->xp_context The type of thing to expand. Will be one of:
+///
+/// EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
+/// the command line, like an unknown command. Caller
+/// should beep.
+/// EXPAND_NOTHING Unrecognised context for completion, use char like
+/// a normal char, rather than for completion. eg
+/// :s/^I/
+/// EXPAND_COMMANDS Cursor is still touching the command, so complete
+/// it.
+/// EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
+/// EXPAND_FILES After command with EX_XFILE set, or after setting
+/// with P_EXPAND set. eg :e ^I, :w>>^I
+/// EXPAND_DIRECTORIES In some cases this is used instead of the latter
+/// when we know only directories are of interest. eg
+/// :set dir=^I
+/// EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
+/// EXPAND_SETTINGS Complete variable names. eg :set d^I
+/// EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
+/// EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
+/// EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
+/// EXPAND_HELP Complete tags from the file 'helpfile'/tags
+/// EXPAND_EVENTS Complete event names
+/// EXPAND_SYNTAX Complete :syntax command arguments
+/// EXPAND_HIGHLIGHT Complete highlight (syntax) group names
+/// EXPAND_AUGROUP Complete autocommand group names
+/// EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
+/// EXPAND_MAPPINGS Complete mapping and abbreviation names,
+/// eg :unmap a^I , :cunab x^I
+/// EXPAND_FUNCTIONS Complete internal or user defined function names,
+/// eg :call sub^I
+/// EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
+/// EXPAND_EXPRESSION Complete internal or user defined function/variable
+/// names in expressions, eg :while s^I
+/// EXPAND_ENV_VARS Complete environment variable names
+/// EXPAND_USER Complete user names
+void set_expand_context(expand_T *xp)
+{
+ CmdlineInfo *const ccline = get_cmdline_info();
+
+ // only expansion for ':', '>' and '=' command-lines
+ if (ccline->cmdfirstc != ':'
+ && ccline->cmdfirstc != '>' && ccline->cmdfirstc != '='
+ && !ccline->input_fn) {
+ xp->xp_context = EXPAND_NOTHING;
+ return;
+ }
+ set_cmd_context(xp, ccline->cmdbuff, ccline->cmdlen, ccline->cmdpos, true);
+}
+
+/// Sets the index of a built-in or user defined command "cmd" in eap->cmdidx.
+/// For user defined commands, the completion context is set in "xp" and the
+/// completion flags in "complp".
+///
+/// @return a pointer to the text after the command or NULL for failure.
+static const char *set_cmd_index(const char *cmd, exarg_T *eap, expand_T *xp, int *complp)
+{
+ const char *p = NULL;
+ size_t len = 0;
+
+ // Isolate the command and search for it in the command table.
+ // Exceptions:
+ // - the 'k' command can directly be followed by any character, but
+ // do accept "keepmarks", "keepalt" and "keepjumps".
+ // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
+ if (*cmd == 'k' && cmd[1] != 'e') {
+ eap->cmdidx = CMD_k;
+ p = cmd + 1;
+ } else {
+ p = cmd;
+ while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card
+ p++;
+ }
+ // a user command may contain digits
+ if (ASCII_ISUPPER(cmd[0])) {
+ while (ASCII_ISALNUM(*p) || *p == '*') {
+ p++;
+ }
+ }
+ // for python 3.x: ":py3*" commands completion
+ if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') {
+ p++;
+ while (ASCII_ISALPHA(*p) || *p == '*') {
+ p++;
+ }
+ }
+ // check for non-alpha command
+ if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) {
+ p++;
+ }
+ len = (size_t)(p - cmd);
+
+ if (len == 0) {
+ xp->xp_context = EXPAND_UNSUCCESSFUL;
+ return NULL;
+ }
+
+ eap->cmdidx = excmd_get_cmdidx(cmd, len);
+
+ if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
+ while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
+ p++;
+ }
+ }
+ }
+
+ // If the cursor is touching the command, and it ends in an alphanumeric
+ // character, complete the command name.
+ if (*p == NUL && ASCII_ISALNUM(p[-1])) {
+ return NULL;
+ }
+
+ if (eap->cmdidx == CMD_SIZE) {
+ if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) {
+ eap->cmdidx = CMD_substitute;
+ p = cmd + 1;
+ } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
+ eap->cmd = (char *)cmd;
+ p = (const char *)find_ucmd(eap, (char *)p, NULL, xp, complp);
+ if (p == NULL) {
+ eap->cmdidx = CMD_SIZE; // Ambiguous user command.
+ }
+ }
+ }
+ if (eap->cmdidx == CMD_SIZE) {
+ // Not still touching the command and it was an illegal one
+ xp->xp_context = EXPAND_UNSUCCESSFUL;
+ return NULL;
+ }
+
+ return p;
+}
+
+/// Set the completion context for a command argument with wild card characters.
+static void set_context_for_wildcard_arg(exarg_T *eap, const char *arg, bool usefilter,
+ expand_T *xp, int *complp)
+{
+ bool in_quote = false;
+ const char *bow = NULL; // Beginning of word.
+ size_t len = 0;
+
+ // Allow spaces within back-quotes to count as part of the argument
+ // being expanded.
+ xp->xp_pattern = skipwhite(arg);
+ const char *p = (const char *)xp->xp_pattern;
+ while (*p != NUL) {
+ int c = utf_ptr2char(p);
+ if (c == '\\' && p[1] != NUL) {
+ p++;
+ } else if (c == '`') {
+ if (!in_quote) {
+ xp->xp_pattern = (char *)p;
+ bow = p + 1;
+ }
+ in_quote = !in_quote;
+ // An argument can contain just about everything, except
+ // characters that end the command and white space.
+ } else if (c == '|' || c == '\n' || c == '"' || ascii_iswhite(c)) {
+ len = 0; // avoid getting stuck when space is in 'isfname'
+ while (*p != NUL) {
+ c = utf_ptr2char(p);
+ if (c == '`' || vim_isfilec_or_wc(c)) {
+ break;
+ }
+ len = (size_t)utfc_ptr2len(p);
+ MB_PTR_ADV(p);
+ }
+ if (in_quote) {
+ bow = p;
+ } else {
+ xp->xp_pattern = (char *)p;
+ }
+ p -= len;
+ }
+ MB_PTR_ADV(p);
+ }
+
+ // If we are still inside the quotes, and we passed a space, just
+ // expand from there.
+ if (bow != NULL && in_quote) {
+ xp->xp_pattern = (char *)bow;
+ }
+ xp->xp_context = EXPAND_FILES;
+
+ // For a shell command more chars need to be escaped.
+ if (usefilter || eap->cmdidx == CMD_bang || eap->cmdidx == CMD_terminal) {
+#ifndef BACKSLASH_IN_FILENAME
+ xp->xp_shell = true;
+#endif
+ // When still after the command name expand executables.
+ if (xp->xp_pattern == skipwhite(arg)) {
+ xp->xp_context = EXPAND_SHELLCMD;
+ }
+ }
+
+ // Check for environment variable.
+ if (*xp->xp_pattern == '$') {
+ for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) {
+ if (!vim_isIDc((uint8_t)(*p))) {
+ break;
+ }
+ }
+ if (*p == NUL) {
+ xp->xp_context = EXPAND_ENV_VARS;
+ xp->xp_pattern++;
+ // Avoid that the assignment uses EXPAND_FILES again.
+ if (*complp != EXPAND_USER_DEFINED && *complp != EXPAND_USER_LIST) {
+ *complp = EXPAND_ENV_VARS;
+ }
+ }
+ }
+ // Check for user names.
+ if (*xp->xp_pattern == '~') {
+ for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {}
+ // Complete ~user only if it partially matches a user name.
+ // A full match ~user<Tab> will be replaced by user's home
+ // directory i.e. something like ~user<Tab> -> /home/user/
+ if (*p == NUL && p > (const char *)xp->xp_pattern + 1
+ && match_user((char_u *)xp->xp_pattern + 1) >= 1) {
+ xp->xp_context = EXPAND_USER;
+ xp->xp_pattern++;
+ }
+ }
+}
+
+/// Set the completion context in "xp" for command "cmd" with index "cmdidx".
+/// The argument to the command is "arg" and the argument flags is "argt".
+/// For user-defined commands and for environment variables, "context" has the
+/// completion type.
+///
+/// @return a pointer to the next command, or NULL if there is no next command.
+static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, const char *arg,
+ uint32_t argt, int context, expand_T *xp, bool forceit)
+{
+ const char *p;
+
+ switch (cmdidx) {
+ case CMD_find:
+ case CMD_sfind:
+ case CMD_tabfind:
+ if (xp->xp_context == EXPAND_FILES) {
+ xp->xp_context = EXPAND_FILES_IN_PATH;
+ }
+ break;
+ case CMD_cd:
+ case CMD_chdir:
+ case CMD_lcd:
+ case CMD_lchdir:
+ case CMD_tcd:
+ case CMD_tchdir:
+ if (xp->xp_context == EXPAND_FILES) {
+ xp->xp_context = EXPAND_DIRECTORIES;
+ }
+ break;
+ case CMD_help:
+ xp->xp_context = EXPAND_HELP;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ // Command modifiers: return the argument.
+ // Also for commands with an argument that is a command.
+ case CMD_aboveleft:
+ case CMD_argdo:
+ case CMD_belowright:
+ case CMD_botright:
+ case CMD_browse:
+ case CMD_bufdo:
+ case CMD_cdo:
+ case CMD_cfdo:
+ case CMD_confirm:
+ case CMD_debug:
+ case CMD_folddoclosed:
+ case CMD_folddoopen:
+ case CMD_hide:
+ case CMD_keepalt:
+ case CMD_keepjumps:
+ case CMD_keepmarks:
+ case CMD_keeppatterns:
+ case CMD_ldo:
+ case CMD_leftabove:
+ case CMD_lfdo:
+ case CMD_lockmarks:
+ case CMD_noautocmd:
+ case CMD_noswapfile:
+ case CMD_rightbelow:
+ case CMD_sandbox:
+ case CMD_silent:
+ case CMD_tab:
+ case CMD_tabdo:
+ case CMD_topleft:
+ case CMD_verbose:
+ case CMD_vertical:
+ case CMD_windo:
+ return arg;
+
+ case CMD_filter:
+ if (*arg != NUL) {
+ arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
+ }
+ if (arg == NULL || *arg == NUL) {
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+ return (const char *)skipwhite(arg);
+
+ case CMD_match:
+ if (*arg == NUL || !ends_excmd(*arg)) {
+ // also complete "None"
+ set_context_in_echohl_cmd(xp, arg);
+ arg = (const char *)skipwhite(skiptowhite(arg));
+ if (*arg != NUL) {
+ xp->xp_context = EXPAND_NOTHING;
+ arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic, NULL);
+ }
+ }
+ return (const char *)find_nextcmd(arg);
+
+ // All completion for the +cmdline_compl feature goes here.
+
+ case CMD_command:
+ return set_context_in_user_cmd(xp, arg);
+
+ case CMD_delcommand:
+ xp->xp_context = EXPAND_USER_COMMANDS;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_global:
+ case CMD_vglobal: {
+ const int delim = (uint8_t)(*arg); // Get the delimiter.
+ if (delim) {
+ arg++; // Skip delimiter if there is one.
+ }
+
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ arg++;
+ }
+ if (arg[0] != NUL) {
+ return arg + 1;
+ }
+ break;
+ }
+ case CMD_and:
+ case CMD_substitute: {
+ const int delim = (uint8_t)(*arg);
+ if (delim) {
+ // Skip "from" part.
+ arg++;
+ arg = (const char *)skip_regexp((char *)arg, delim, p_magic, NULL);
+ }
+ // Skip "to" part.
+ while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
+ if (arg[0] == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ arg++;
+ }
+ if (arg[0] != NUL) { // Skip delimiter.
+ arg++;
+ }
+ while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
+ arg++;
+ }
+ if (arg[0] != NUL) {
+ return arg;
+ }
+ break;
+ }
+ case CMD_isearch:
+ case CMD_dsearch:
+ case CMD_ilist:
+ case CMD_dlist:
+ case CMD_ijump:
+ case CMD_psearch:
+ case CMD_djump:
+ case CMD_isplit:
+ case CMD_dsplit:
+ // Skip count.
+ arg = (const char *)skipwhite(skipdigits(arg));
+ if (*arg == '/') { // Match regexp, not just whole words.
+ for (++arg; *arg && *arg != '/'; arg++) {
+ if (*arg == '\\' && arg[1] != NUL) {
+ arg++;
+ }
+ }
+ if (*arg) {
+ arg = (const char *)skipwhite(arg + 1);
+
+ // Check for trailing illegal characters.
+ if (*arg && strchr("|\"\n", *arg) == NULL) {
+ xp->xp_context = EXPAND_NOTHING;
+ } else {
+ return arg;
+ }
+ }
+ }
+ break;
+ case CMD_autocmd:
+ return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
+
+ case CMD_doautocmd:
+ case CMD_doautoall:
+ return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
+ case CMD_set:
+ set_context_in_set_cmd(xp, (char_u *)arg, 0);
+ break;
+ case CMD_setglobal:
+ set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL);
+ break;
+ case CMD_setlocal:
+ set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL);
+ break;
+ case CMD_tag:
+ case CMD_stag:
+ case CMD_ptag:
+ case CMD_ltag:
+ case CMD_tselect:
+ case CMD_stselect:
+ case CMD_ptselect:
+ case CMD_tjump:
+ case CMD_stjump:
+ case CMD_ptjump:
+ if (wop_flags & WOP_TAGFILE) {
+ xp->xp_context = EXPAND_TAGS_LISTFILES;
+ } else {
+ xp->xp_context = EXPAND_TAGS;
+ }
+ xp->xp_pattern = (char *)arg;
+ break;
+ case CMD_augroup:
+ xp->xp_context = EXPAND_AUGROUP;
+ xp->xp_pattern = (char *)arg;
+ break;
+ case CMD_syntax:
+ set_context_in_syntax_cmd(xp, arg);
+ break;
+ case CMD_const:
+ case CMD_let:
+ case CMD_if:
+ case CMD_elseif:
+ case CMD_while:
+ case CMD_for:
+ case CMD_echo:
+ case CMD_echon:
+ case CMD_execute:
+ case CMD_echomsg:
+ case CMD_echoerr:
+ case CMD_call:
+ case CMD_return:
+ case CMD_cexpr:
+ case CMD_caddexpr:
+ case CMD_cgetexpr:
+ case CMD_lexpr:
+ case CMD_laddexpr:
+ case CMD_lgetexpr:
+ set_context_for_expression(xp, (char *)arg, cmdidx);
+ break;
+
+ case CMD_unlet:
+ while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
+ arg = (const char *)xp->xp_pattern + 1;
+ }
+
+ xp->xp_context = EXPAND_USER_VARS;
+ xp->xp_pattern = (char *)arg;
+
+ if (*xp->xp_pattern == '$') {
+ xp->xp_context = EXPAND_ENV_VARS;
+ xp->xp_pattern++;
+ }
+
+ break;
+
+ case CMD_function:
+ case CMD_delfunction:
+ xp->xp_context = EXPAND_USER_FUNC;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_echohl:
+ set_context_in_echohl_cmd(xp, arg);
+ break;
+ case CMD_highlight:
+ set_context_in_highlight_cmd(xp, arg);
+ break;
+ case CMD_cscope:
+ case CMD_lcscope:
+ case CMD_scscope:
+ set_context_in_cscope_cmd(xp, arg, cmdidx);
+ break;
+ case CMD_sign:
+ set_context_in_sign_cmd(xp, (char_u *)arg);
+ break;
+ case CMD_bdelete:
+ case CMD_bwipeout:
+ case CMD_bunload:
+ while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
+ arg = (const char *)xp->xp_pattern + 1;
+ }
+ FALLTHROUGH;
+ case CMD_buffer:
+ case CMD_sbuffer:
+ case CMD_checktime:
+ xp->xp_context = EXPAND_BUFFERS;
+ xp->xp_pattern = (char *)arg;
+ break;
+ case CMD_diffget:
+ case CMD_diffput:
+ // If current buffer is in diff mode, complete buffer names
+ // which are in diff mode, and different than current buffer.
+ xp->xp_context = EXPAND_DIFF_BUFFERS;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_USER:
+ case CMD_USER_BUF:
+ if (context != EXPAND_NOTHING) {
+ // EX_XFILE: file names are handled above.
+ if (!(argt & EX_XFILE)) {
+ if (context == EXPAND_MENUS) {
+ return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
+ } else if (context == EXPAND_COMMANDS) {
+ return arg;
+ } else if (context == EXPAND_MAPPINGS) {
+ return (const char *)set_context_in_map_cmd(xp, "map", (char_u *)arg, forceit,
+ false, false,
+ CMD_map);
+ }
+ // Find start of last argument.
+ p = arg;
+ while (*p) {
+ if (*p == ' ') {
+ // argument starts after a space
+ arg = p + 1;
+ } else if (*p == '\\' && *(p + 1) != NUL) {
+ p++; // skip over escaped character
+ }
+ MB_PTR_ADV(p);
+ }
+ xp->xp_pattern = (char *)arg;
+ }
+ xp->xp_context = context;
+ }
+ break;
+
+ case CMD_map:
+ case CMD_noremap:
+ case CMD_nmap:
+ case CMD_nnoremap:
+ case CMD_vmap:
+ case CMD_vnoremap:
+ case CMD_omap:
+ case CMD_onoremap:
+ case CMD_imap:
+ case CMD_inoremap:
+ case CMD_cmap:
+ case CMD_cnoremap:
+ case CMD_lmap:
+ case CMD_lnoremap:
+ case CMD_smap:
+ case CMD_snoremap:
+ case CMD_xmap:
+ case CMD_xnoremap:
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
+ false, cmdidx);
+ case CMD_unmap:
+ case CMD_nunmap:
+ case CMD_vunmap:
+ case CMD_ounmap:
+ case CMD_iunmap:
+ case CMD_cunmap:
+ case CMD_lunmap:
+ case CMD_sunmap:
+ case CMD_xunmap:
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, false,
+ true, cmdidx);
+ case CMD_mapclear:
+ case CMD_nmapclear:
+ case CMD_vmapclear:
+ case CMD_omapclear:
+ case CMD_imapclear:
+ case CMD_cmapclear:
+ case CMD_lmapclear:
+ case CMD_smapclear:
+ case CMD_xmapclear:
+ xp->xp_context = EXPAND_MAPCLEAR;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_abbreviate:
+ case CMD_noreabbrev:
+ case CMD_cabbrev:
+ case CMD_cnoreabbrev:
+ case CMD_iabbrev:
+ case CMD_inoreabbrev:
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
+ false, cmdidx);
+ case CMD_unabbreviate:
+ case CMD_cunabbrev:
+ case CMD_iunabbrev:
+ return (const char *)set_context_in_map_cmd(xp, (char *)cmd, (char_u *)arg, forceit, true,
+ true, cmdidx);
+ case CMD_menu:
+ case CMD_noremenu:
+ case CMD_unmenu:
+ case CMD_amenu:
+ case CMD_anoremenu:
+ case CMD_aunmenu:
+ case CMD_nmenu:
+ case CMD_nnoremenu:
+ case CMD_nunmenu:
+ case CMD_vmenu:
+ case CMD_vnoremenu:
+ case CMD_vunmenu:
+ case CMD_omenu:
+ case CMD_onoremenu:
+ case CMD_ounmenu:
+ case CMD_imenu:
+ case CMD_inoremenu:
+ case CMD_iunmenu:
+ case CMD_cmenu:
+ case CMD_cnoremenu:
+ case CMD_cunmenu:
+ case CMD_tlmenu:
+ case CMD_tlnoremenu:
+ case CMD_tlunmenu:
+ case CMD_tmenu:
+ case CMD_tunmenu:
+ case CMD_popup:
+ case CMD_emenu:
+ return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
+
+ case CMD_colorscheme:
+ xp->xp_context = EXPAND_COLORS;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_compiler:
+ xp->xp_context = EXPAND_COMPILER;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_ownsyntax:
+ xp->xp_context = EXPAND_OWNSYNTAX;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_setfiletype:
+ xp->xp_context = EXPAND_FILETYPE;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_packadd:
+ xp->xp_context = EXPAND_PACKADD;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+#ifdef HAVE_WORKING_LIBINTL
+ case CMD_language:
+ p = (const char *)skiptowhite(arg);
+ if (*p == NUL) {
+ xp->xp_context = EXPAND_LANGUAGE;
+ xp->xp_pattern = (char *)arg;
+ } else {
+ if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
+ || strncmp(arg, "ctype", (size_t)(p - arg)) == 0
+ || strncmp(arg, "time", (size_t)(p - arg)) == 0
+ || strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
+ xp->xp_context = EXPAND_LOCALES;
+ xp->xp_pattern = skipwhite(p);
+ } else {
+ xp->xp_context = EXPAND_NOTHING;
+ }
+ }
+ break;
+#endif
+ case CMD_profile:
+ set_context_in_profile_cmd(xp, arg);
+ break;
+ case CMD_checkhealth:
+ xp->xp_context = EXPAND_CHECKHEALTH;
+ xp->xp_pattern = (char *)arg;
+ break;
+ case CMD_behave:
+ xp->xp_context = EXPAND_BEHAVE;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_messages:
+ xp->xp_context = EXPAND_MESSAGES;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_history:
+ xp->xp_context = EXPAND_HISTORY;
+ xp->xp_pattern = (char *)arg;
+ break;
+ case CMD_syntime:
+ xp->xp_context = EXPAND_SYNTIME;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_argdelete:
+ while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) {
+ arg = (const char *)(xp->xp_pattern + 1);
+ }
+ xp->xp_context = EXPAND_ARGLIST;
+ xp->xp_pattern = (char *)arg;
+ break;
+
+ case CMD_lua:
+ xp->xp_context = EXPAND_LUA;
+ break;
+
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
+/// we don't need/want deleted. Maybe this could be done better if we didn't
+/// repeat all this stuff. The only problem is that they may not stay
+/// perfectly compatible with each other, but then the command line syntax
+/// probably won't change that much -- webb.
+///
+/// @param buff buffer for command string
+static const char *set_one_cmd_context(expand_T *xp, const char *buff)
+{
+ size_t len = 0;
+ exarg_T ea;
+ int context = EXPAND_NOTHING;
+ bool forceit = false;
+ bool usefilter = false; // Filter instead of file name.
+
+ ExpandInit(xp);
+ xp->xp_pattern = (char *)buff;
+ xp->xp_line = (char *)buff;
+ xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
+ ea.argt = 0;
+
+ // 1. skip comment lines and leading space, colons or bars
+ const char *cmd;
+ for (cmd = buff; vim_strchr(" \t:|", *cmd) != NULL; cmd++) {}
+ xp->xp_pattern = (char *)cmd;
+
+ if (*cmd == NUL) {
+ return NULL;
+ }
+ if (*cmd == '"') { // ignore comment lines
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+
+ // 3. skip over a range specifier of the form: addr [,addr] [;addr] ..
+ cmd = (const char *)skip_range(cmd, &xp->xp_context);
+ xp->xp_pattern = (char *)cmd;
+ if (*cmd == NUL) {
+ return NULL;
+ }
+ if (*cmd == '"') {
+ xp->xp_context = EXPAND_NOTHING;
+ return NULL;
+ }
+
+ if (*cmd == '|' || *cmd == '\n') {
+ return cmd + 1; // There's another command
+ }
+
+ // Get the command index.
+ const char *p = set_cmd_index(cmd, &ea, xp, &context);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
+
+ if (*p == '!') { // forced commands
+ forceit = true;
+ p++;
+ }
+
+ // 6. parse arguments
+ if (!IS_USER_CMDIDX(ea.cmdidx)) {
+ ea.argt = excmd_get_argt(ea.cmdidx);
+ }
+
+ const char *arg = (const char *)skipwhite(p);
+
+ // Skip over ++argopt argument
+ if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
+ p = arg;
+ while (*p && !ascii_isspace(*p)) {
+ MB_PTR_ADV(p);
+ }
+ arg = (const char *)skipwhite(p);
+ }
+
+ if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
+ if (*arg == '>') { // append
+ if (*++arg == '>') {
+ arg++;
+ }
+ arg = (const char *)skipwhite(arg);
+ } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
+ arg++;
+ usefilter = true;
+ }
+ }
+
+ if (ea.cmdidx == CMD_read) {
+ usefilter = forceit; // :r! filter if forced
+ if (*arg == '!') { // :r !filter
+ arg++;
+ usefilter = true;
+ }
+ }
+
+ if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
+ while (*arg == *cmd) { // allow any number of '>' or '<'
+ arg++;
+ }
+ arg = (const char *)skipwhite(arg);
+ }
+
+ // Does command allow "+command"?
+ if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') {
+ // Check if we're in the +command
+ p = arg + 1;
+ arg = (const char *)skip_cmd_arg((char *)arg, false);
+
+ // Still touching the command after '+'?
+ if (*arg == NUL) {
+ return p;
+ }
+
+ // Skip space(s) after +command to get to the real argument.
+ arg = (const char *)skipwhite(arg);
+ }
+
+ // Check for '|' to separate commands and '"' to start comments.
+ // Don't do this for ":read !cmd" and ":write !cmd".
+ if ((ea.argt & EX_TRLBAR) && !usefilter) {
+ p = arg;
+ // ":redir @" is not the start of a comment
+ if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') {
+ p += 2;
+ }
+ while (*p) {
+ if (*p == Ctrl_V) {
+ if (p[1] != NUL) {
+ p++;
+ }
+ } else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM))
+ || *p == '|'
+ || *p == '\n') {
+ if (*(p - 1) != '\\') {
+ if (*p == '|' || *p == '\n') {
+ return p + 1;
+ }
+ return NULL; // It's a comment
+ }
+ }
+ MB_PTR_ADV(p);
+ }
+ }
+
+ if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) {
+ // no arguments allowed but there is something
+ return NULL;
+ }
+
+ // Find start of last argument (argument just before cursor):
+ p = buff;
+ xp->xp_pattern = (char *)p;
+ len = strlen(buff);
+ while (*p && p < buff + len) {
+ if (*p == ' ' || *p == TAB) {
+ // argument starts after a space
+ xp->xp_pattern = (char *)++p;
+ } else {
+ if (*p == '\\' && *(p + 1) != NUL) {
+ p++; // skip over escaped character
+ }
+ MB_PTR_ADV(p);
+ }
+ }
+
+ if (ea.argt & EX_XFILE) {
+ set_context_for_wildcard_arg(&ea, arg, usefilter, xp, &context);
+ }
+
+ // Switch on command name.
+ return set_context_by_cmdname(cmd, ea.cmdidx, arg, ea.argt, context, xp, forceit);
+}
+
+/// @param str start of command line
+/// @param len length of command line (excl. NUL)
+/// @param col position of cursor
+/// @param use_ccline use ccline for info
+void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline)
+{
+ CmdlineInfo *const ccline = get_cmdline_info();
+ char_u old_char = NUL;
+
+ // Avoid a UMR warning from Purify, only save the character if it has been
+ // written before.
+ if (col < len) {
+ old_char = str[col];
+ }
+ str[col] = NUL;
+ const char *nextcomm = (const char *)str;
+
+ if (use_ccline && ccline->cmdfirstc == '=') {
+ // pass CMD_SIZE because there is no real command
+ set_context_for_expression(xp, (char *)str, CMD_SIZE);
+ } else if (use_ccline && ccline->input_fn) {
+ xp->xp_context = ccline->xp_context;
+ xp->xp_pattern = (char *)ccline->cmdbuff;
+ xp->xp_arg = (char *)ccline->xp_arg;
+ } else {
+ while (nextcomm != NULL) {
+ nextcomm = set_one_cmd_context(xp, nextcomm);
+ }
+ }
+
+ // Store the string here so that call_user_expand_func() can get to them
+ // easily.
+ xp->xp_line = (char *)str;
+ xp->xp_col = col;
+
+ str[col] = old_char;
+}
+
+/// Expand the command line "str" from context "xp".
+/// "xp" must have been set by set_cmd_context().
+/// xp->xp_pattern points into "str", to where the text that is to be expanded
+/// starts.
+/// Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
+/// cursor.
+/// Returns EXPAND_NOTHING when there is nothing to expand, might insert the
+/// key that triggered expansion literally.
+/// Returns EXPAND_OK otherwise.
+///
+/// @param str start of command line
+/// @param col position of cursor
+/// @param matchcount return: nr of matches
+/// @param matches return: array of pointers to matches
+int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches)
+{
+ char_u *file_str = NULL;
+ int options = WILD_ADD_SLASH|WILD_SILENT;
+
+ if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
+ beep_flush();
+ return EXPAND_UNSUCCESSFUL; // Something illegal on command line
+ }
+ if (xp->xp_context == EXPAND_NOTHING) {
+ // Caller can use the character as a normal char instead
+ return EXPAND_NOTHING;
+ }
+
+ // add star to file name, or convert to regexp if not exp. files.
+ assert((str + col) - (char_u *)xp->xp_pattern >= 0);
+ xp->xp_pattern_len = (size_t)((str + col) - (char_u *)xp->xp_pattern);
+ file_str = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
+
+ if (p_wic) {
+ options += WILD_ICASE;
+ }
+
+ // find all files that match the description
+ if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) {
+ *matchcount = 0;
+ *matches = NULL;
+ }
+ xfree(file_str);
+
+ return EXPAND_OK;
+}
+
+/// Expand file or directory names.
+static int expand_files_and_dirs(expand_T *xp, char_u *pat, char ***file, int *num_file, int flags,
+ int options)
+{
+ bool free_pat = false;
+
+ // for ":set path=" and ":set tags=" halve backslashes for escaped space
+ if (xp->xp_backslash != XP_BS_NONE) {
+ free_pat = true;
+ pat = vim_strsave(pat);
+ for (int i = 0; pat[i]; i++) {
+ if (pat[i] == '\\') {
+ if (xp->xp_backslash == XP_BS_THREE
+ && pat[i + 1] == '\\'
+ && pat[i + 2] == '\\'
+ && pat[i + 3] == ' ') {
+ STRMOVE(pat + i, pat + i + 3);
+ }
+ if (xp->xp_backslash == XP_BS_ONE
+ && pat[i + 1] == ' ') {
+ STRMOVE(pat + i, pat + i + 1);
+ }
+ }
+ }
+ }
+
+ if (xp->xp_context == EXPAND_FILES) {
+ flags |= EW_FILE;
+ } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
+ flags |= (EW_FILE | EW_PATH);
+ } else {
+ flags = (flags | EW_DIR) & ~EW_FILE;
+ }
+ if (options & WILD_ICASE) {
+ flags |= EW_ICASE;
+ }
+
+ // Expand wildcards, supporting %:h and the like.
+ int ret = expand_wildcards_eval(&pat, num_file, file, flags);
+ if (free_pat) {
+ xfree(pat);
+ }
+#ifdef BACKSLASH_IN_FILENAME
+ if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
+ for (int j = 0; j < *num_file; j++) {
+ char *ptr = (*file)[j];
+ while (*ptr != NUL) {
+ if (p_csl[0] == 's' && *ptr == '\\') {
+ *ptr = '/';
+ } else if (p_csl[0] == 'b' && *ptr == '/') {
+ *ptr = '\\';
+ }
+ ptr += utfc_ptr2len(ptr);
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":behave {mswin,xterm}" command.
+static char *get_behave_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx == 0) {
+ return "mswin";
+ }
+ if (idx == 1) {
+ return "xterm";
+ }
+ return NULL;
+}
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":messages {clear}" command.
+static char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx == 0) {
+ return "clear";
+ }
+ return NULL;
+}
+
+static char *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ if (idx == 0) {
+ return "<buffer>";
+ }
+ return NULL;
+}
+
+/// Completion for |:checkhealth| command.
+///
+/// Given to ExpandGeneric() to obtain all available heathcheck names.
+/// @param[in] idx Index of the healthcheck item.
+/// @param[in] xp Not used.
+static char *get_healthcheck_names(expand_T *xp FUNC_ATTR_UNUSED, int idx)
+{
+ static Object names = OBJECT_INIT;
+ static unsigned last_gen = 0;
+
+ if (last_gen != get_cmdline_last_prompt_id() || last_gen == 0) {
+ Array a = ARRAY_DICT_INIT;
+ Error err = ERROR_INIT;
+ Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err);
+ api_clear_error(&err);
+ api_free_object(names);
+ names = res;
+ last_gen = get_cmdline_last_prompt_id();
+ }
+
+ if (names.type == kObjectTypeArray && idx < (int)names.data.array.size) {
+ return names.data.array.items[idx].data.string.data;
+ }
+ return NULL;
+}
+
+/// Do the expansion based on xp->xp_context and "rmp".
+static int ExpandOther(expand_T *xp, regmatch_T *rmp, int *num_file, char ***file)
+{
+ typedef CompleteListItemGetter ExpandFunc;
+ static struct expgen {
+ int context;
+ ExpandFunc func;
+ int ic;
+ int escaped;
+ } tab[] = {
+ { EXPAND_COMMANDS, get_command_name, false, true },
+ { EXPAND_BEHAVE, get_behave_arg, true, true },
+ { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
+ { EXPAND_MESSAGES, get_messages_arg, true, true },
+ { EXPAND_HISTORY, get_history_arg, true, true },
+ { EXPAND_USER_COMMANDS, get_user_commands, false, true },
+ { EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
+ { EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, false, true },
+ { EXPAND_USER_NARGS, get_user_cmd_nargs, false, true },
+ { EXPAND_USER_COMPLETE, get_user_cmd_complete, false, true },
+ { EXPAND_USER_VARS, get_user_var_name, false, true },
+ { EXPAND_FUNCTIONS, get_function_name, false, true },
+ { EXPAND_USER_FUNC, get_user_func_name, false, true },
+ { EXPAND_EXPRESSION, get_expr_name, false, true },
+ { EXPAND_MENUS, get_menu_name, false, true },
+ { EXPAND_MENUNAMES, get_menu_names, false, true },
+ { EXPAND_SYNTAX, get_syntax_name, true, true },
+ { EXPAND_SYNTIME, get_syntime_arg, true, true },
+ { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, false },
+ { EXPAND_EVENTS, expand_get_event_name, true, false },
+ { EXPAND_AUGROUP, expand_get_augroup_name, true, false },
+ { EXPAND_CSCOPE, get_cscope_name, true, true },
+ { EXPAND_SIGN, get_sign_name, true, true },
+ { EXPAND_PROFILE, get_profile_name, true, true },
+#ifdef HAVE_WORKING_LIBINTL
+ { EXPAND_LANGUAGE, get_lang_arg, true, false },
+ { EXPAND_LOCALES, get_locales, true, false },
+#endif
+ { EXPAND_ENV_VARS, get_env_name, true, true },
+ { EXPAND_USER, get_users, true, false },
+ { EXPAND_ARGLIST, get_arglist_name, true, false },
+ { EXPAND_CHECKHEALTH, get_healthcheck_names, true, false },
+ };
+ int ret = FAIL;
+
+ // Find a context in the table and call the ExpandGeneric() with the
+ // right function to do the expansion.
+ for (int i = 0; i < (int)ARRAY_SIZE(tab); i++) {
+ if (xp->xp_context == tab[i].context) {
+ if (tab[i].ic) {
+ rmp->rm_ic = true;
+ }
+ ExpandGeneric(xp, rmp, num_file, file, tab[i].func, tab[i].escaped);
+ ret = OK;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/// Do the expansion based on xp->xp_context and "pat".
+///
+/// @param options WILD_ flags
+static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***file, int options)
+{
+ regmatch_T regmatch;
+ int ret;
+ int flags;
+
+ flags = EW_DIR; // include directories
+ if (options & WILD_LIST_NOTFOUND) {
+ flags |= EW_NOTFOUND;
+ }
+ if (options & WILD_ADD_SLASH) {
+ flags |= EW_ADDSLASH;
+ }
+ if (options & WILD_KEEP_ALL) {
+ flags |= EW_KEEPALL;
+ }
+ if (options & WILD_SILENT) {
+ flags |= EW_SILENT;
+ }
+ if (options & WILD_NOERROR) {
+ flags |= EW_NOERROR;
+ }
+ if (options & WILD_ALLLINKS) {
+ flags |= EW_ALLLINKS;
+ }
+
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_DIRECTORIES
+ || xp->xp_context == EXPAND_FILES_IN_PATH) {
+ return expand_files_and_dirs(xp, pat, file, num_file, flags, options);
+ }
+
+ *file = NULL;
+ *num_file = 0;
+ if (xp->xp_context == EXPAND_HELP) {
+ // With an empty argument we would get all the help tags, which is
+ // very slow. Get matches for "help" instead.
+ if (find_help_tags(*pat == NUL ? "help" : (char *)pat,
+ num_file, file, false) == OK) {
+ cleanup_help_tags(*num_file, *file);
+ return OK;
+ }
+ return FAIL;
+ }
+
+ if (xp->xp_context == EXPAND_SHELLCMD) {
+ *file = NULL;
+ expand_shellcmd(pat, num_file, file, flags);
+ return OK;
+ }
+ if (xp->xp_context == EXPAND_OLD_SETTING) {
+ ExpandOldSetting(num_file, file);
+ return OK;
+ }
+ if (xp->xp_context == EXPAND_BUFFERS) {
+ return ExpandBufnames((char *)pat, num_file, file, options);
+ }
+ if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
+ return ExpandBufnames((char *)pat, num_file, file, options | BUF_DIFF_FILTER);
+ }
+ if (xp->xp_context == EXPAND_TAGS
+ || xp->xp_context == EXPAND_TAGS_LISTFILES) {
+ return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
+ }
+ if (xp->xp_context == EXPAND_COLORS) {
+ char *directories[] = { "colors", NULL };
+ return ExpandRTDir((char *)pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories);
+ }
+ if (xp->xp_context == EXPAND_COMPILER) {
+ char *directories[] = { "compiler", NULL };
+ return ExpandRTDir((char *)pat, DIP_LUA, num_file, file, directories);
+ }
+ if (xp->xp_context == EXPAND_OWNSYNTAX) {
+ char *directories[] = { "syntax", NULL };
+ return ExpandRTDir((char *)pat, 0, num_file, file, directories);
+ }
+ if (xp->xp_context == EXPAND_FILETYPE) {
+ char *directories[] = { "syntax", "indent", "ftplugin", NULL };
+ return ExpandRTDir((char *)pat, DIP_LUA, num_file, file, directories);
+ }
+ if (xp->xp_context == EXPAND_USER_LIST) {
+ return ExpandUserList(xp, num_file, file);
+ }
+ if (xp->xp_context == EXPAND_USER_LUA) {
+ return ExpandUserLua(xp, num_file, file);
+ }
+ if (xp->xp_context == EXPAND_PACKADD) {
+ return ExpandPackAddDir((char *)pat, num_file, file);
+ }
+
+ // When expanding a function name starting with s:, match the <SNR>nr_
+ // prefix.
+ char *tofree = NULL;
+ if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) {
+ const size_t len = STRLEN(pat) + 20;
+
+ tofree = xmalloc(len);
+ snprintf(tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
+ pat = (char_u *)tofree;
+ }
+
+ if (xp->xp_context == EXPAND_LUA) {
+ ILOG("PAT %s", pat);
+ return nlua_expand_pat(xp, pat, num_file, file);
+ }
+
+ regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
+ if (regmatch.regprog == NULL) {
+ return FAIL;
+ }
+
+ // set ignore-case according to p_ic, p_scs and pat
+ regmatch.rm_ic = ignorecase(pat);
+
+ if (xp->xp_context == EXPAND_SETTINGS
+ || xp->xp_context == EXPAND_BOOL_SETTINGS) {
+ ret = ExpandSettings(xp, &regmatch, num_file, file);
+ } else if (xp->xp_context == EXPAND_MAPPINGS) {
+ ret = ExpandMappings(&regmatch, num_file, file);
+ } else if (xp->xp_context == EXPAND_USER_DEFINED) {
+ ret = ExpandUserDefined(xp, &regmatch, num_file, file);
+ } else {
+ ret = ExpandOther(xp, &regmatch, num_file, file);
+ }
+
+ vim_regfree(regmatch.regprog);
+ xfree(tofree);
+
+ return ret;
+}
+
+/// Expand a list of names.
+///
+/// Generic function for command line completion. It calls a function to
+/// obtain strings, one by one. The strings are matched against a regexp
+/// program. Matching strings are copied into an array, which is returned.
+///
+/// @param func returns a string from the list
+static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file,
+ CompleteListItemGetter func, int escaped)
+{
+ int i;
+ size_t count = 0;
+ char_u *str;
+
+ // count the number of matching names
+ for (i = 0;; i++) {
+ str = (char_u *)(*func)(xp, i);
+ if (str == NULL) { // end of list
+ break;
+ }
+ if (*str == NUL) { // skip empty strings
+ continue;
+ }
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ return;
+ }
+ assert(count < INT_MAX);
+ *num_file = (int)count;
+ *file = xmalloc(count * sizeof(char_u *));
+
+ // copy the matching names into allocated memory
+ count = 0;
+ for (i = 0;; i++) {
+ str = (char_u *)(*func)(xp, i);
+ if (str == NULL) { // End of list.
+ break;
+ }
+ if (*str == NUL) { // Skip empty strings.
+ continue;
+ }
+ if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
+ if (escaped) {
+ str = vim_strsave_escaped(str, (char_u *)" \t\\.");
+ } else {
+ str = vim_strsave(str);
+ }
+ (*file)[count++] = (char *)str;
+ if (func == get_menu_names) {
+ // Test for separator added by get_menu_names().
+ str += STRLEN(str) - 1;
+ if (*str == '\001') {
+ *str = '.';
+ }
+ }
+ }
+ }
+
+ // Sort the results. Keep menu's in the specified order.
+ if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
+ if (xp->xp_context == EXPAND_EXPRESSION
+ || xp->xp_context == EXPAND_FUNCTIONS
+ || xp->xp_context == EXPAND_USER_FUNC) {
+ // <SNR> functions should be sorted to the end.
+ qsort((void *)(*file), (size_t)(*num_file), sizeof(char_u *),
+ sort_func_compare);
+ } else {
+ sort_strings(*file, *num_file);
+ }
+ }
+
+ // Reset the variables used for special highlight names expansion, so that
+ // they don't show up when getting normal highlight names by ID.
+ reset_expand_highlight();
+}
+
+/// Complete a shell command.
+///
+/// @param filepat is a pattern to match with command names.
+/// @param[out] num_file is pointer to number of matches.
+/// @param[out] file is pointer to array of pointers to matches.
+/// *file will either be set to NULL or point to
+/// allocated memory.
+/// @param flagsarg is a combination of EW_* flags.
+static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int flagsarg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char_u *pat;
+ int i;
+ char_u *path = NULL;
+ garray_T ga;
+ char *buf = xmalloc(MAXPATHL);
+ size_t l;
+ char_u *s, *e;
+ int flags = flagsarg;
+ int ret;
+ bool did_curdir = false;
+
+ // for ":set path=" and ":set tags=" halve backslashes for escaped space
+ pat = vim_strsave(filepat);
+ for (i = 0; pat[i]; i++) {
+ if (pat[i] == '\\' && pat[i + 1] == ' ') {
+ STRMOVE(pat + i, pat + i + 1);
+ }
+ }
+
+ flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
+
+ bool mustfree = false; // Track memory allocation for *path.
+ if (pat[0] == '.' && (vim_ispathsep(pat[1])
+ || (pat[1] == '.' && vim_ispathsep(pat[2])))) {
+ path = (char_u *)".";
+ } else {
+ // For an absolute name we don't use $PATH.
+ if (!path_is_absolute(pat)) {
+ path = (char_u *)vim_getenv("PATH");
+ }
+ if (path == NULL) {
+ path = (char_u *)"";
+ } else {
+ mustfree = true;
+ }
+ }
+
+ // Go over all directories in $PATH. Expand matches in that directory and
+ // collect them in "ga". When "." is not in $PATH also expaned for the
+ // current directory, to find "subdir/cmd".
+ ga_init(&ga, (int)sizeof(char *), 10);
+ hashtab_T found_ht;
+ hash_init(&found_ht);
+ for (s = path;; s = e) {
+ e = (char_u *)vim_strchr((char *)s, ENV_SEPCHAR);
+ if (e == NULL) {
+ e = s + STRLEN(s);
+ }
+
+ if (*s == NUL) {
+ if (did_curdir) {
+ break;
+ }
+ // Find directories in the current directory, path is empty.
+ did_curdir = true;
+ flags |= EW_DIR;
+ } else if (STRNCMP(s, ".", e - s) == 0) {
+ did_curdir = true;
+ flags |= EW_DIR;
+ } else {
+ // Do not match directories inside a $PATH item.
+ flags &= ~EW_DIR;
+ }
+
+ l = (size_t)(e - s);
+ if (l > MAXPATHL - 5) {
+ break;
+ }
+ STRLCPY(buf, s, l + 1);
+ add_pathsep(buf);
+ l = STRLEN(buf);
+ STRLCPY(buf + l, pat, MAXPATHL - l);
+
+ // Expand matches in one directory of $PATH.
+ ret = expand_wildcards(1, &buf, num_file, file, flags);
+ if (ret == OK) {
+ ga_grow(&ga, *num_file);
+ {
+ for (i = 0; i < *num_file; i++) {
+ char_u *name = (char_u *)(*file)[i];
+
+ if (STRLEN(name) > l) {
+ // Check if this name was already found.
+ hash_T hash = hash_hash(name + l);
+ hashitem_T *hi =
+ hash_lookup(&found_ht, (const char *)(name + l),
+ STRLEN(name + l), hash);
+ if (HASHITEM_EMPTY(hi)) {
+ // Remove the path that was prepended.
+ STRMOVE(name, name + l);
+ ((char_u **)ga.ga_data)[ga.ga_len++] = name;
+ hash_add_item(&found_ht, hi, name, hash);
+ name = NULL;
+ }
+ }
+ xfree(name);
+ }
+ xfree(*file);
+ }
+ }
+ if (*e != NUL) {
+ e++;
+ }
+ }
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+
+ xfree(buf);
+ xfree(pat);
+ if (mustfree) {
+ xfree(path);
+ }
+ hash_clear(&found_ht);
+}
+
+/// Call "user_expand_func()" to invoke a user defined Vim script function and
+/// return the result (either a string, a List or NULL).
+static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file,
+ char ***file)
+ FUNC_ATTR_NONNULL_ALL
+{
+ CmdlineInfo *const ccline = get_cmdline_info();
+ char_u keep = 0;
+ typval_T args[4];
+ char_u *pat = NULL;
+ const sctx_T save_current_sctx = current_sctx;
+
+ if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
+ return NULL;
+ }
+ *num_file = 0;
+ *file = NULL;
+
+ if (ccline->cmdbuff != NULL) {
+ keep = ccline->cmdbuff[ccline->cmdlen];
+ ccline->cmdbuff[ccline->cmdlen] = 0;
+ }
+
+ pat = vim_strnsave((char_u *)xp->xp_pattern, xp->xp_pattern_len);
+ args[0].v_type = VAR_STRING;
+ args[1].v_type = VAR_STRING;
+ args[2].v_type = VAR_NUMBER;
+ args[3].v_type = VAR_UNKNOWN;
+ args[0].vval.v_string = (char *)pat;
+ args[1].vval.v_string = xp->xp_line;
+ args[2].vval.v_number = xp->xp_col;
+
+ current_sctx = xp->xp_script_ctx;
+
+ void *const ret = user_expand_func((char_u *)xp->xp_arg, 3, args);
+
+ current_sctx = save_current_sctx;
+ if (ccline->cmdbuff != NULL) {
+ ccline->cmdbuff[ccline->cmdlen] = keep;
+ }
+
+ xfree(pat);
+ return ret;
+}
+
+/// Expand names with a function defined by the user.
+static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
+{
+ char_u *e;
+ garray_T ga;
+
+ char_u *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file,
+ file);
+
+ if (retstr == NULL) {
+ return FAIL;
+ }
+
+ ga_init(&ga, (int)sizeof(char *), 3);
+ for (char_u *s = retstr; *s != NUL; s = e) {
+ e = (char_u *)vim_strchr((char *)s, '\n');
+ if (e == NULL) {
+ e = s + STRLEN(s);
+ }
+ const char_u keep = *e;
+ *e = NUL;
+
+ const bool skip = xp->xp_pattern[0]
+ && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0;
+ *e = keep;
+ if (!skip) {
+ GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
+ }
+
+ if (*e != NUL) {
+ e++;
+ }
+ }
+ xfree(retstr);
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
+/// Expand names with a list returned by a function defined by the user.
+static int ExpandUserList(expand_T *xp, int *num_file, char ***file)
+{
+ list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file,
+ file);
+ if (retlist == NULL) {
+ return FAIL;
+ }
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char *), 3);
+ // Loop over the items in the list.
+ TV_LIST_ITER_CONST(retlist, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
+ || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
+ continue; // Skip non-string items and empty strings.
+ }
+
+ GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
+ });
+ tv_list_unref(retlist);
+
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
+static int ExpandUserLua(expand_T *xp, int *num_file, char ***file)
+{
+ typval_T rettv;
+ nlua_call_user_expand_func(xp, &rettv);
+ if (rettv.v_type != VAR_LIST) {
+ tv_clear(&rettv);
+ return FAIL;
+ }
+
+ list_T *const retlist = rettv.vval.v_list;
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char *), 3);
+ // Loop over the items in the list.
+ TV_LIST_ITER_CONST(retlist, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
+ || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
+ continue; // Skip non-string items and empty strings.
+ }
+
+ GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
+ });
+ tv_list_unref(retlist);
+
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
+/// Expand `file` for all comma-separated directories in `path`.
+/// Adds matches to `ga`.
+void globpath(char *path, char *file, garray_T *ga, int expand_options)
+{
+ expand_T xpc;
+ ExpandInit(&xpc);
+ xpc.xp_context = EXPAND_FILES;
+
+ char_u *buf = xmalloc(MAXPATHL);
+
+ // Loop over all entries in {path}.
+ while (*path != NUL) {
+ // Copy one item of the path to buf[] and concatenate the file name.
+ copy_option_part(&path, (char *)buf, MAXPATHL, ",");
+ if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
+ add_pathsep((char *)buf);
+ STRCAT(buf, file); // NOLINT
+
+ char **p;
+ int num_p = 0;
+ (void)ExpandFromContext(&xpc, buf, &num_p, &p,
+ WILD_SILENT | expand_options);
+ if (num_p > 0) {
+ ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
+
+ // Concatenate new results to previous ones.
+ ga_grow(ga, num_p);
+ // take over the pointers and put them in "ga"
+ for (int i = 0; i < num_p; i++) {
+ ((char_u **)ga->ga_data)[ga->ga_len] = (char_u *)p[i];
+ ga->ga_len++;
+ }
+ xfree(p);
+ }
+ }
+ }
+
+ xfree(buf);
+}
+
+/// Translate some keys pressed when 'wildmenu' is used.
+int wildmenu_translate_key(CmdlineInfo *cclp, int key, expand_T *xp, int did_wild_list)
+{
+ int c = key;
+
+ if (did_wild_list) {
+ if (c == K_LEFT) {
+ c = Ctrl_P;
+ } else if (c == K_RIGHT) {
+ c = Ctrl_N;
+ }
+ }
+
+ // Hitting CR after "emenu Name.": complete submenu
+ if (xp->xp_context == EXPAND_MENUNAMES
+ && cclp->cmdpos > 1
+ && cclp->cmdbuff[cclp->cmdpos - 1] == '.'
+ && cclp->cmdbuff[cclp->cmdpos - 2] != '\\'
+ && (c == '\n' || c == '\r' || c == K_KENTER)) {
+ c = K_DOWN;
+ }
+
+ return c;
+}
+
+/// Delete characters on the command line, from "from" to the current position.
+static void cmdline_del(CmdlineInfo *cclp, int from)
+{
+ assert(cclp->cmdpos <= cclp->cmdlen);
+ memmove(cclp->cmdbuff + from, cclp->cmdbuff + cclp->cmdpos,
+ (size_t)cclp->cmdlen - (size_t)cclp->cmdpos + 1);
+ cclp->cmdlen -= cclp->cmdpos - from;
+ cclp->cmdpos = from;
+}
+
+/// Handle a key pressed when wild menu is displayed
+int wildmenu_process_key(CmdlineInfo *cclp, int key, expand_T *xp)
+{
+ int c = key;
+
+ // Special translations for 'wildmenu'
+ if (xp->xp_context == EXPAND_MENUNAMES) {
+ // Hitting <Down> after "emenu Name.": complete submenu
+ if (c == K_DOWN && cclp->cmdpos > 0
+ && cclp->cmdbuff[cclp->cmdpos - 1] == '.') {
+ c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ } else if (c == K_UP) {
+ // Hitting <Up>: Remove one submenu name in front of the
+ // cursor
+ int found = false;
+
+ int j = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff);
+ int i = 0;
+ while (--j > 0) {
+ // check for start of menu name
+ if (cclp->cmdbuff[j] == ' '
+ && cclp->cmdbuff[j - 1] != '\\') {
+ i = j + 1;
+ break;
+ }
+
+ // check for start of submenu name
+ if (cclp->cmdbuff[j] == '.'
+ && cclp->cmdbuff[j - 1] != '\\') {
+ if (found) {
+ i = j + 1;
+ break;
+ } else {
+ found = true;
+ }
+ }
+ }
+ if (i > 0) {
+ cmdline_del(cclp, i);
+ }
+ c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ xp->xp_context = EXPAND_NOTHING;
+ }
+ }
+ if (xp->xp_context == EXPAND_FILES
+ || xp->xp_context == EXPAND_DIRECTORIES
+ || xp->xp_context == EXPAND_SHELLCMD) {
+ char_u upseg[5];
+
+ upseg[0] = PATHSEP;
+ upseg[1] = '.';
+ upseg[2] = '.';
+ upseg[3] = PATHSEP;
+ upseg[4] = NUL;
+
+ if (c == K_DOWN
+ && cclp->cmdpos > 0
+ && cclp->cmdbuff[cclp->cmdpos - 1] == PATHSEP
+ && (cclp->cmdpos < 3
+ || cclp->cmdbuff[cclp->cmdpos - 2] != '.'
+ || cclp->cmdbuff[cclp->cmdpos - 3] != '.')) {
+ // go down a directory
+ c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ } else if (STRNCMP(xp->xp_pattern, upseg + 1, 3) == 0
+ && c == K_DOWN) {
+ // If in a direct ancestor, strip off one ../ to go down
+ int found = false;
+
+ int j = cclp->cmdpos;
+ int i = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff);
+ while (--j > i) {
+ j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j);
+ if (vim_ispathsep(cclp->cmdbuff[j])) {
+ found = true;
+ break;
+ }
+ }
+ if (found
+ && cclp->cmdbuff[j - 1] == '.'
+ && cclp->cmdbuff[j - 2] == '.'
+ && (vim_ispathsep(cclp->cmdbuff[j - 3]) || j == i + 2)) {
+ cmdline_del(cclp, j - 2);
+ c = (int)p_wc;
+ KeyTyped = true; // in case the key was mapped
+ }
+ } else if (c == K_UP) {
+ // go up a directory
+ int found = false;
+
+ int j = cclp->cmdpos - 1;
+ int i = (int)((char_u *)xp->xp_pattern - cclp->cmdbuff);
+ while (--j > i) {
+ j -= utf_head_off(cclp->cmdbuff, cclp->cmdbuff + j);
+ if (vim_ispathsep(cclp->cmdbuff[j])
+#ifdef BACKSLASH_IN_FILENAME
+ && vim_strchr((const char_u *)" *?[{`$%#", cclp->cmdbuff[j + 1])
+ == NULL
+#endif
+ ) {
+ if (found) {
+ i = j + 1;
+ break;
+ } else {
+ found = true;
+ }
+ }
+ }
+
+ if (!found) {
+ j = i;
+ } else if (STRNCMP(cclp->cmdbuff + j, upseg, 4) == 0) {
+ j += 4;
+ } else if (STRNCMP(cclp->cmdbuff + j, upseg + 1, 3) == 0
+ && j == i) {
+ j += 3;
+ } else {
+ j = 0;
+ }
+
+ if (j > 0) {
+ // TODO(tarruda): this is only for DOS/Unix systems - need to put in
+ // machine-specific stuff here and in upseg init
+ cmdline_del(cclp, j);
+ put_on_cmdline(upseg + 1, 3, false);
+ } else if (cclp->cmdpos > i) {
+ cmdline_del(cclp, i);
+ }
+
+ // Now complete in the new directory. Set KeyTyped in case the
+ // Up key came from a mapping.
+ c = (int)p_wc;
+ KeyTyped = true;
+ }
+ }
+
+ return c;
+}
+
+/// Free expanded names when finished walking through the matches
+void wildmenu_cleanup(CmdlineInfo *cclp)
+{
+ if (!p_wmnu || wild_menu_showing == 0) {
+ return;
+ }
+
+ const bool skt = KeyTyped;
+ const int old_RedrawingDisabled = RedrawingDisabled;
+
+ if (cclp->input_fn) {
+ RedrawingDisabled = 0;
+ }
+
+ if (wild_menu_showing == WM_SCROLLED) {
+ // Entered command line, move it up
+ cmdline_row--;
+ redrawcmd();
+ wild_menu_showing = 0;
+ } else if (save_p_ls != -1) {
+ // restore 'laststatus' and 'winminheight'
+ p_ls = save_p_ls;
+ p_wmh = save_p_wmh;
+ last_status(false);
+ update_screen(UPD_VALID); // redraw the screen NOW
+ redrawcmd();
+ save_p_ls = -1;
+ wild_menu_showing = 0;
+ // don't redraw statusline if WM_LIST is showing
+ } else if (wild_menu_showing != WM_LIST) {
+ win_redraw_last_status(topframe);
+ wild_menu_showing = 0; // must be before redraw_statuslines #8385
+ redraw_statuslines();
+ } else {
+ wild_menu_showing = 0;
+ }
+ KeyTyped = skt;
+ if (cclp->input_fn) {
+ RedrawingDisabled = old_RedrawingDisabled;
+ }
+}
+
+/// "getcompletion()" function
+void f_getcompletion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char_u *pat;
+ expand_T xpc;
+ bool filtered = false;
+ int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
+ | WILD_NO_BEEP | WILD_HOME_REPLACE;
+
+ if (argvars[1].v_type != VAR_STRING) {
+ semsg(_(e_invarg2), "type must be a string");
+ return;
+ }
+ const char *const type = tv_get_string(&argvars[1]);
+
+ if (argvars[2].v_type != VAR_UNKNOWN) {
+ filtered = (bool)tv_get_number_chk(&argvars[2], NULL);
+ }
+
+ if (p_wic) {
+ options |= WILD_ICASE;
+ }
+
+ // For filtered results, 'wildignore' is used
+ if (!filtered) {
+ options |= WILD_KEEP_ALL;
+ }
+
+ if (argvars[0].v_type != VAR_STRING) {
+ emsg(_(e_invarg));
+ return;
+ }
+ const char *pattern = tv_get_string(&argvars[0]);
+
+ if (strcmp(type, "cmdline") == 0) {
+ set_one_cmd_context(&xpc, pattern);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_col = (int)STRLEN(pattern);
+ goto theend;
+ }
+
+ ExpandInit(&xpc);
+ xpc.xp_pattern = (char *)pattern;
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ xpc.xp_context = cmdcomplete_str_to_type(type);
+ if (xpc.xp_context == EXPAND_NOTHING) {
+ semsg(_(e_invarg2), type);
+ return;
+ }
+
+ if (xpc.xp_context == EXPAND_MENUS) {
+ set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ }
+
+ if (xpc.xp_context == EXPAND_CSCOPE) {
+ set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ }
+
+ if (xpc.xp_context == EXPAND_SIGN) {
+ set_context_in_sign_cmd(&xpc, (char_u *)xpc.xp_pattern);
+ xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
+ }
+
+theend:
+ pat = addstar((char_u *)xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
+ ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
+ tv_list_alloc_ret(rettv, xpc.xp_numfiles);
+
+ for (int i = 0; i < xpc.xp_numfiles; i++) {
+ tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
+ -1);
+ }
+ xfree(pat);
+ ExpandCleanup(&xpc);
+}
diff --git a/src/nvim/cmdexpand.h b/src/nvim/cmdexpand.h
new file mode 100644
index 0000000000..93e91af169
--- /dev/null
+++ b/src/nvim/cmdexpand.h
@@ -0,0 +1,44 @@
+#ifndef NVIM_CMDEXPAND_H
+#define NVIM_CMDEXPAND_H
+
+#include "nvim/eval/typval.h"
+#include "nvim/ex_getln.h"
+#include "nvim/garray.h"
+#include "nvim/types.h"
+
+// Values for nextwild() and ExpandOne(). See ExpandOne() for meaning.
+
+enum {
+ WILD_FREE = 1,
+ WILD_EXPAND_FREE = 2,
+ WILD_EXPAND_KEEP = 3,
+ WILD_NEXT = 4,
+ WILD_PREV = 5,
+ WILD_ALL = 6,
+ WILD_LONGEST = 7,
+ WILD_ALL_KEEP = 8,
+ WILD_CANCEL = 9,
+ WILD_APPLY = 10,
+};
+
+enum {
+ WILD_LIST_NOTFOUND = 0x01,
+ WILD_HOME_REPLACE = 0x02,
+ WILD_USE_NL = 0x04,
+ WILD_NO_BEEP = 0x08,
+ WILD_ADD_SLASH = 0x10,
+ WILD_KEEP_ALL = 0x20,
+ WILD_SILENT = 0x40,
+ WILD_ESCAPE = 0x80,
+ WILD_ICASE = 0x100,
+ WILD_ALLLINKS = 0x200,
+ WILD_IGNORE_COMPLETESLASH = 0x400,
+ WILD_NOERROR = 0x800, ///< sets EW_NOERROR
+ WILD_BUFLASTUSED = 0x1000,
+ BUF_DIFF_FILTER = 0x2000,
+};
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "cmdexpand.h.generated.h"
+#endif
+#endif // NVIM_CMDEXPAND_H
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
new file mode 100644
index 0000000000..1426054d0b
--- /dev/null
+++ b/src/nvim/cmdhist.c
@@ -0,0 +1,744 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// cmdhist.c: Functions for the history of the command-line.
+
+#include "nvim/ascii.h"
+#include "nvim/charset.h"
+#include "nvim/cmdhist.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_getln.h"
+#include "nvim/regexp.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "cmdhist.c.generated.h"
+#endif
+
+static histentry_T *(history[HIST_COUNT]) = { NULL, NULL, NULL, NULL, NULL };
+static int hisidx[HIST_COUNT] = { -1, -1, -1, -1, -1 }; ///< lastused entry
+/// identifying (unique) number of newest history entry
+static int hisnum[HIST_COUNT] = { 0, 0, 0, 0, 0 };
+static int hislen = 0; ///< actual length of history tables
+
+/// Return the length of the history tables
+int get_hislen(void)
+{
+ return hislen;
+}
+
+/// Return a pointer to a specified history table
+histentry_T *get_histentry(int hist_type)
+{
+ return history[hist_type];
+}
+
+void set_histentry(int hist_type, histentry_T *entry)
+{
+ history[hist_type] = entry;
+}
+
+int *get_hisidx(int hist_type)
+{
+ return &hisidx[hist_type];
+}
+
+int *get_hisnum(int hist_type)
+{
+ return &hisnum[hist_type];
+}
+
+/// Translate a history character to the associated type number
+HistoryType hist_char2type(const int c)
+ FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ switch (c) {
+ case ':':
+ return HIST_CMD;
+ case '=':
+ return HIST_EXPR;
+ case '@':
+ return HIST_INPUT;
+ case '>':
+ return HIST_DEBUG;
+ case NUL:
+ case '/':
+ case '?':
+ return HIST_SEARCH;
+ default:
+ return HIST_INVALID;
+ }
+ // Silence -Wreturn-type
+ return 0;
+}
+
+/// Table of history names.
+/// These names are used in :history and various hist...() functions.
+/// It is sufficient to give the significant prefix of a history name.
+static char *(history_names[]) = {
+ "cmd",
+ "search",
+ "expr",
+ "input",
+ "debug",
+ NULL
+};
+
+/// Function given to ExpandGeneric() to obtain the possible first
+/// arguments of the ":history command.
+char *get_history_arg(expand_T *xp, int idx)
+{
+ const char *short_names = ":=@>?/";
+ const int short_names_count = (int)STRLEN(short_names);
+ const int history_name_count = ARRAY_SIZE(history_names) - 1;
+
+ if (idx < short_names_count) {
+ xp->xp_buf[0] = short_names[idx];
+ xp->xp_buf[1] = NUL;
+ return xp->xp_buf;
+ }
+ if (idx < short_names_count + history_name_count) {
+ return history_names[idx - short_names_count];
+ }
+ if (idx == short_names_count + history_name_count) {
+ return "all";
+ }
+ return NULL;
+}
+
+/// Initialize command line history.
+/// Also used to re-allocate history tables when size changes.
+void init_history(void)
+{
+ assert(p_hi >= 0 && p_hi <= INT_MAX);
+ int newlen = (int)p_hi;
+ int oldlen = hislen;
+
+ // If history tables size changed, reallocate them.
+ // Tables are circular arrays (current position marked by hisidx[type]).
+ // On copying them to the new arrays, we take the chance to reorder them.
+ if (newlen != oldlen) {
+ for (int type = 0; type < HIST_COUNT; type++) {
+ histentry_T *temp = (newlen
+ ? xmalloc((size_t)newlen * sizeof(*temp))
+ : NULL);
+
+ int j = hisidx[type];
+ if (j >= 0) {
+ // old array gets partitioned this way:
+ // [0 , i1 ) --> newest entries to be deleted
+ // [i1 , i1 + l1) --> newest entries to be copied
+ // [i1 + l1 , i2 ) --> oldest entries to be deleted
+ // [i2 , i2 + l2) --> oldest entries to be copied
+ int l1 = MIN(j + 1, newlen); // how many newest to copy
+ int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy
+ int i1 = j + 1 - l1; // copy newest from here
+ int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here
+
+ // copy as much entries as they fit to new table, reordering them
+ if (newlen) {
+ // copy oldest entries
+ memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
+ // copy newest entries
+ memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
+ }
+
+ // delete entries that don't fit in newlen, if any
+ for (int i = 0; i < i1; i++) {
+ hist_free_entry(history[type] + i);
+ }
+ for (int i = i1 + l1; i < i2; i++) {
+ hist_free_entry(history[type] + i);
+ }
+ }
+
+ // clear remaining space, if any
+ int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries
+ if (newlen) {
+ memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
+ }
+
+ hisidx[type] = l3 - 1;
+ xfree(history[type]);
+ history[type] = temp;
+ }
+ hislen = newlen;
+ }
+}
+
+static inline void hist_free_entry(histentry_T *hisptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ xfree(hisptr->hisstr);
+ tv_list_unref(hisptr->additional_elements);
+ clear_hist_entry(hisptr);
+}
+
+static inline void clear_hist_entry(histentry_T *hisptr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ CLEAR_POINTER(hisptr);
+}
+
+/// Check if command line 'str' is already in history.
+/// If 'move_to_front' is true, matching entry is moved to end of history.
+///
+/// @param move_to_front Move the entry to the front if it exists
+static int in_history(int type, char_u *str, int move_to_front, int sep)
+{
+ int last_i = -1;
+
+ if (hisidx[type] < 0) {
+ return false;
+ }
+ int i = hisidx[type];
+ do {
+ if (history[type][i].hisstr == NULL) {
+ return false;
+ }
+
+ // For search history, check that the separator character matches as
+ // well.
+ char_u *p = history[type][i].hisstr;
+ if (STRCMP(str, p) == 0
+ && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) {
+ if (!move_to_front) {
+ return true;
+ }
+ last_i = i;
+ break;
+ }
+ if (--i < 0) {
+ i = hislen - 1;
+ }
+ } while (i != hisidx[type]);
+
+ if (last_i >= 0) {
+ list_T *const list = history[type][i].additional_elements;
+ str = history[type][i].hisstr;
+ while (i != hisidx[type]) {
+ if (++i >= hislen) {
+ i = 0;
+ }
+ history[type][last_i] = history[type][i];
+ last_i = i;
+ }
+ tv_list_unref(list);
+ history[type][i].hisnum = ++hisnum[type];
+ history[type][i].hisstr = str;
+ history[type][i].timestamp = os_time();
+ history[type][i].additional_elements = NULL;
+ return true;
+ }
+ return false;
+}
+
+/// Convert history name to its HIST_ equivalent
+///
+/// Names are taken from the table above. When `name` is empty returns currently
+/// active history or HIST_DEFAULT, depending on `return_default` argument.
+///
+/// @param[in] name Converted name.
+/// @param[in] len Name length.
+/// @param[in] return_default Determines whether HIST_DEFAULT should be
+/// returned or value based on `ccline.cmdfirstc`.
+///
+/// @return Any value from HistoryType enum, including HIST_INVALID. May not
+/// return HIST_DEFAULT unless return_default is true.
+static HistoryType get_histtype(const char *const name, const size_t len, const bool return_default)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // No argument: use current history.
+ if (len == 0) {
+ return return_default ? HIST_DEFAULT : hist_char2type(get_cmdline_firstc());
+ }
+
+ for (HistoryType i = 0; history_names[i] != NULL; i++) {
+ if (STRNICMP(name, history_names[i], len) == 0) {
+ return i;
+ }
+ }
+
+ if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) {
+ return hist_char2type(name[0]);
+ }
+
+ return HIST_INVALID;
+}
+
+static int last_maptick = -1; // last seen maptick
+
+/// Add the given string to the given history. If the string is already in the
+/// history then it is moved to the front.
+///
+/// @param histype may be one of the HIST_ values.
+/// @param in_map consider maptick when inside a mapping
+/// @param sep separator character used (search hist)
+void add_to_history(int histype, char_u *new_entry, int in_map, int sep)
+{
+ histentry_T *hisptr;
+
+ if (hislen == 0 || histype == HIST_INVALID) { // no history
+ return;
+ }
+ assert(histype != HIST_DEFAULT);
+
+ if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH) {
+ return;
+ }
+
+ // Searches inside the same mapping overwrite each other, so that only
+ // the last line is kept. Be careful not to remove a line that was moved
+ // down, only lines that were added.
+ if (histype == HIST_SEARCH && in_map) {
+ if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) {
+ // Current line is from the same mapping, remove it
+ hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]];
+ hist_free_entry(hisptr);
+ hisnum[histype]--;
+ if (--hisidx[HIST_SEARCH] < 0) {
+ hisidx[HIST_SEARCH] = hislen - 1;
+ }
+ }
+ last_maptick = -1;
+ }
+ if (!in_history(histype, new_entry, true, sep)) {
+ if (++hisidx[histype] == hislen) {
+ hisidx[histype] = 0;
+ }
+ hisptr = &history[histype][hisidx[histype]];
+ hist_free_entry(hisptr);
+
+ // Store the separator after the NUL of the string.
+ size_t len = STRLEN(new_entry);
+ hisptr->hisstr = vim_strnsave(new_entry, len + 2);
+ hisptr->timestamp = os_time();
+ hisptr->additional_elements = NULL;
+ hisptr->hisstr[len + 1] = (char_u)sep;
+
+ hisptr->hisnum = ++hisnum[histype];
+ if (histype == HIST_SEARCH && in_map) {
+ last_maptick = maptick;
+ }
+ }
+}
+
+/// Get identifier of newest history entry.
+///
+/// @param histype may be one of the HIST_ values.
+static int get_history_idx(int histype)
+{
+ if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
+ || hisidx[histype] < 0) {
+ return -1;
+ }
+
+ return history[histype][hisidx[histype]].hisnum;
+}
+
+/// Calculate history index from a number:
+///
+/// @param num > 0: seen as identifying number of a history entry
+/// < 0: relative position in history wrt newest entry
+/// @param histype may be one of the HIST_ values.
+static int calc_hist_idx(int histype, int num)
+{
+ int i;
+ int wrapped = false;
+
+ if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
+ || (i = hisidx[histype]) < 0 || num == 0) {
+ return -1;
+ }
+
+ histentry_T *hist = history[histype];
+ if (num > 0) {
+ while (hist[i].hisnum > num) {
+ if (--i < 0) {
+ if (wrapped) {
+ break;
+ }
+ i += hislen;
+ wrapped = true;
+ }
+ }
+ if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) {
+ return i;
+ }
+ } else if (-num <= hislen) {
+ i += num + 1;
+ if (i < 0) {
+ i += hislen;
+ }
+ if (hist[i].hisstr != NULL) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/// Get a history entry by its index.
+///
+/// @param histype may be one of the HIST_ values.
+static char_u *get_history_entry(int histype, int idx)
+{
+ idx = calc_hist_idx(histype, idx);
+ if (idx >= 0) {
+ return history[histype][idx].hisstr;
+ } else {
+ return (char_u *)"";
+ }
+}
+
+/// Clear all entries in a history
+///
+/// @param[in] histype One of the HIST_ values.
+///
+/// @return OK if there was something to clean and histype was one of HIST_
+/// values, FAIL otherwise.
+int clr_history(const int histype)
+{
+ if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) {
+ histentry_T *hisptr = history[histype];
+ for (int i = hislen; i--; hisptr++) {
+ hist_free_entry(hisptr);
+ }
+ hisidx[histype] = -1; // mark history as cleared
+ hisnum[histype] = 0; // reset identifier counter
+ return OK;
+ }
+ return FAIL;
+}
+
+/// Remove all entries matching {str} from a history.
+///
+/// @param histype may be one of the HIST_ values.
+static int del_history_entry(int histype, char_u *str)
+{
+ regmatch_T regmatch;
+ histentry_T *hisptr;
+ int idx;
+ int i;
+ int last;
+ bool found = false;
+
+ regmatch.regprog = NULL;
+ regmatch.rm_ic = false; // always match case
+ if (hislen != 0
+ && histype >= 0
+ && histype < HIST_COUNT
+ && *str != NUL
+ && (idx = hisidx[histype]) >= 0
+ && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING)) != NULL) {
+ i = last = idx;
+ do {
+ hisptr = &history[histype][i];
+ if (hisptr->hisstr == NULL) {
+ break;
+ }
+ if (vim_regexec(&regmatch, (char *)hisptr->hisstr, (colnr_T)0)) {
+ found = true;
+ hist_free_entry(hisptr);
+ } else {
+ if (i != last) {
+ history[histype][last] = *hisptr;
+ clear_hist_entry(hisptr);
+ }
+ if (--last < 0) {
+ last += hislen;
+ }
+ }
+ if (--i < 0) {
+ i += hislen;
+ }
+ } while (i != idx);
+ if (history[histype][idx].hisstr == NULL) {
+ hisidx[histype] = -1;
+ }
+ }
+ vim_regfree(regmatch.regprog);
+ return found;
+}
+
+/// Remove an indexed entry from a history.
+///
+/// @param histype may be one of the HIST_ values.
+static int del_history_idx(int histype, int idx)
+{
+ int i = calc_hist_idx(histype, idx);
+ if (i < 0) {
+ return false;
+ }
+ idx = hisidx[histype];
+ hist_free_entry(&history[histype][i]);
+
+ // When deleting the last added search string in a mapping, reset
+ // last_maptick, so that the last added search string isn't deleted again.
+ if (histype == HIST_SEARCH && maptick == last_maptick && i == idx) {
+ last_maptick = -1;
+ }
+
+ while (i != idx) {
+ int j = (i + 1) % hislen;
+ history[histype][i] = history[histype][j];
+ i = j;
+ }
+ clear_hist_entry(&history[histype][idx]);
+ if (--i < 0) {
+ i += hislen;
+ }
+ hisidx[histype] = i;
+ return true;
+}
+
+/// "histadd()" function
+void f_histadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ HistoryType histype;
+
+ rettv->vval.v_number = false;
+ if (check_secure()) {
+ return;
+ }
+ const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error
+ histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID;
+ if (histype != HIST_INVALID) {
+ char buf[NUMBUFLEN];
+ str = tv_get_string_buf(&argvars[1], buf);
+ if (*str != NUL) {
+ init_history();
+ add_to_history(histype, (char_u *)str, false, NUL);
+ rettv->vval.v_number = true;
+ return;
+ }
+ }
+}
+
+/// "histdel()" function
+void f_histdel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int n;
+ const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
+ if (str == NULL) {
+ n = 0;
+ } else if (argvars[1].v_type == VAR_UNKNOWN) {
+ // only one argument: clear entire history
+ n = clr_history(get_histtype(str, strlen(str), false));
+ } else if (argvars[1].v_type == VAR_NUMBER) {
+ // index given: remove that entry
+ n = del_history_idx(get_histtype(str, strlen(str), false),
+ (int)tv_get_number(&argvars[1]));
+ } else {
+ // string given: remove all matching entries
+ char buf[NUMBUFLEN];
+ n = del_history_entry(get_histtype(str, strlen(str), false),
+ (char_u *)tv_get_string_buf(&argvars[1], buf));
+ }
+ rettv->vval.v_number = n;
+}
+
+/// "histget()" function
+void f_histget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ HistoryType type;
+ int idx;
+
+ const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
+ if (str == NULL) {
+ rettv->vval.v_string = NULL;
+ } else {
+ type = get_histtype(str, strlen(str), false);
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ idx = get_history_idx(type);
+ } else {
+ idx = (int)tv_get_number_chk(&argvars[1], NULL);
+ }
+ // -1 on type error
+ rettv->vval.v_string = (char *)vim_strsave(get_history_entry(type, idx));
+ }
+ rettv->v_type = VAR_STRING;
+}
+
+/// "histnr()" function
+void f_histnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const char *const histname = tv_get_string_chk(&argvars[0]);
+ HistoryType i = histname == NULL
+ ? HIST_INVALID
+ : get_histtype(histname, strlen(histname), false);
+ if (i != HIST_INVALID) {
+ i = get_history_idx(i);
+ }
+ rettv->vval.v_number = i;
+}
+
+/// :history command - print a history
+void ex_history(exarg_T *eap)
+{
+ histentry_T *hist;
+ int histype1 = HIST_CMD;
+ int histype2 = HIST_CMD;
+ int hisidx1 = 1;
+ int hisidx2 = -1;
+ int idx;
+ int i, j, k;
+ char *end;
+ char_u *arg = (char_u *)eap->arg;
+
+ if (hislen == 0) {
+ msg(_("'history' option is zero"));
+ return;
+ }
+
+ if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) {
+ end = (char *)arg;
+ while (ASCII_ISALPHA(*end)
+ || vim_strchr(":=@>/?", *end) != NULL) {
+ end++;
+ }
+ histype1 = get_histtype((const char *)arg, (size_t)(end - (char *)arg), false);
+ if (histype1 == HIST_INVALID) {
+ if (STRNICMP(arg, "all", end - (char *)arg) == 0) {
+ histype1 = 0;
+ histype2 = HIST_COUNT - 1;
+ } else {
+ semsg(_(e_trailing_arg), arg);
+ return;
+ }
+ } else {
+ histype2 = histype1;
+ }
+ } else {
+ end = (char *)arg;
+ }
+ if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) {
+ semsg(_(e_trailing_arg), end);
+ return;
+ }
+
+ for (; !got_int && histype1 <= histype2; histype1++) {
+ STRCPY(IObuff, "\n # ");
+ assert(history_names[histype1] != NULL);
+ STRCAT(STRCAT(IObuff, history_names[histype1]), " history");
+ msg_puts_title((char *)IObuff);
+ idx = hisidx[histype1];
+ hist = history[histype1];
+ j = hisidx1;
+ k = hisidx2;
+ if (j < 0) {
+ j = (-j > hislen) ? 0 : hist[(hislen + j + idx + 1) % hislen].hisnum;
+ }
+ if (k < 0) {
+ k = (-k > hislen) ? 0 : hist[(hislen + k + idx + 1) % hislen].hisnum;
+ }
+ if (idx >= 0 && j <= k) {
+ for (i = idx + 1; !got_int; i++) {
+ if (i == hislen) {
+ i = 0;
+ }
+ if (hist[i].hisstr != NULL
+ && hist[i].hisnum >= j && hist[i].hisnum <= k) {
+ msg_putchar('\n');
+ snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
+ hist[i].hisnum);
+ if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) {
+ trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff),
+ Columns - 10, IOSIZE - (int)STRLEN(IObuff));
+ } else {
+ STRCAT(IObuff, hist[i].hisstr);
+ }
+ msg_outtrans((char *)IObuff);
+ ui_flush();
+ }
+ if (i == idx) {
+ break;
+ }
+ }
+ }
+ }
+}
+
+/// Iterate over history items
+///
+/// @warning No history-editing functions must be run while iteration is in
+/// progress.
+///
+/// @param[in] iter Pointer to the last history entry.
+/// @param[in] history_type Type of the history (HIST_*). Ignored if iter
+/// parameter is not NULL.
+/// @param[in] zero If true then zero (but not free) returned items.
+///
+/// @warning When using this parameter user is
+/// responsible for calling clr_history()
+/// itself after iteration is over. If
+/// clr_history() is not called behaviour is
+/// undefined. No functions that work with
+/// history must be called during iteration
+/// in this case.
+/// @param[out] hist Next history entry.
+///
+/// @return Pointer used in next iteration or NULL to indicate that iteration
+/// was finished.
+const void *hist_iter(const void *const iter, const uint8_t history_type, const bool zero,
+ histentry_T *const hist)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(4)
+{
+ *hist = (histentry_T) {
+ .hisstr = NULL
+ };
+ if (hisidx[history_type] == -1) {
+ return NULL;
+ }
+ histentry_T *const hstart = &(history[history_type][0]);
+ histentry_T *const hlast = &(history[history_type][hisidx[history_type]]);
+ const histentry_T *const hend = &(history[history_type][hislen - 1]);
+ histentry_T *hiter;
+ if (iter == NULL) {
+ histentry_T *hfirst = hlast;
+ do {
+ hfirst++;
+ if (hfirst > hend) {
+ hfirst = hstart;
+ }
+ if (hfirst->hisstr != NULL) {
+ break;
+ }
+ } while (hfirst != hlast);
+ hiter = hfirst;
+ } else {
+ hiter = (histentry_T *)iter;
+ }
+ if (hiter == NULL) {
+ return NULL;
+ }
+ *hist = *hiter;
+ if (zero) {
+ CLEAR_POINTER(hiter);
+ }
+ if (hiter == hlast) {
+ return NULL;
+ }
+ hiter++;
+ return (const void *)((hiter > hend) ? hstart : hiter);
+}
+
+/// Get array of history items
+///
+/// @param[in] history_type Type of the history to get array for.
+/// @param[out] new_hisidx Location where last index in the new array should
+/// be saved.
+/// @param[out] new_hisnum Location where last history number in the new
+/// history should be saved.
+///
+/// @return Pointer to the array or NULL.
+histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx,
+ int **const new_hisnum)
+ FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
+{
+ init_history();
+ *new_hisidx = &(hisidx[history_type]);
+ *new_hisnum = &(hisnum[history_type]);
+ return history[history_type];
+}
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
new file mode 100644
index 0000000000..797b79a5f0
--- /dev/null
+++ b/src/nvim/cmdhist.h
@@ -0,0 +1,33 @@
+#ifndef NVIM_CMDHIST_H
+#define NVIM_CMDHIST_H
+
+#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/os/time.h"
+
+/// Present history tables
+typedef enum {
+ HIST_DEFAULT = -2, ///< Default (current) history.
+ HIST_INVALID = -1, ///< Unknown history.
+ HIST_CMD = 0, ///< Colon commands.
+ HIST_SEARCH, ///< Search commands.
+ HIST_EXPR, ///< Expressions (e.g. from entering = register).
+ HIST_INPUT, ///< input() lines.
+ HIST_DEBUG, ///< Debug commands.
+} HistoryType;
+
+/// Number of history tables
+#define HIST_COUNT (HIST_DEBUG + 1)
+
+/// History entry definition
+typedef struct hist_entry {
+ int hisnum; ///< Entry identifier number.
+ char_u *hisstr; ///< Actual entry, separator char after the NUL.
+ Timestamp timestamp; ///< Time when entry was added.
+ list_T *additional_elements; ///< Additional entries from ShaDa file.
+} histentry_T;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "cmdhist.h.generated.h"
+#endif
+#endif // NVIM_CMDHIST_H
diff --git a/src/nvim/context.c b/src/nvim/context.c
index db26667009..34692cdf64 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -9,6 +9,7 @@
#include "nvim/api/vimscript.h"
#include "nvim/context.h"
#include "nvim/eval/encode.h"
+#include "nvim/eval/userfunc.h"
#include "nvim/ex_docmd.h"
#include "nvim/option.h"
#include "nvim/shada.h"
@@ -249,7 +250,7 @@ static inline void ctx_save_funcs(Context *ctx, bool scriptonly)
ctx->funcs = (Array)ARRAY_DICT_INIT;
Error err = ERROR_INIT;
- HASHTAB_ITER(&func_hashtab, hi, {
+ HASHTAB_ITER(func_tbl_get(), hi, {
const char_u *const name = hi->hi_key;
bool islambda = (STRNCMP(name, "<lambda>", 8) == 0);
bool isscript = (name[0] == K_SPECIAL);
@@ -344,7 +345,7 @@ Dictionary ctx_to_dict(Context *ctx)
PUT(rv, "jumps", ARRAY_OBJ(sbuf_to_array(ctx->jumps)));
PUT(rv, "bufs", ARRAY_OBJ(sbuf_to_array(ctx->bufs)));
PUT(rv, "gvars", ARRAY_OBJ(sbuf_to_array(ctx->gvars)));
- PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs)));
+ PUT(rv, "funcs", ARRAY_OBJ(copy_array(ctx->funcs, NULL)));
return rv;
}
@@ -380,7 +381,7 @@ int ctx_from_dict(Dictionary dict, Context *ctx)
ctx->gvars = array_to_sbuf(item.value.data.array);
} else if (strequal(item.key.data, "funcs")) {
types |= kCtxFuncs;
- ctx->funcs = copy_object(item.value).data.array;
+ ctx->funcs = copy_object(item.value, NULL).data.array;
}
}
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 1446257f7e..ed0488cf76 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -9,6 +9,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/fold.h"
#include "nvim/mark.h"
@@ -17,7 +18,6 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/vim.h"
@@ -137,14 +137,18 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a
}
}
- char_u *ptr = line;
- while (col <= wcol && *ptr != NUL) {
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
+ while (cts.cts_vcol <= wcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- csize = win_lbr_chartabsize(curwin, line, ptr, col, &head);
- MB_PTR_ADV(ptr);
- col += csize;
+ csize = win_lbr_chartabsize(&cts, &head);
+ MB_PTR_ADV(cts.cts_ptr);
+ cts.cts_vcol += csize;
}
- idx = (int)(ptr - line);
+ col = cts.cts_vcol;
+ idx = (int)(cts.cts_ptr - (char *)line);
+ clear_chartabsize_arg(&cts);
+
// Handle all the special cases. The virtual_active() check
// is needed to ensure that a virtual position off the end of
// a line has the correct indexing. The one_more comparison
@@ -471,7 +475,7 @@ bool leftcol_changed(void)
if (retval) {
curwin->w_set_curswant = true;
}
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
return retval;
}
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 9c33b1a806..73ad99876a 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -120,7 +120,7 @@ char *parse_shape_opt(int what)
}
}
// Repeat for all comma separated parts.
- char *modep = (char *)p_guicursor;
+ char *modep = p_guicursor;
while (modep != NULL && *modep != NUL) {
colonp = vim_strchr(modep, ':');
commap = vim_strchr(modep, ',');
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index 0eaff06833..36df62b502 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -8,6 +8,7 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
@@ -17,7 +18,7 @@
#include "nvim/os/os.h"
#include "nvim/pos.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/types.h"
#include "nvim/vim.h"
@@ -46,7 +47,7 @@ struct debuggy {
/// Debug mode. Repeatedly get Ex commands, until told to continue normal
/// execution.
-void do_debug(char_u *cmd)
+void do_debug(char *cmd)
{
int save_msg_scroll = msg_scroll;
int save_State = State;
@@ -98,14 +99,17 @@ void do_debug(char_u *cmd)
xfree(debug_newval);
debug_newval = NULL;
}
- if (sourcing_name != NULL) {
- msg(sourcing_name);
+ char *sname = estack_sfile(ESTACK_NONE);
+ if (sname != NULL) {
+ msg(sname);
}
- if (sourcing_lnum != 0) {
- smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
+ xfree(sname);
+ if (SOURCING_LNUM != 0) {
+ smsg(_("line %" PRId64 ": %s"), (int64_t)SOURCING_LNUM, cmd);
} else {
smsg(_("cmd: %s"), cmd);
}
+
// Repeat getting a command and executing it.
for (;;) {
msg_scroll = true;
@@ -235,11 +239,11 @@ void do_debug(char_u *cmd)
last_cmd = CMD_STEP;
break;
case CMD_BACKTRACE:
- do_showbacktrace(cmd);
+ do_showbacktrace((char_u *)cmd);
continue;
case CMD_FRAME:
if (*p == NUL) {
- do_showbacktrace(cmd);
+ do_showbacktrace((char_u *)cmd);
} else {
p = skipwhite(p);
do_setdebugtracelevel((char_u *)p);
@@ -271,7 +275,7 @@ void do_debug(char_u *cmd)
RedrawingDisabled--;
no_wait_return--;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
need_wait_return = false;
msg_scroll = save_msg_scroll;
lines_left = Rows - 1;
@@ -287,12 +291,12 @@ void do_debug(char_u *cmd)
debug_did_msg = true;
}
-static int get_maxbacktrace_level(void)
+static int get_maxbacktrace_level(char *sname)
{
int maxbacktrace = 0;
- if (sourcing_name != NULL) {
- char *p = sourcing_name;
+ if (sname != NULL) {
+ char *p = sname;
char *q;
while ((q = strstr(p, "..")) != NULL) {
p = q + 2;
@@ -320,20 +324,24 @@ static void do_checkbacktracelevel(void)
debug_backtrace_level = 0;
msg(_("frame is zero"));
} else {
- int max = get_maxbacktrace_level();
+ char *sname = estack_sfile(ESTACK_NONE);
+ int max = get_maxbacktrace_level(sname);
+
if (debug_backtrace_level > max) {
debug_backtrace_level = max;
smsg(_("frame at highest level: %d"), max);
}
+ xfree(sname);
}
}
static void do_showbacktrace(char_u *cmd)
{
- if (sourcing_name != NULL) {
+ char *sname = estack_sfile(ESTACK_NONE);
+ int max = get_maxbacktrace_level(sname);
+ if (sname != NULL) {
int i = 0;
- int max = get_maxbacktrace_level();
- char *cur = sourcing_name;
+ char *cur = sname;
while (!got_int) {
char *next = strstr(cur, "..");
if (next != NULL) {
@@ -351,9 +359,11 @@ static void do_showbacktrace(char_u *cmd)
*next = '.';
cur = next + 2;
}
+ xfree(sname);
}
- if (sourcing_lnum != 0) {
- smsg(_("line %" PRId64 ": %s"), (int64_t)sourcing_lnum, cmd);
+
+ if (SOURCING_LNUM != 0) {
+ smsg(_("line %" PRId64 ": %s"), (int64_t)SOURCING_LNUM, cmd);
} else {
smsg(_("cmd: %s"), cmd);
}
@@ -404,7 +414,7 @@ void dbg_check_breakpoint(exarg_T *eap)
debug_breakpoint_name + (*p == NUL ? 0 : 3),
(int64_t)debug_breakpoint_lnum);
debug_breakpoint_name = NULL;
- do_debug((char_u *)eap->cmd);
+ do_debug(eap->cmd);
} else {
debug_skipped = true;
debug_skipped_name = debug_breakpoint_name;
@@ -412,7 +422,7 @@ void dbg_check_breakpoint(exarg_T *eap)
}
} else if (ex_nesting_level <= debug_break_level) {
if (!eap->skip) {
- do_debug((char_u *)eap->cmd);
+ do_debug(eap->cmd);
} else {
debug_skipped = true;
debug_skipped_name = NULL;
@@ -693,7 +703,7 @@ void ex_breaklist(exarg_T *eap)
smsg(_("%3d %s %s line %" PRId64),
bp->dbg_nr,
bp->dbg_type == DBG_FUNC ? "func" : "file",
- bp->dbg_type == DBG_FUNC ? bp->dbg_name : NameBuff,
+ bp->dbg_type == DBG_FUNC ? bp->dbg_name : (char_u *)NameBuff,
(int64_t)bp->dbg_lnum);
} else {
smsg(_("%3d expr %s"), bp->dbg_nr, bp->dbg_name);
@@ -708,9 +718,9 @@ void ex_breaklist(exarg_T *eap)
/// @param file true for a file, false for a function
/// @param fname file or function name
/// @param after after this line number
-linenr_T dbg_find_breakpoint(bool file, char_u *fname, linenr_T after)
+linenr_T dbg_find_breakpoint(bool file, char *fname, linenr_T after)
{
- return debuggy_find(file, fname, after, &dbg_breakp, NULL);
+ return debuggy_find(file, (char_u *)fname, after, &dbg_breakp, NULL);
}
/// @param file true for a file, false for a function
@@ -718,9 +728,9 @@ linenr_T dbg_find_breakpoint(bool file, char_u *fname, linenr_T after)
/// @param fp[out] forceit
///
/// @returns true if profiling is on for a function or sourced file.
-bool has_profiling(bool file, char_u *fname, bool *fp)
+bool has_profiling(bool file, char *fname, bool *fp)
{
- return debuggy_find(file, fname, (linenr_T)0, &prof_ga, fp)
+ return debuggy_find(file, (char_u *)fname, (linenr_T)0, &prof_ga, fp)
!= (linenr_T)0;
}
@@ -815,9 +825,9 @@ static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T
}
/// Called when a breakpoint was encountered.
-void dbg_breakpoint(char_u *name, linenr_T lnum)
+void dbg_breakpoint(char *name, linenr_T lnum)
{
// We need to check if this line is actually executed in do_one_cmd()
- debug_breakpoint_name = name;
+ debug_breakpoint_name = (char_u *)name;
debug_breakpoint_lnum = lnum;
}
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index e7c76fe38e..9f3e5a8638 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -4,12 +4,12 @@
#include "nvim/api/ui.h"
#include "nvim/buffer.h"
#include "nvim/decoration.h"
+#include "nvim/drawscreen.h"
#include "nvim/extmark.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
#include "nvim/move.h"
-#include "nvim/screen.h"
#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -358,7 +358,8 @@ next_mark:
return attr;
}
-void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs[])
+void decor_redraw_signs(buf_T *buf, int row, int *num_signs, SignTextAttrs sattrs[],
+ HlPriAttr *num_attrs, HlPriAttr *line_attrs, HlPriAttr *cul_attrs)
{
if (!buf->b_signs) {
return;
@@ -383,30 +384,37 @@ void decor_redraw_signs(buf_T *buf, int row, int *num_signs, sign_attrs_T sattrs
goto next_mark;
}
- int j;
- for (j = (*num_signs); j > 0; j--) {
- if (sattrs[j].sat_prio <= decor->priority) {
- break;
- }
- sattrs[j] = sattrs[j - 1];
- }
- if (j < SIGN_SHOW_MAX) {
- memset(&sattrs[j], 0, sizeof(sign_attrs_T));
- sattrs[j].sat_text = decor->sign_text;
- if (decor->sign_hl_id != 0) {
- sattrs[j].sat_texthl = syn_id2attr(decor->sign_hl_id);
- }
- if (decor->number_hl_id != 0) {
- sattrs[j].sat_numhl = syn_id2attr(decor->number_hl_id);
+ if (decor->sign_text) {
+ int j;
+ for (j = (*num_signs); j > 0; j--) {
+ if (sattrs[j - 1].priority >= decor->priority) {
+ break;
+ }
+ sattrs[j] = sattrs[j - 1];
}
- if (decor->line_hl_id != 0) {
- sattrs[j].sat_linehl = syn_id2attr(decor->line_hl_id);
+ if (j < SIGN_SHOW_MAX) {
+ sattrs[j] = (SignTextAttrs) {
+ .text = decor->sign_text,
+ .hl_attr_id = decor->sign_hl_id == 0 ? 0 : syn_id2attr(decor->sign_hl_id),
+ .priority = decor->priority
+ };
+ (*num_signs)++;
}
- if (decor->cursorline_hl_id != 0) {
- sattrs[j].sat_culhl = syn_id2attr(decor->cursorline_hl_id);
+ }
+
+ struct { HlPriAttr *dest; int hl; } cattrs[] = {
+ { line_attrs, decor->line_hl_id },
+ { num_attrs, decor->number_hl_id },
+ { cul_attrs, decor->cursorline_hl_id },
+ { NULL, -1 },
+ };
+ for (int i = 0; cattrs[i].dest; i++) {
+ if (cattrs[i].hl != 0 && decor->priority >= cattrs[i].dest->priority) {
+ *cattrs[i].dest = (HlPriAttr) {
+ .attr_id = syn_id2attr(cattrs[i].hl),
+ .priority = decor->priority
+ };
}
- sattrs[j].sat_prio = decor->priority;
- (*num_signs)++;
}
next_mark:
@@ -539,7 +547,7 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
}
int virt_lines = 0;
- int row = (int)MAX(lnum - 2, 0);
+ int row = MAX(lnum - 2, 0);
int end_row = (int)lnum;
MarkTreeIter itr[1] = { 0 };
marktree_itr_get(buf->b_marktree, row, 0, itr);
@@ -550,7 +558,7 @@ int decor_virt_lines(win_T *wp, linenr_T lnum, VirtLines *lines)
} else if (marktree_decor_level(mark) < kDecorLevelVirtLine) {
goto next_mark;
}
- bool above = mark.pos.row > (int)(lnum - 2);
+ bool above = mark.pos.row > (lnum - 2);
Decoration *decor = mark.decor_full;
if (decor && decor->virt_lines_above == above) {
virt_lines += (int)kv_size(decor->virt_lines);
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 04d875c4e3..14c1238fa4 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -14,7 +14,7 @@ static kvec_t(DecorProvider) decor_providers = KV_INITIAL_VALUE;
#define DECORATION_PROVIDER_INIT(ns_id) (DecorProvider) \
{ ns_id, false, LUA_NOREF, LUA_NOREF, \
LUA_NOREF, LUA_NOREF, LUA_NOREF, \
- LUA_NOREF, -1 }
+ LUA_NOREF, -1, false }
static bool decor_provider_invoke(NS ns_id, const char *name, LuaRef ref, Array args,
bool default_true, char **perr)
@@ -107,8 +107,6 @@ void decor_providers_invoke_win(win_T *wp, DecorProviders *providers,
}
}
}
-
- win_check_ns_hl(wp);
}
/// For each provider invoke the 'line' callback for a given window row.
@@ -135,7 +133,7 @@ void providers_invoke_line(win_T *wp, DecorProviders *providers, int row, bool *
kv_A(*providers, k) = NULL;
}
- win_check_ns_hl(wp);
+ hl_check_ns();
}
}
}
@@ -176,6 +174,7 @@ void decor_providers_invoke_end(DecorProviders *providers, char **err)
DecorProvider *get_decor_provider(NS ns_id, bool force)
{
+ assert(ns_id > 0);
size_t i;
size_t len = kv_size(decor_providers);
for (i = 0; i < len; i++) {
diff --git a/src/nvim/decoration_provider.h b/src/nvim/decoration_provider.h
index 3ec7c80357..dd1ed6c581 100644
--- a/src/nvim/decoration_provider.h
+++ b/src/nvim/decoration_provider.h
@@ -13,6 +13,7 @@ typedef struct {
LuaRef redraw_end;
LuaRef hl_def;
int hl_valid;
+ bool hl_cached;
} DecorProvider;
typedef kvec_withinit_t(DecorProvider *, 4) DecorProviders;
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index 849204f789..1cfb535fe8 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -14,11 +14,13 @@
#include <stdbool.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
@@ -32,10 +34,10 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
@@ -118,7 +120,7 @@ void diff_buf_delete(buf_T *buf)
// don't redraw right away, more might change or buffer state
// is invalid right now
need_diff_redraw = true;
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
}
}
@@ -571,9 +573,9 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
if (dir == BACKWARD) {
off_org = dp->df_count[i_org] - 1;
}
- char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
- dp->df_lnum[i_org] + off_org,
- false));
+ char *line_org = (char *)vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org],
+ dp->df_lnum[i_org] + off_org,
+ false));
int i_new;
for (i_new = i_org + 1; i_new < DB_COUNT; i_new++) {
@@ -590,9 +592,9 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp)
break;
}
- if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new],
- dp->df_lnum[i_new] + off_new,
- false)) != 0) {
+ if (diff_cmp((char_u *)line_org, ml_get_buf(tp->tp_diffbuf[i_new],
+ dp->df_lnum[i_new] + off_new,
+ false)) != 0) {
break;
}
}
@@ -657,7 +659,7 @@ void diff_redraw(bool dofold)
continue;
}
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
if (wp != curwin) {
wp_other = wp;
}
@@ -795,8 +797,8 @@ static int diff_write(buf_T *buf, diffin_T *din)
}
// Always use 'fileformat' set to "unix".
- char_u *save_ff = buf->b_p_ff;
- buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ char *save_ff = buf->b_p_ff;
+ buf->b_p_ff = xstrdup(FF_UNIX);
const bool save_cmod_flags = cmdmod.cmod_flags;
// Writing the buffer is an implementation detail of performing the diff,
// so it shouldn't update the '[ and '] marks.
@@ -956,13 +958,13 @@ void ex_diffupdate(exarg_T *eap)
// Only use the internal method if it did not fail for one of the buffers.
diffio_T diffio;
- memset(&diffio, 0, sizeof(diffio));
+ CLEAR_FIELD(diffio);
diffio.dio_internal = diff_internal() && !diff_internal_failed();
diff_try_update(&diffio, idx_orig, eap);
if (diffio.dio_internal && diff_internal_failed()) {
// Internal diff failed, use external diff instead.
- memset(&diffio, 0, sizeof(diffio));
+ CLEAR_FIELD(diffio);
diff_try_update(&diffio, idx_orig, eap);
}
@@ -1075,9 +1077,9 @@ static int diff_file_internal(diffio_T *diffio)
xdemitconf_t emit_cfg;
xdemitcb_t emit_cb;
- memset(&param, 0, sizeof(param));
- memset(&emit_cfg, 0, sizeof(emit_cfg));
- memset(&emit_cb, 0, sizeof(emit_cb));
+ CLEAR_FIELD(param);
+ CLEAR_FIELD(emit_cfg);
+ CLEAR_FIELD(emit_cb);
param.flags = (unsigned long)diff_algorithm;
@@ -1146,7 +1148,7 @@ static int diff_file(diffio_T *dio)
(diff_flags & DIFF_IBLANK) ? "-B " : "",
(diff_flags & DIFF_ICASE) ? "-i " : "",
tmp_orig, tmp_new);
- append_redir(cmd, len, (char *)p_srr, tmp_diff);
+ append_redir(cmd, len, p_srr, tmp_diff);
block_autocmds(); // Avoid ShellCmdPost stuff
(void)call_shell((char_u *)cmd,
kShellOptFilter | kShellOptSilent | kShellOptDoOut,
@@ -1372,7 +1374,7 @@ static void set_diff_option(win_T *wp, int value)
curwin = wp;
curbuf = curwin->w_buffer;
curbuf->b_ro_locked++;
- set_option_value("diff", (long)value, NULL, OPT_LOCAL);
+ set_option_value_give_err("diff", (long)value, NULL, OPT_LOCAL);
curbuf->b_ro_locked--;
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -1406,18 +1408,14 @@ void diff_win_options(win_T *wp, int addbuf)
}
wp->w_p_wrap = false;
}
- curwin = wp; // -V519
- curbuf = curwin->w_buffer;
if (!wp->w_p_diff) {
if (wp->w_p_diff_saved) {
free_string_option(wp->w_p_fdm_save);
}
- wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm);
+ wp->w_p_fdm_save = xstrdup(wp->w_p_fdm);
}
- set_string_option_direct("fdm", -1, "diff", OPT_LOCAL | OPT_FREE, 0);
- curwin = old_curwin;
- curbuf = curwin->w_buffer;
+ set_string_option_direct_in_win(wp, "fdm", -1, "diff", OPT_LOCAL | OPT_FREE, 0);
if (!wp->w_p_diff) {
wp->w_p_fen_save = wp->w_p_fen;
@@ -1426,12 +1424,12 @@ void diff_win_options(win_T *wp, int addbuf)
if (wp->w_p_diff_saved) {
free_string_option(wp->w_p_fdc_save);
}
- wp->w_p_fdc_save = vim_strsave(wp->w_p_fdc);
+ wp->w_p_fdc_save = xstrdup(wp->w_p_fdc);
}
free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = (char_u *)xstrdup("2");
+ wp->w_p_fdc = xstrdup("2");
assert(diff_foldcolumn >= 0 && diff_foldcolumn <= 9);
- snprintf((char *)wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn);
+ snprintf(wp->w_p_fdc, STRLEN(wp->w_p_fdc) + 1, "%d", diff_foldcolumn);
wp->w_p_fen = true;
wp->w_p_fdl = 0;
foldUpdateAll(wp);
@@ -1450,7 +1448,7 @@ void diff_win_options(win_T *wp, int addbuf)
if (addbuf) {
diff_buf_add(wp->w_buffer);
}
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
/// Set options not to show diffs. For the current window or all windows.
@@ -1482,11 +1480,9 @@ void ex_diffoff(exarg_T *eap)
}
}
free_string_option(wp->w_p_fdm);
- wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save
- ? wp->w_p_fdm_save
- : (char_u *)"manual");
+ wp->w_p_fdm = xstrdup(*wp->w_p_fdm_save ? wp->w_p_fdm_save : "manual");
free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = vim_strsave(wp->w_p_fdc_save);
+ wp->w_p_fdc = xstrdup(wp->w_p_fdc_save);
if (wp->w_p_fdl == 0) {
wp->w_p_fdl = wp->w_p_fdl_save;
@@ -1543,8 +1539,8 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
diff_T *dp = curtab->tp_first_diff;
diff_T *dn, *dpl;
diffout_T *dout = &dio->dio_diff;
- char_u linebuf[LBUFLEN]; // only need to hold the diff line
- char_u *line;
+ char linebuf[LBUFLEN]; // only need to hold the diff line
+ char *line;
linenr_T off;
int i;
int notset = true; // block "*dp" not set yet
@@ -1580,9 +1576,9 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
if (line_idx >= dout->dout_ga.ga_len) {
break; // did last line
}
- line = ((char_u **)dout->dout_ga.ga_data)[line_idx++];
+ line = ((char **)dout->dout_ga.ga_data)[line_idx++];
} else {
- if (vim_fgets(linebuf, LBUFLEN, fd)) {
+ if (vim_fgets((char_u *)linebuf, LBUFLEN, fd)) {
break; // end of file
}
line = linebuf;
@@ -1604,9 +1600,9 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
} else if ((STRNCMP(line, "@@ ", 3) == 0)) {
diffstyle = DIFF_UNIFIED;
} else if ((STRNCMP(line, "--- ", 4) == 0) // -V501
- && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
+ && (vim_fgets((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501
&& (STRNCMP(line, "+++ ", 4) == 0)
- && (vim_fgets(linebuf, LBUFLEN, fd) == 0) // -V501
+ && (vim_fgets((char_u *)linebuf, LBUFLEN, fd) == 0) // -V501
&& (STRNCMP(line, "@@ ", 3) == 0)) {
diffstyle = DIFF_UNIFIED;
} else {
@@ -1620,7 +1616,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
if (!isdigit(*line)) {
continue; // not the start of a diff block
}
- if (parse_diff_ed(line, hunk) == FAIL) {
+ if (parse_diff_ed((char_u *)line, hunk) == FAIL) {
continue;
}
} else {
@@ -1628,7 +1624,7 @@ static void diff_read(int idx_orig, int idx_new, diffio_T *dio)
if (STRNCMP(line, "@@ ", 3) != 0) {
continue; // not the start of a diff block
}
- if (parse_diff_unified(line, hunk) == FAIL) {
+ if (parse_diff_unified((char_u *)line, hunk) == FAIL) {
continue;
}
}
@@ -1924,11 +1920,11 @@ static bool diff_equal_entry(diff_T *dp, int idx1, int idx2)
}
for (int i = 0; i < dp->df_count[idx1]; i++) {
- char_u *line = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
- dp->df_lnum[idx1] + i, false));
+ char *line = (char *)vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx1],
+ dp->df_lnum[idx1] + i, false));
- int cmp = diff_cmp(line, ml_get_buf(curtab->tp_diffbuf[idx2],
- dp->df_lnum[idx2] + i, false));
+ int cmp = diff_cmp((char_u *)line, ml_get_buf(curtab->tp_diffbuf[idx2],
+ dp->df_lnum[idx2] + i, false));
xfree(line);
if (cmp != 0) {
@@ -2143,14 +2139,14 @@ int diffopt_changed(void)
long diff_algorithm_new = 0;
long diff_indent_heuristic = 0;
- char_u *p = p_dip;
+ char *p = p_dip;
while (*p != NUL) {
if (STRNCMP(p, "filler", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_FILLER;
} else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) {
p += 8;
- diff_context_new = getdigits_int((char **)&p, false, diff_context_new);
+ diff_context_new = getdigits_int(&p, false, diff_context_new);
} else if (STRNCMP(p, "iblank", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_IBLANK;
@@ -2174,7 +2170,7 @@ int diffopt_changed(void)
diff_flags_new |= DIFF_VERTICAL;
} else if ((STRNCMP(p, "foldcolumn:", 11) == 0) && ascii_isdigit(p[11])) {
p += 11;
- diff_foldcolumn_new = getdigits_int((char **)&p, false, diff_foldcolumn_new);
+ diff_foldcolumn_new = getdigits_int(&p, false, diff_foldcolumn_new);
} else if (STRNCMP(p, "hiddenoff", 9) == 0) {
p += 9;
diff_flags_new |= DIFF_HIDDEN_OFF;
@@ -2285,7 +2281,7 @@ bool diffopt_filler(void)
bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- char_u *line_new;
+ char *line_new;
int si_org;
int si_new;
int ei_org;
@@ -2294,7 +2290,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
int l;
// Make a copy of the line, the next ml_get() will invalidate it.
- char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, false));
+ char *line_org = (char *)vim_strsave(ml_get_buf(wp->w_buffer, lnum, false));
int idx = diff_buf_idx(wp->w_buffer);
if (idx == DB_COUNT) {
@@ -2326,8 +2322,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
continue;
}
added = false;
- line_new = ml_get_buf(curtab->tp_diffbuf[i],
- dp->df_lnum[i] + off, false);
+ line_new = (char *)ml_get_buf(curtab->tp_diffbuf[i],
+ dp->df_lnum[i] + off, false);
// Search for start of difference
si_org = si_new = 0;
@@ -2339,10 +2335,10 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
|| ((diff_flags & DIFF_IWHITEALL)
&& (ascii_iswhite(line_org[si_org])
|| ascii_iswhite(line_new[si_new])))) {
- si_org = (int)((char_u *)skipwhite((char *)line_org + si_org) - line_org);
- si_new = (int)((char_u *)skipwhite((char *)line_new + si_new) - line_new);
+ si_org = (int)(skipwhite(line_org + si_org) - line_org);
+ si_new = (int)(skipwhite(line_new + si_new) - line_new);
} else {
- if (!diff_equal_char(line_org + si_org, line_new + si_new, &l)) {
+ if (!diff_equal_char((char_u *)line_org + si_org, (char_u *)line_new + si_new, &l)) {
break;
}
si_org += l;
@@ -2352,8 +2348,8 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
// Move back to first byte of character in both lines (may
// have "nn^" in line_org and "n^ in line_new).
- si_org -= utf_head_off(line_org, line_org + si_org);
- si_new -= utf_head_off(line_new, line_new + si_new);
+ si_org -= utf_head_off((char_u *)line_org, (char_u *)line_org + si_org);
+ si_new -= utf_head_off((char_u *)line_new, (char_u *)line_new + si_new);
if (*startp > si_org) {
*startp = si_org;
@@ -2382,11 +2378,11 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp)
ei_new--;
}
} else {
- const char_u *p1 = line_org + ei_org;
- const char_u *p2 = line_new + ei_new;
+ const char_u *p1 = (char_u *)line_org + ei_org;
+ const char_u *p2 = (char_u *)line_new + ei_new;
- p1 -= utf_head_off(line_org, p1);
- p2 -= utf_head_off(line_new, p2);
+ p1 -= utf_head_off((char_u *)line_org, p1);
+ p2 -= utf_head_off((char_u *)line_new, p2);
if (!diff_equal_char(p1, p2, &l)) {
break;
@@ -2515,7 +2511,7 @@ void ex_diffgetput(exarg_T *eap)
diff_T *dfree;
int i;
int added;
- char_u *p;
+ char *p;
aco_save_T aco;
buf_T *buf;
linenr_T start_skip;
@@ -2569,18 +2565,18 @@ void ex_diffgetput(exarg_T *eap)
}
} else {
// Buffer number or pattern given. Ignore trailing white space.
- p = (char_u *)eap->arg + STRLEN(eap->arg);
- while (p > (char_u *)eap->arg && ascii_iswhite(p[-1])) {
+ p = eap->arg + STRLEN(eap->arg);
+ while (p > eap->arg && ascii_iswhite(p[-1])) {
p--;
}
- for (i = 0; ascii_isdigit(eap->arg[i]) && (char_u *)eap->arg + i < p; i++) {}
+ for (i = 0; ascii_isdigit(eap->arg[i]) && eap->arg + i < p; i++) {}
- if ((char_u *)eap->arg + i == p) {
+ if (eap->arg + i == p) {
// digits only
i = (int)atol(eap->arg);
} else {
- i = buflist_findpat(eap->arg, (char *)p, false, true, false);
+ i = buflist_findpat(eap->arg, p, false, true, false);
if (i < 0) {
// error message already given
@@ -2722,8 +2718,8 @@ void ex_diffgetput(exarg_T *eap)
if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) {
break;
}
- p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
- ml_append(lnum + i - 1, (char *)p, 0, false);
+ p = (char *)vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false));
+ ml_append(lnum + i - 1, p, 0, false);
xfree(p);
added++;
if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) {
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index 733b3d3d5d..e4528f9038 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -12,8 +12,8 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
@@ -24,7 +24,7 @@
#include "nvim/message.h"
#include "nvim/normal.h"
#include "nvim/os/input.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
@@ -1695,7 +1695,7 @@ static void digraph_header(const char *msg)
if (msg_col > 0) {
msg_putchar('\n');
}
- msg_outtrans_attr((const char_u *)msg, HL_ATTR(HLF_CM));
+ msg_outtrans_attr(msg, HL_ATTR(HLF_CM));
msg_putchar('\n');
}
@@ -1856,7 +1856,7 @@ static void printdigraph(const digr_T *dp, result_T *previous)
p += utf_char2bytes(dp->result, (char *)p);
*p = NUL;
- msg_outtrans_attr(buf, HL_ATTR(HLF_8));
+ msg_outtrans_attr((char *)buf, HL_ATTR(HLF_8));
p = buf;
if (char2cells(dp->result) == 1) {
*p++ = ' ';
@@ -1917,7 +1917,7 @@ static bool digraph_set_common(const typval_T *argchars, const typval_T *argdigr
}
/// "digraph_get()" function
-void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; // Return empty string for failure
@@ -1938,7 +1938,7 @@ void f_digraph_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "digraph_getlist()" function
-void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_getlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool flag_list_all;
@@ -1957,7 +1957,7 @@ void f_digraph_getlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "digraph_set()" function
-void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_set(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_BOOL;
rettv->vval.v_bool = kBoolVarFalse;
@@ -1970,7 +1970,7 @@ void f_digraph_set(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "digraph_setlist()" function
-void f_digraph_setlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_digraph_setlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_BOOL;
rettv->vval.v_bool = kBoolVarFalse;
@@ -2096,10 +2096,10 @@ void ex_loadkeymap(exarg_T *eap)
if ((*p != '"') && (*p != NUL)) {
kmap_T *kp = GA_APPEND_VIA_PTR(kmap_T, &curbuf->b_kmap_ga);
- s = skiptowhite(p);
+ s = (char_u *)skiptowhite((char *)p);
kp->from = vim_strnsave(p, (size_t)(s - p));
p = (char_u *)skipwhite((char *)s);
- s = skiptowhite(p);
+ s = (char_u *)skiptowhite((char *)p);
kp->to = vim_strnsave(p, (size_t)(s - p));
if ((STRLEN(kp->from) + STRLEN(kp->to) >= KMAP_LLEN)
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
new file mode 100644
index 0000000000..b6c4400c60
--- /dev/null
+++ b/src/nvim/drawline.c
@@ -0,0 +1,2680 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// drawline.c: Functions for drawing window lines on the screen.
+// This is the middle level, drawscreen.c is the top and grid.c/screen.c the lower level.
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/arabic.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/cursor_shape.h"
+#include "nvim/diff.h"
+#include "nvim/drawline.h"
+#include "nvim/fold.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
+#include "nvim/match.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/plines.h"
+#include "nvim/quickfix.h"
+#include "nvim/search.h"
+#include "nvim/sign.h"
+#include "nvim/spell.h"
+#include "nvim/state.h"
+#include "nvim/syntax.h"
+#include "nvim/undo.h"
+#include "nvim/window.h"
+
+#define MB_FILLER_CHAR '<' // character used when a double-width character
+ // doesn't fit.
+
+/// for line_putchar. Contains the state that needs to be remembered from
+/// putting one character to the next.
+typedef struct {
+ const char *p;
+ int prev_c; ///< previous Arabic character
+ int prev_c1; ///< first composing char for prev_c
+} LineState;
+#define LINE_STATE(p) { p, 0, 0 }
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawline.c.generated.h"
+#endif
+
+/// Advance **color_cols
+///
+/// @return true when there are columns to draw.
+static bool advance_color_col(int vcol, int **color_cols)
+{
+ while (**color_cols >= 0 && vcol > **color_cols) {
+ (*color_cols)++;
+ }
+ return **color_cols >= 0;
+}
+
+/// Used when 'cursorlineopt' contains "screenline": compute the margins between
+/// which the highlighting is used.
+static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
+{
+ // cache previous calculations depending on w_virtcol
+ static int saved_w_virtcol;
+ static win_T *prev_wp;
+ static int prev_left_col;
+ static int prev_right_col;
+ static int prev_col_off;
+
+ int cur_col_off = win_col_off(wp);
+ int width1;
+ int width2;
+
+ if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp
+ && prev_col_off == cur_col_off) {
+ *right_col = prev_right_col;
+ *left_col = prev_left_col;
+ return;
+ }
+
+ width1 = wp->w_width - cur_col_off;
+ width2 = width1 + win_col_off2(wp);
+
+ *left_col = 0;
+ *right_col = width1;
+
+ if (wp->w_virtcol >= (colnr_T)width1) {
+ *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2;
+ }
+ if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
+ *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1;
+ }
+
+ // cache values
+ prev_left_col = *left_col;
+ prev_right_col = *right_col;
+ prev_wp = wp;
+ saved_w_virtcol = wp->w_virtcol;
+ prev_col_off = cur_col_off;
+}
+
+/// Put a single char from an UTF-8 buffer into a line buffer.
+///
+/// Handles composing chars and arabic shaping state.
+static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol)
+{
+ const char_u *p = (char_u *)s->p;
+ int cells = utf_ptr2cells((char *)p);
+ int c_len = utfc_ptr2len((char *)p);
+ int u8c, u8cc[MAX_MCO];
+ if (cells > maxcells) {
+ return -1;
+ }
+ u8c = utfc_ptr2char((char *)p, u8cc);
+ if (*p == TAB) {
+ cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
+ for (int c = 0; c < cells; c++) {
+ schar_from_ascii(dest[c], ' ');
+ }
+ goto done;
+ } else if (*p < 0x80 && u8cc[0] == 0) {
+ schar_from_ascii(dest[0], (char)(*p));
+ s->prev_c = u8c;
+ } else {
+ if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
+ // Do Arabic shaping.
+ int pc, pc1, nc;
+ int pcc[MAX_MCO];
+ int firstbyte = *p;
+
+ // The idea of what is the previous and next
+ // character depends on 'rightleft'.
+ if (rl) {
+ pc = s->prev_c;
+ pc1 = s->prev_c1;
+ nc = utf_ptr2char((char *)p + c_len);
+ s->prev_c1 = u8cc[0];
+ } else {
+ pc = utfc_ptr2char((char *)p + c_len, pcc);
+ nc = s->prev_c;
+ pc1 = pcc[0];
+ }
+ s->prev_c = u8c;
+
+ u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc);
+ } else {
+ s->prev_c = u8c;
+ }
+ schar_from_cc(dest[0], u8c, u8cc);
+ }
+ if (cells > 1) {
+ dest[1][0] = 0;
+ }
+done:
+ s->p += c_len;
+ return cells;
+}
+
+static inline void provider_err_virt_text(linenr_T lnum, char *err)
+{
+ Decoration err_decor = DECORATION_INIT;
+ int hl_err = syn_check_group(S_LEN("ErrorMsg"));
+ kv_push(err_decor.virt_text,
+ ((VirtTextChunk){ .text = err,
+ .hl_id = hl_err }));
+ err_decor.virt_text_width = (int)mb_string2cells(err);
+ decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
+}
+
+static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col,
+ int win_row)
+{
+ DecorState *state = &decor_state;
+ int right_pos = max_col;
+ bool do_eol = state->eol_col > -1;
+ for (size_t i = 0; i < kv_size(state->active); i++) {
+ DecorRange *item = &kv_A(state->active, i);
+ if (!(item->start_row == state->row
+ && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) {
+ continue;
+ }
+ if (item->win_col == -1) {
+ if (item->decor.virt_text_pos == kVTRightAlign) {
+ right_pos -= item->decor.virt_text_width;
+ item->win_col = right_pos;
+ } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
+ item->win_col = state->eol_col;
+ } else if (item->decor.virt_text_pos == kVTWinCol) {
+ item->win_col = MAX(item->decor.col + col_off, 0);
+ }
+ }
+ if (item->win_col < 0) {
+ continue;
+ }
+ int col;
+ if (item->decor.ui_watched) {
+ // send mark position to UI
+ col = item->win_col;
+ WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col };
+ kv_push(win_extmark_arr, m);
+ }
+ if (kv_size(item->decor.virt_text)) {
+ col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
+ item->decor.hl_mode, max_col, item->win_col - col_off);
+ }
+ item->win_col = -2; // deactivate
+ if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
+ state->eol_col = col + 1;
+ }
+
+ *end_col = MAX(*end_col, col);
+ }
+}
+
+static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
+ int vcol)
+{
+ LineState s = LINE_STATE("");
+ int virt_attr = 0;
+ size_t virt_pos = 0;
+
+ while (col < max_col) {
+ if (!*s.p) {
+ if (virt_pos >= kv_size(vt)) {
+ break;
+ }
+ virt_attr = 0;
+ do {
+ s.p = kv_A(vt, virt_pos).text;
+ int hl_id = kv_A(vt, virt_pos).hl_id;
+ virt_attr = hl_combine_attr(virt_attr,
+ hl_id > 0 ? syn_id2attr(hl_id) : 0);
+ virt_pos++;
+ } while (!s.p && virt_pos < kv_size(vt));
+ if (!s.p) {
+ break;
+ }
+ }
+ if (!*s.p) {
+ continue;
+ }
+ int attr;
+ bool through = false;
+ if (hl_mode == kHlModeCombine) {
+ attr = hl_combine_attr(linebuf_attr[col], virt_attr);
+ } else if (hl_mode == kHlModeBlend) {
+ through = (*s.p == ' ');
+ attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
+ } else {
+ attr = virt_attr;
+ }
+ schar_T dummy[2];
+ int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
+ max_col - col, false, vcol);
+ // if we failed to emit a char, we still need to advance
+ cells = MAX(cells, 1);
+
+ for (int c = 0; c < cells; c++) {
+ linebuf_attr[col++] = attr;
+ }
+ vcol += cells;
+ }
+ return col;
+}
+
+/// Return true if CursorLineSign highlight is to be used.
+static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR);
+}
+
+// Get information needed to display the sign in line 'lnum' in window 'wp'.
+// If 'nrcol' is true, the sign is going to be displayed in the number column.
+// Otherwise the sign is going to be displayed in the sign column.
+//
+// @param count max number of signs
+// @param[out] n_extrap number of characters from pp_extra to display
+// @param sign_idxp Index of the displayed sign
+static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, SignTextAttrs sattrs[],
+ int row, int startrow, int filler_lines, int filler_todo,
+ int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
+ char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx,
+ int cul_attr)
+{
+ // Draw cells with the sign value or blank.
+ *c_extrap = ' ';
+ *c_finalp = NUL;
+ if (nrcol) {
+ *n_extrap = number_width(wp) + 1;
+ } else {
+ if (use_cursor_line_sign(wp, lnum)) {
+ *char_attrp = win_hl_attr(wp, HLF_CLS);
+ } else {
+ *char_attrp = win_hl_attr(wp, HLF_SC);
+ }
+ *n_extrap = win_signcol_width(wp);
+ }
+
+ if (row == startrow + filler_lines && filler_todo <= 0) {
+ SignTextAttrs *sattr = sign_get_attr(sign_idx, sattrs, wp->w_scwidth);
+ if (sattr != NULL) {
+ *pp_extra = sattr->text;
+ if (*pp_extra != NULL) {
+ *c_extrap = NUL;
+ *c_finalp = NUL;
+
+ if (nrcol) {
+ int n, width = number_width(wp) - 2;
+ for (n = 0; n < width; n++) {
+ extra[n] = ' ';
+ }
+ extra[n] = NUL;
+ STRCAT(extra, *pp_extra);
+ STRCAT(extra, " ");
+ *pp_extra = extra;
+ *n_extrap = (int)STRLEN(*pp_extra);
+ } else {
+ size_t symbol_blen = STRLEN(*pp_extra);
+
+ // TODO(oni-link): Is sign text already extended to
+ // full cell width?
+ assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra)));
+ // symbol(s) bytes + (filling spaces) (one byte each)
+ *n_extrap = (int)symbol_blen + win_signcol_width(wp) -
+ (int)mb_string2cells((char *)(*pp_extra));
+
+ assert(extra_size > symbol_blen);
+ memset(extra, ' ', extra_size);
+ memcpy(extra, *pp_extra, symbol_blen);
+
+ *pp_extra = extra;
+ (*pp_extra)[*n_extrap] = NUL;
+ }
+ }
+
+ if (use_cursor_line_sign(wp, lnum) && cul_attr > 0) {
+ *char_attrp = cul_attr;
+ } else {
+ *char_attrp = sattr->hl_attr_id;
+ }
+ }
+ }
+}
+
+static int get_sign_attrs(buf_T *buf, linenr_T lnum, SignTextAttrs *sattrs, int *line_attr,
+ int *num_attr, int *cul_attr)
+{
+ HlPriAttr line_attrs = { *line_attr, 0 };
+ HlPriAttr num_attrs = { *num_attr, 0 };
+ HlPriAttr cul_attrs = { *cul_attr, 0 };
+
+ // TODO(bfredl, vigoux): line_attr should not take priority over decoration!
+ int num_signs = buf_get_signattrs(buf, lnum, sattrs, &num_attrs, &line_attrs, &cul_attrs);
+ decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs, &num_attrs, &line_attrs, &cul_attrs);
+
+ *line_attr = line_attrs.attr_id;
+ *num_attr = num_attrs.attr_id;
+ *cul_attr = cul_attrs.attr_id;
+
+ return num_signs;
+}
+
+/// Return true if CursorLineNr highlight is to be used for the number column.
+///
+/// - 'cursorline' must be set
+/// - lnum must be the cursor line
+/// - 'cursorlineopt' has "number"
+/// - don't highlight filler lines (when in diff mode)
+/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number
+/// itself on the first screenline of the wrapped line, otherwise highlight the number column of
+/// all screenlines of the wrapped line.
+static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
+{
+ return wp->w_p_cul
+ && lnum == wp->w_cursor.lnum
+ && (wp->w_p_culopt_flags & CULOPT_NBR)
+ && (row == startrow + filler_lines
+ || (row > startrow + filler_lines
+ && (wp->w_p_culopt_flags & CULOPT_LINE)));
+}
+
+static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
+{
+ long num;
+ char *fmt = "%*ld ";
+
+ if (wp->w_p_nu && !wp->w_p_rnu) {
+ // 'number' + 'norelativenumber'
+ num = (long)lnum;
+ } else {
+ // 'relativenumber', don't use negative numbers
+ num = labs((long)get_cursor_rel_lnum(wp, lnum));
+ if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
+ // 'number' + 'relativenumber'
+ num = lnum;
+ fmt = "%-*ld ";
+ }
+ }
+
+ snprintf((char *)buf, buf_len, fmt, number_width(wp), num);
+}
+
+static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
+{
+ if (wp->w_p_rnu) {
+ if (lnum < wp->w_cursor.lnum) {
+ // Use LineNrAbove
+ return win_hl_attr(wp, HLF_LNA);
+ }
+ if (lnum > wp->w_cursor.lnum) {
+ // Use LineNrBelow
+ return win_hl_attr(wp, HLF_LNB);
+ }
+ }
+
+ if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) {
+ // TODO(vim): Can we use CursorLine instead of CursorLineNr
+ // when CursorLineNr isn't set?
+ return win_hl_attr(wp, HLF_CLN);
+ }
+
+ return win_hl_attr(wp, HLF_N);
+}
+
+static void apply_cursorline_highlight(win_T *wp, linenr_T lnum, int *line_attr, int *cul_attr,
+ int *line_attr_lowprio)
+{
+ *cul_attr = win_hl_attr(wp, HLF_CUL);
+ HlAttrs ae = syn_attr2entry(*cul_attr);
+ // We make a compromise here (#7383):
+ // * low-priority CursorLine if fg is not set
+ // * high-priority ("same as Vim" priority) CursorLine if fg is set
+ if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
+ *line_attr_lowprio = *cul_attr;
+ } else {
+ if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
+ && qf_current_entry(wp) == lnum) {
+ *line_attr = hl_combine_attr(*cul_attr, *line_attr);
+ } else {
+ *line_attr = *cul_attr;
+ }
+ }
+}
+
+static bool check_mb_utf8(int *c, int *u8cc)
+{
+ if (utf_char2len(*c) > 1) {
+ *u8cc = 0;
+ *c = 0xc0;
+ return true;
+ }
+ return false;
+}
+
+/// Display line "lnum" of window 'wp' on the screen.
+/// wp->w_virtcol needs to be valid.
+///
+/// @param lnum line to display
+/// @param startrow first row relative to window grid
+/// @param endrow last grid row to be redrawn
+/// @param nochange not updating for changed text
+/// @param number_only only update the number column
+/// @param foldinfo fold info for this line
+/// @param[in, out] providers decoration providers active this line
+/// items will be disables if they cause errors
+/// or explicitly return `false`.
+///
+/// @return the number of last row the line occupies.
+int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange, bool number_only,
+ foldinfo_T foldinfo, DecorProviders *providers, char **provider_err)
+{
+ int c = 0; // init for GCC
+ long vcol = 0; // virtual column (for tabs)
+ long vcol_sbr = -1; // virtual column after showbreak
+ long vcol_prev = -1; // "vcol" of previous character
+ char_u *line; // current line
+ char_u *ptr; // current position in "line"
+ int row; // row in the window, excl w_winrow
+ ScreenGrid *grid = &wp->w_grid; // grid specific to the window
+
+ char_u extra[57]; // sign, line number and 'fdc' must
+ // fit in here
+ int n_extra = 0; // number of extra chars
+ char_u *p_extra = NULL; // string of extra chars, plus NUL
+ char_u *p_extra_free = NULL; // p_extra needs to be freed
+ int c_extra = NUL; // extra chars, all the same
+ int c_final = NUL; // final char, mandatory if set
+ int extra_attr = 0; // attributes when n_extra != 0
+ static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
+ // curwin->w_p_lcs_chars.eol at
+ // end-of-line
+ int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
+ int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
+ bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
+
+ // saved "extra" items for when draw_state becomes WL_LINE (again)
+ int saved_n_extra = 0;
+ char_u *saved_p_extra = NULL;
+ int saved_c_extra = 0;
+ int saved_c_final = 0;
+ int saved_char_attr = 0;
+
+ int n_attr = 0; // chars with special attr
+ int saved_attr2 = 0; // char_attr saved for n_attr
+ int n_attr3 = 0; // chars with overruling special attr
+ int saved_attr3 = 0; // char_attr saved for n_attr3
+
+ int n_skip = 0; // nr of chars to skip for 'nowrap'
+
+ int fromcol = -10; // start of inverting
+ int tocol = MAXCOL; // end of inverting
+ int fromcol_prev = -2; // start of inverting after cursor
+ bool noinvcur = false; // don't invert the cursor
+ bool lnum_in_visual_area = false;
+ pos_T pos;
+ long v;
+
+ int char_attr = 0; // attributes for next character
+ bool attr_pri = false; // char_attr has priority
+ bool area_highlighting = false; // Visual or incsearch highlighting in this line
+ int attr = 0; // attributes for area highlighting
+ int area_attr = 0; // attributes desired by highlighting
+ int search_attr = 0; // attributes desired by 'hlsearch'
+ int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
+ int syntax_attr = 0; // attributes desired by syntax
+ bool has_syntax = false; // this buffer has syntax highl.
+ int save_did_emsg;
+ int eol_hl_off = 0; // 1 if highlighted char after EOL
+ bool draw_color_col = false; // highlight colorcolumn
+ int *color_cols = NULL; // pointer to according columns array
+ bool has_spell = false; // this buffer has spell checking
+#define SPWORDLEN 150
+ char_u nextline[SPWORDLEN * 2]; // text with start of the next line
+ int nextlinecol = 0; // column where nextline[] starts
+ int nextline_idx = 0; // index in nextline[] where next line
+ // starts
+ int spell_attr = 0; // attributes desired by spelling
+ int word_end = 0; // last byte with same spell_attr
+ static linenr_T checked_lnum = 0; // line number for "checked_col"
+ static int checked_col = 0; // column in "checked_lnum" up to which
+ // there are no spell errors
+ static int cap_col = -1; // column to check for Cap word
+ static linenr_T capcol_lnum = 0; // line number where "cap_col"
+ int cur_checked_col = 0; // checked column for current line
+ int extra_check = 0; // has syntax or linebreak
+ int multi_attr = 0; // attributes desired by multibyte
+ int mb_l = 1; // multi-byte byte length
+ int mb_c = 0; // decoded multi-byte character
+ bool mb_utf8 = false; // screen char is UTF-8 char
+ int u8cc[MAX_MCO]; // composing UTF-8 chars
+ int filler_lines; // nr of filler lines to be drawn
+ int filler_todo; // nr of filler lines still to do + 1
+ hlf_T diff_hlf = (hlf_T)0; // type of diff highlighting
+ int change_start = MAXCOL; // first col of changed area
+ int change_end = -1; // last col of changed area
+ colnr_T trailcol = MAXCOL; // start of trailing spaces
+ colnr_T leadcol = 0; // start of leading spaces
+ bool in_multispace = false; // in multiple consecutive spaces
+ int multispace_pos = 0; // position in lcs-multispace string
+ bool need_showbreak = false; // overlong line, skip first x chars
+ int line_attr = 0; // attribute for the whole line
+ int line_attr_save;
+ int line_attr_lowprio = 0; // low-priority attribute for the line
+ int line_attr_lowprio_save;
+ int prev_c = 0; // previous Arabic character
+ int prev_c1 = 0; // first composing char for prev_c
+
+ bool search_attr_from_match = false; // if search_attr is from :match
+ bool has_decor = false; // this buffer has decoration
+ int win_col_offset = 0; // offset for window columns
+
+ char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
+
+ bool area_active = false;
+
+ int cul_attr = 0; // set when 'cursorline' active
+ // 'cursorlineopt' has "screenline" and cursor is in this line
+ bool cul_screenline = false;
+ // margin columns for the screen line, needed for when 'cursorlineopt'
+ // contains "screenline"
+ int left_curline_col = 0;
+ int right_curline_col = 0;
+
+ // draw_state: items that are drawn in sequence:
+#define WL_START 0 // nothing done yet
+#define WL_CMDLINE (WL_START + 1) // cmdline window column
+#define WL_FOLD (WL_CMDLINE + 1) // 'foldcolumn'
+#define WL_SIGN (WL_FOLD + 1) // column for signs
+#define WL_NR (WL_SIGN + 1) // line number
+#define WL_BRI (WL_NR + 1) // 'breakindent'
+#define WL_SBR (WL_BRI + 1) // 'showbreak' or 'diff'
+#define WL_LINE (WL_SBR + 1) // text in the line
+ int draw_state = WL_START; // what to draw next
+
+ int syntax_flags = 0;
+ int syntax_seqnr = 0;
+ int prev_syntax_id = 0;
+ int conceal_attr = win_hl_attr(wp, HLF_CONCEAL);
+ bool is_concealing = false;
+ int boguscols = 0; ///< nonexistent columns added to
+ ///< force wrapping
+ int vcol_off = 0; ///< offset for concealed characters
+ int did_wcol = false;
+ int match_conc = 0; ///< cchar for match functions
+ int old_boguscols = 0;
+#define VCOL_HLC (vcol - vcol_off)
+#define FIX_FOR_BOGUSCOLS \
+ { \
+ n_extra += vcol_off; \
+ vcol -= vcol_off; \
+ vcol_off = 0; \
+ col -= boguscols; \
+ old_boguscols = boguscols; \
+ boguscols = 0; \
+ }
+
+ if (startrow > endrow) { // past the end already!
+ return startrow;
+ }
+
+ row = startrow;
+
+ buf_T *buf = wp->w_buffer;
+ bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
+
+ if (!number_only) {
+ // To speed up the loop below, set extra_check when there is linebreak,
+ // trailing white space and/or syntax processing to be done.
+ extra_check = wp->w_p_lbr;
+ if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow
+ && !has_fold && !end_fill) {
+ // Prepare for syntax highlighting in this line. When there is an
+ // error, stop syntax highlighting.
+ save_did_emsg = did_emsg;
+ did_emsg = false;
+ syntax_start(wp, lnum);
+ if (did_emsg) {
+ wp->w_s->b_syn_error = true;
+ } else {
+ did_emsg = save_did_emsg;
+ if (!wp->w_s->b_syn_slow) {
+ has_syntax = true;
+ extra_check = true;
+ }
+ }
+ }
+
+ has_decor = decor_redraw_line(buf, lnum - 1, &decor_state);
+
+ providers_invoke_line(wp, providers, lnum - 1, &has_decor, provider_err);
+
+ if (*provider_err) {
+ provider_err_virt_text(lnum, *provider_err);
+ has_decor = true;
+ *provider_err = NULL;
+ }
+
+ if (has_decor) {
+ extra_check = true;
+ }
+
+ // Check for columns to display for 'colorcolumn'.
+ color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
+ if (color_cols != NULL) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ if (wp->w_p_spell
+ && !has_fold
+ && !end_fill
+ && *wp->w_s->b_p_spl != NUL
+ && !GA_EMPTY(&wp->w_s->b_langp)
+ && *(char **)(wp->w_s->b_langp.ga_data) != NULL) {
+ // Prepare for spell checking.
+ has_spell = true;
+ extra_check = true;
+
+ // Get the start of the next line, so that words that wrap to the next
+ // line are found too: "et<line-break>al.".
+ // Trick: skip a few chars for C/shell/Vim comments
+ nextline[SPWORDLEN] = NUL;
+ if (lnum < wp->w_buffer->b_ml.ml_line_count) {
+ line = ml_get_buf(wp->w_buffer, lnum + 1, false);
+ spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
+ }
+
+ // When a word wrapped from the previous line the start of the current
+ // line is valid.
+ if (lnum == checked_lnum) {
+ cur_checked_col = checked_col;
+ }
+ checked_lnum = 0;
+
+ // When there was a sentence end in the previous line may require a
+ // word starting with capital in this line. In line 1 always check
+ // the first word.
+ if (lnum != capcol_lnum) {
+ cap_col = -1;
+ }
+ if (lnum == 1) {
+ cap_col = 0;
+ }
+ capcol_lnum = 0;
+ }
+
+ // handle Visual active in this window
+ if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
+ pos_T *top, *bot;
+
+ if (ltoreq(curwin->w_cursor, VIsual)) {
+ // Visual is after curwin->w_cursor
+ top = &curwin->w_cursor;
+ bot = &VIsual;
+ } else {
+ // Visual is before curwin->w_cursor
+ top = &VIsual;
+ bot = &curwin->w_cursor;
+ }
+ lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum);
+ if (VIsual_mode == Ctrl_V) {
+ // block mode
+ if (lnum_in_visual_area) {
+ fromcol = wp->w_old_cursor_fcol;
+ tocol = wp->w_old_cursor_lcol;
+ }
+ } else {
+ // non-block mode
+ if (lnum > top->lnum && lnum <= bot->lnum) {
+ fromcol = 0;
+ } else if (lnum == top->lnum) {
+ if (VIsual_mode == 'V') { // linewise
+ fromcol = 0;
+ } else {
+ getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL);
+ if (gchar_pos(top) == NUL) {
+ tocol = fromcol + 1;
+ }
+ }
+ }
+ if (VIsual_mode != 'V' && lnum == bot->lnum) {
+ if (*p_sel == 'e' && bot->col == 0
+ && bot->coladd == 0) {
+ fromcol = -10;
+ tocol = MAXCOL;
+ } else if (bot->col == MAXCOL) {
+ tocol = MAXCOL;
+ } else {
+ pos = *bot;
+ if (*p_sel == 'e') {
+ getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL);
+ } else {
+ getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol);
+ tocol++;
+ }
+ }
+ }
+ }
+
+ // Check if the char under the cursor should be inverted (highlighted).
+ if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
+ && cursor_is_block_during_visual(*p_sel == 'e')) {
+ noinvcur = true;
+ }
+
+ // if inverting in this line set area_highlighting
+ if (fromcol >= 0) {
+ area_highlighting = true;
+ attr = win_hl_attr(wp, HLF_V);
+ }
+ // handle 'incsearch' and ":s///c" highlighting
+ } else if (highlight_match
+ && wp == curwin
+ && !has_fold
+ && lnum >= curwin->w_cursor.lnum
+ && lnum <= curwin->w_cursor.lnum + search_match_lines) {
+ if (lnum == curwin->w_cursor.lnum) {
+ getvcol(curwin, &(curwin->w_cursor),
+ (colnr_T *)&fromcol, NULL, NULL);
+ } else {
+ fromcol = 0;
+ }
+ if (lnum == curwin->w_cursor.lnum + search_match_lines) {
+ pos.lnum = lnum;
+ pos.col = search_match_endcol;
+ getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL);
+ }
+ // do at least one character; happens when past end of line
+ if (fromcol == tocol && search_match_endcol) {
+ tocol = fromcol + 1;
+ }
+ area_highlighting = true;
+ attr = win_hl_attr(wp, HLF_I);
+ }
+ }
+
+ int bg_attr = win_bg_attr(wp);
+
+ filler_lines = diff_check(wp, lnum);
+ if (filler_lines < 0) {
+ if (filler_lines == -1) {
+ if (diff_find_change(wp, lnum, &change_start, &change_end)) {
+ diff_hlf = HLF_ADD; // added line
+ } else if (change_start == 0) {
+ diff_hlf = HLF_TXD; // changed text
+ } else {
+ diff_hlf = HLF_CHD; // changed line
+ }
+ } else {
+ diff_hlf = HLF_ADD; // added line
+ }
+ filler_lines = 0;
+ area_highlighting = true;
+ }
+ VirtLines virt_lines = KV_INITIAL_VALUE;
+ int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines);
+ filler_lines += n_virt_lines;
+ if (lnum == wp->w_topline) {
+ filler_lines = wp->w_topfill;
+ n_virt_lines = MIN(n_virt_lines, filler_lines);
+ }
+ filler_todo = filler_lines;
+
+ // Cursor line highlighting for 'cursorline' in the current window.
+ if (lnum == wp->w_cursor.lnum) {
+ // Do not show the cursor line in the text when Visual mode is active,
+ // because it's not clear what is selected then.
+ if (wp->w_p_cul && !(wp == curwin && VIsual_active)
+ && wp->w_p_culopt_flags != CULOPT_NBR) {
+ cul_screenline = (wp->w_p_wrap
+ && (wp->w_p_culopt_flags & CULOPT_SCRLINE));
+ if (!cul_screenline) {
+ apply_cursorline_highlight(wp, lnum, &line_attr, &cul_attr, &line_attr_lowprio);
+ } else {
+ margin_columns_win(wp, &left_curline_col, &right_curline_col);
+ }
+ area_highlighting = true;
+ }
+ }
+
+ SignTextAttrs sattrs[SIGN_SHOW_MAX]; // sign attributes for the sign column
+ int sign_num_attr = 0; // sign attribute for the number column
+ int sign_cul_attr = 0; // sign attribute for cursorline
+ CLEAR_FIELD(sattrs);
+ int num_signs = get_sign_attrs(buf, lnum, sattrs, &line_attr, &sign_num_attr, &sign_cul_attr);
+
+ // Highlight the current line in the quickfix window.
+ if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
+ line_attr = win_hl_attr(wp, HLF_QFL);
+ }
+
+ if (line_attr_lowprio || line_attr) {
+ area_highlighting = true;
+ }
+
+ if (cul_screenline) {
+ line_attr_save = line_attr;
+ line_attr_lowprio_save = line_attr_lowprio;
+ }
+
+ line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false);
+ ptr = line;
+
+ if (has_spell && !number_only) {
+ // For checking first word with a capital skip white space.
+ if (cap_col == 0) {
+ cap_col = (int)getwhitecols((char *)line);
+ }
+
+ // To be able to spell-check over line boundaries copy the end of the
+ // current line into nextline[]. Above the start of the next line was
+ // copied to nextline[SPWORDLEN].
+ if (nextline[SPWORDLEN] == NUL) {
+ // No next line or it is empty.
+ nextlinecol = MAXCOL;
+ nextline_idx = 0;
+ } else {
+ v = (long)STRLEN(line);
+ if (v < SPWORDLEN) {
+ // Short line, use it completely and append the start of the
+ // next line.
+ nextlinecol = 0;
+ memmove(nextline, line, (size_t)v);
+ STRMOVE(nextline + v, nextline + SPWORDLEN);
+ nextline_idx = (int)v + 1;
+ } else {
+ // Long line, use only the last SPWORDLEN bytes.
+ nextlinecol = (int)v - SPWORDLEN;
+ memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512
+ nextline_idx = SPWORDLEN + 1;
+ }
+ }
+ }
+
+ if (wp->w_p_list && !has_fold && !end_fill) {
+ if (wp->w_p_lcs_chars.space
+ || wp->w_p_lcs_chars.multispace != NULL
+ || wp->w_p_lcs_chars.leadmultispace != NULL
+ || wp->w_p_lcs_chars.trail
+ || wp->w_p_lcs_chars.lead
+ || wp->w_p_lcs_chars.nbsp) {
+ extra_check = true;
+ }
+ // find start of trailing whitespace
+ if (wp->w_p_lcs_chars.trail) {
+ trailcol = (colnr_T)STRLEN(ptr);
+ while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
+ trailcol--;
+ }
+ trailcol += (colnr_T)(ptr - line);
+ }
+ // find end of leading whitespace
+ if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) {
+ leadcol = 0;
+ while (ascii_iswhite(ptr[leadcol])) {
+ leadcol++;
+ }
+ if (ptr[leadcol] == NUL) {
+ // in a line full of spaces all of them are treated as trailing
+ leadcol = (colnr_T)0;
+ } else {
+ // keep track of the first column not filled with spaces
+ leadcol += (colnr_T)(ptr - line) + 1;
+ }
+ }
+ }
+
+ // 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
+ // first character to be displayed.
+ if (wp->w_p_wrap) {
+ v = wp->w_skipcol;
+ } else {
+ v = wp->w_leftcol;
+ }
+ if (v > 0 && !number_only) {
+ char_u *prev_ptr = ptr;
+ chartabsize_T cts;
+ int charsize;
+
+ init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, ptr);
+ while (cts.cts_vcol < v && *cts.cts_ptr != NUL) {
+ charsize = win_lbr_chartabsize(&cts, NULL);
+ cts.cts_vcol += charsize;
+ prev_ptr = (char_u *)cts.cts_ptr;
+ MB_PTR_ADV(cts.cts_ptr);
+ }
+ vcol = cts.cts_vcol;
+ ptr = (char_u *)cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
+
+ // When:
+ // - 'cuc' is set, or
+ // - 'colorcolumn' is set, or
+ // - 'virtualedit' is set, or
+ // - the visual mode is active,
+ // the end of the line may be before the start of the displayed part.
+ if (vcol < v && (wp->w_p_cuc
+ || draw_color_col
+ || virtual_active()
+ || (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
+ vcol = v;
+ }
+
+ // Handle a character that's not completely on the screen: Put ptr at
+ // that character but skip the first few screen characters.
+ if (vcol > v) {
+ vcol -= charsize;
+ ptr = prev_ptr;
+ // If the character fits on the screen, don't need to skip it.
+ // Except for a TAB.
+ if (utf_ptr2cells((char *)ptr) >= charsize || *ptr == TAB) {
+ n_skip = (int)(v - vcol);
+ }
+ }
+
+ // Adjust for when the inverted text is before the screen,
+ // and when the start of the inverted text is before the screen.
+ if (tocol <= vcol) {
+ fromcol = 0;
+ } else if (fromcol >= 0 && fromcol < vcol) {
+ fromcol = (int)vcol;
+ }
+
+ // When w_skipcol is non-zero, first line needs 'showbreak'
+ if (wp->w_p_wrap) {
+ need_showbreak = true;
+ }
+ // When spell checking a word we need to figure out the start of the
+ // word and if it's badly spelled or not.
+ if (has_spell) {
+ size_t len;
+ colnr_T linecol = (colnr_T)(ptr - line);
+ hlf_T spell_hlf = HLF_COUNT;
+
+ pos = wp->w_cursor;
+ wp->w_cursor.lnum = lnum;
+ wp->w_cursor.col = linecol;
+ len = spell_move_to(wp, FORWARD, true, true, &spell_hlf);
+
+ // spell_move_to() may call ml_get() and make "line" invalid
+ line = ml_get_buf(wp->w_buffer, lnum, false);
+ ptr = line + linecol;
+
+ if (len == 0 || (int)wp->w_cursor.col > ptr - line) {
+ // no bad word found at line start, don't check until end of a
+ // word
+ spell_hlf = HLF_COUNT;
+ word_end = (int)(spell_to_word_end(ptr, wp) - line + 1);
+ } else {
+ // bad word found, use attributes until end of word
+ assert(len <= INT_MAX);
+ word_end = wp->w_cursor.col + (int)len + 1;
+
+ // Turn index into actual attributes.
+ if (spell_hlf != HLF_COUNT) {
+ spell_attr = highlight_attr[spell_hlf];
+ }
+ }
+ wp->w_cursor = pos;
+
+ // Need to restart syntax highlighting for this line.
+ if (has_syntax) {
+ syntax_start(wp, lnum);
+ }
+ }
+ }
+
+ // Correct highlighting for cursor that can't be disabled.
+ // Avoids having to check this for each character.
+ if (fromcol >= 0) {
+ if (noinvcur) {
+ if ((colnr_T)fromcol == wp->w_virtcol) {
+ // highlighting starts at cursor, let it start just after the
+ // cursor
+ fromcol_prev = fromcol;
+ fromcol = -1;
+ } else if ((colnr_T)fromcol < wp->w_virtcol) {
+ // restart highlighting after the cursor
+ fromcol_prev = wp->w_virtcol;
+ }
+ }
+ if (fromcol >= tocol) {
+ fromcol = -1;
+ }
+ }
+
+ if (!number_only && !has_fold && !end_fill) {
+ v = ptr - line;
+ area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
+ &line, &screen_search_hl, &search_attr,
+ &search_attr_from_match);
+ ptr = line + v; // "line" may have been updated
+ }
+
+ int off = 0; // Offset relative start of line
+ int col = 0; // Visual column on screen.
+ if (wp->w_p_rl) {
+ // Rightleft window: process the text in the normal direction, but put
+ // it in linebuf_char[off] from right to left. Start at the
+ // rightmost column of the window.
+ col = grid->cols - 1;
+ off += col;
+ }
+
+ // won't highlight after TERM_ATTRS_MAX columns
+ int term_attrs[TERM_ATTRS_MAX] = { 0 };
+ if (wp->w_buffer->terminal) {
+ terminal_get_line_attributes(wp->w_buffer->terminal, wp, lnum, term_attrs);
+ extra_check = true;
+ }
+
+ int sign_idx = 0;
+ // Repeat for the whole displayed line.
+ for (;;) {
+ int has_match_conc = 0; ///< match wants to conceal
+ int decor_conceal = 0;
+
+ bool did_decrement_ptr = false;
+
+ // Skip this quickly when working on the text.
+ if (draw_state != WL_LINE) {
+ if (cul_screenline) {
+ cul_attr = 0;
+ line_attr = line_attr_save;
+ line_attr_lowprio = line_attr_lowprio_save;
+ }
+
+ if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
+ draw_state = WL_CMDLINE;
+ if (cmdwin_type != 0 && wp == curwin) {
+ // Draw the cmdline character.
+ n_extra = 1;
+ c_extra = cmdwin_type;
+ c_final = NUL;
+ char_attr = win_hl_attr(wp, HLF_AT);
+ }
+ }
+
+ if (draw_state == WL_FOLD - 1 && n_extra == 0) {
+ int fdc = compute_foldcolumn(wp, 0);
+
+ draw_state = WL_FOLD;
+ if (fdc > 0) {
+ // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
+ // already be in use.
+ xfree(p_extra_free);
+ p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1);
+ n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
+ p_extra_free[n_extra] = NUL;
+ p_extra = p_extra_free;
+ c_extra = NUL;
+ c_final = NUL;
+ if (use_cursor_line_sign(wp, lnum)) {
+ char_attr = win_hl_attr(wp, HLF_CLF);
+ } else {
+ char_attr = win_hl_attr(wp, HLF_FC);
+ }
+ }
+ }
+
+ // sign column, this is hit until sign_idx reaches count
+ if (draw_state == WL_SIGN - 1 && n_extra == 0) {
+ draw_state = WL_SIGN;
+ // Show the sign column when there are any signs in this buffer
+ if (wp->w_scwidth > 0) {
+ get_sign_display_info(false, wp, lnum, sattrs, row,
+ startrow, filler_lines, filler_todo,
+ &c_extra, &c_final, extra, sizeof(extra),
+ &p_extra, &n_extra, &char_attr, sign_idx,
+ sign_cul_attr);
+ sign_idx++;
+ if (sign_idx < wp->w_scwidth) {
+ draw_state = WL_SIGN - 1;
+ } else {
+ sign_idx = 0;
+ }
+ }
+ }
+
+ if (draw_state == WL_NR - 1 && n_extra == 0) {
+ draw_state = WL_NR;
+ // Display the absolute or relative line number. After the
+ // first fill with blanks when the 'n' flag isn't in 'cpo'
+ if ((wp->w_p_nu || wp->w_p_rnu)
+ && (row == startrow + filler_lines
+ || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) {
+ // If 'signcolumn' is set to 'number' and a sign is present
+ // in 'lnum', then display the sign instead of the line
+ // number.
+ if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u' && num_signs > 0) {
+ get_sign_display_info(true, wp, lnum, sattrs, row,
+ startrow, filler_lines, filler_todo,
+ &c_extra, &c_final, extra, sizeof(extra),
+ &p_extra, &n_extra, &char_attr, sign_idx,
+ sign_cul_attr);
+ } else {
+ // Draw the line number (empty space after wrapping).
+ if (row == startrow + filler_lines) {
+ get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
+ if (wp->w_skipcol > 0) {
+ for (p_extra = extra; *p_extra == ' '; p_extra++) {
+ *p_extra = '-';
+ }
+ }
+ if (wp->w_p_rl) { // reverse line numbers
+ // like rl_mirror(), but keep the space at the end
+ char_u *p2 = (char_u *)skipwhite((char *)extra);
+ p2 = (char_u *)skiptowhite((char *)p2) - 1;
+ for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
+ const char_u t = *p1;
+ *p1 = *p2;
+ *p2 = t;
+ }
+ }
+ p_extra = extra;
+ c_extra = NUL;
+ } else {
+ c_extra = ' ';
+ }
+ c_final = NUL;
+ n_extra = number_width(wp) + 1;
+ if (sign_num_attr > 0) {
+ char_attr = sign_num_attr;
+ } else {
+ char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines);
+ }
+ }
+ }
+ }
+
+ if (draw_state == WL_NR && n_extra == 0) {
+ win_col_offset = off;
+ }
+
+ if (wp->w_briopt_sbr && draw_state == WL_BRI - 1
+ && n_extra == 0 && *get_showbreak_value(wp) != NUL) {
+ // draw indent after showbreak value
+ draw_state = WL_BRI;
+ } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) {
+ // after the showbreak, draw the breakindent
+ draw_state = WL_BRI - 1;
+ }
+
+ // draw 'breakindent': indent wrapped text accordingly
+ if (draw_state == WL_BRI - 1 && n_extra == 0) {
+ draw_state = WL_BRI;
+ // if need_showbreak is set, breakindent also applies
+ if (wp->w_p_bri && (row != startrow || need_showbreak)
+ && filler_lines == 0) {
+ char_attr = 0;
+
+ if (diff_hlf != (hlf_T)0) {
+ char_attr = win_hl_attr(wp, (int)diff_hlf);
+ }
+ p_extra = NULL;
+ c_extra = ' ';
+ c_final = NUL;
+ n_extra =
+ get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false));
+ if (row == startrow) {
+ n_extra -= win_col_off2(wp);
+ if (n_extra < 0) {
+ n_extra = 0;
+ }
+ }
+ if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
+ need_showbreak = false;
+ }
+ // Correct end of highlighted area for 'breakindent',
+ // required wen 'linebreak' is also set.
+ if (tocol == vcol) {
+ tocol += n_extra;
+ }
+ }
+ }
+
+ if (draw_state == WL_SBR - 1 && n_extra == 0) {
+ draw_state = WL_SBR;
+ if (filler_todo > filler_lines - n_virt_lines) {
+ // TODO(bfredl): check this doesn't inhibit TUI-style
+ // clear-to-end-of-line.
+ c_extra = ' ';
+ c_final = NUL;
+ if (wp->w_p_rl) {
+ n_extra = col + 1;
+ } else {
+ n_extra = grid->cols - col;
+ }
+ char_attr = 0;
+ } else if (filler_todo > 0) {
+ // Draw "deleted" diff line(s)
+ if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
+ c_extra = '-';
+ c_final = NUL;
+ } else {
+ c_extra = wp->w_p_fcs_chars.diff;
+ c_final = NUL;
+ }
+ if (wp->w_p_rl) {
+ n_extra = col + 1;
+ } else {
+ n_extra = grid->cols - col;
+ }
+ char_attr = win_hl_attr(wp, HLF_DED);
+ }
+ char_u *const sbr = get_showbreak_value(wp);
+ if (*sbr != NUL && need_showbreak) {
+ // Draw 'showbreak' at the start of each broken line.
+ p_extra = sbr;
+ c_extra = NUL;
+ c_final = NUL;
+ n_extra = (int)STRLEN(sbr);
+ char_attr = win_hl_attr(wp, HLF_AT);
+ if (wp->w_skipcol == 0 || !wp->w_p_wrap) {
+ need_showbreak = false;
+ }
+ vcol_sbr = vcol + mb_charlen(sbr);
+ // Correct end of highlighted area for 'showbreak',
+ // required when 'linebreak' is also set.
+ if (tocol == vcol) {
+ tocol += n_extra;
+ }
+ // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
+ if (cul_attr) {
+ char_attr = hl_combine_attr(cul_attr, char_attr);
+ }
+ }
+ }
+
+ if (draw_state == WL_LINE - 1 && n_extra == 0) {
+ sign_idx = 0;
+ draw_state = WL_LINE;
+
+ if (has_decor && row == startrow + filler_lines) {
+ // hide virt_text on text hidden by 'nowrap'
+ decor_redraw_col(wp->w_buffer, (int)vcol, off, true, &decor_state);
+ }
+
+ if (saved_n_extra) {
+ // Continue item from end of wrapped line.
+ n_extra = saved_n_extra;
+ c_extra = saved_c_extra;
+ c_final = saved_c_final;
+ p_extra = saved_p_extra;
+ char_attr = saved_char_attr;
+ } else {
+ char_attr = 0;
+ }
+ }
+ }
+
+ if (cul_screenline && draw_state == WL_LINE
+ && vcol >= left_curline_col
+ && vcol < right_curline_col) {
+ apply_cursorline_highlight(wp, lnum, &line_attr, &cul_attr, &line_attr_lowprio);
+ }
+
+ // When still displaying '$' of change command, stop at cursor
+ if (((dollar_vcol >= 0
+ && wp == curwin
+ && lnum == wp->w_cursor.lnum
+ && vcol >= (long)wp->w_virtcol)
+ || (number_only && draw_state > WL_NR))
+ && filler_todo <= 0) {
+ draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
+ grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp, bg_attr, false);
+ // Pretend we have finished updating the window. Except when
+ // 'cursorcolumn' is set.
+ if (wp->w_p_cuc) {
+ row = wp->w_cline_row + wp->w_cline_height;
+ } else {
+ row = grid->rows;
+ }
+ break;
+ }
+
+ if (draw_state == WL_LINE
+ && has_fold
+ && col == win_col_offset
+ && n_extra == 0
+ && row == startrow) {
+ char_attr = win_hl_attr(wp, HLF_FL);
+
+ linenr_T lnume = lnum + foldinfo.fi_lines - 1;
+ memset(buf_fold, ' ', FOLD_TEXT_LEN);
+ p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ n_extra = (int)STRLEN(p_extra);
+
+ if (p_extra != buf_fold) {
+ xfree(p_extra_free);
+ p_extra_free = p_extra;
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ p_extra[n_extra] = NUL;
+ }
+
+ if (draw_state == WL_LINE
+ && has_fold
+ && col < grid->cols
+ && n_extra == 0
+ && row == startrow) {
+ // fill rest of line with 'fold'
+ c_extra = wp->w_p_fcs_chars.fold;
+ c_final = NUL;
+
+ n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col);
+ }
+
+ if (draw_state == WL_LINE
+ && has_fold
+ && col >= grid->cols
+ && n_extra != 0
+ && row == startrow) {
+ // Truncate the folding.
+ n_extra = 0;
+ }
+
+ if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
+ // handle Visual or match highlighting in this line
+ if (vcol == fromcol
+ || (vcol + 1 == fromcol && n_extra == 0
+ && utf_ptr2cells((char *)ptr) > 1)
+ || ((int)vcol_prev == fromcol_prev
+ && vcol_prev < vcol // not at margin
+ && vcol < tocol)) {
+ area_attr = attr; // start highlighting
+ if (area_highlighting) {
+ area_active = true;
+ }
+ } else if (area_attr != 0 && (vcol == tocol
+ || (noinvcur
+ && (colnr_T)vcol == wp->w_virtcol))) {
+ area_attr = 0; // stop highlighting
+ area_active = false;
+ }
+
+ if (!n_extra) {
+ // Check for start/end of 'hlsearch' and other matches.
+ // After end, check for start/end of next match.
+ // When another match, have to check for start again.
+ v = (ptr - line);
+ search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &screen_search_hl,
+ &has_match_conc,
+ &match_conc, lcs_eol_one, &search_attr_from_match);
+ ptr = line + v; // "line" may have been changed
+
+ // Do not allow a conceal over EOL otherwise EOL will be missed
+ // and bad things happen.
+ if (*ptr == NUL) {
+ has_match_conc = 0;
+ }
+ }
+
+ if (diff_hlf != (hlf_T)0) {
+ if (diff_hlf == HLF_CHD && ptr - line >= change_start
+ && n_extra == 0) {
+ diff_hlf = HLF_TXD; // changed text
+ }
+ if (diff_hlf == HLF_TXD && ptr - line > change_end
+ && n_extra == 0) {
+ diff_hlf = HLF_CHD; // changed line
+ }
+ line_attr = win_hl_attr(wp, (int)diff_hlf);
+ // Overlay CursorLine onto diff-mode highlight.
+ if (cul_attr) {
+ line_attr = 0 != line_attr_lowprio // Low-priority CursorLine
+ ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr),
+ hl_get_underline())
+ : hl_combine_attr(line_attr, cul_attr);
+ }
+ }
+
+ // Decide which of the highlight attributes to use.
+ attr_pri = true;
+
+ if (area_attr != 0) {
+ char_attr = hl_combine_attr(line_attr, area_attr);
+ if (!highlight_match) {
+ // let search highlight show in Visual area if possible
+ char_attr = hl_combine_attr(search_attr, char_attr);
+ }
+ } else if (search_attr != 0) {
+ char_attr = hl_combine_attr(line_attr, search_attr);
+ } else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL)
+ || vcol < fromcol || vcol_prev < fromcol_prev
+ || vcol >= tocol)) {
+ // Use line_attr when not in the Visual or 'incsearch' area
+ // (area_attr may be 0 when "noinvcur" is set).
+ char_attr = line_attr;
+ } else {
+ attr_pri = false;
+ if (has_syntax) {
+ char_attr = syntax_attr;
+ } else {
+ char_attr = 0;
+ }
+ }
+ }
+
+ // Get the next character to put on the screen.
+ //
+ // The "p_extra" points to the extra stuff that is inserted to
+ // represent special characters (non-printable stuff) and other
+ // things. When all characters are the same, c_extra is used.
+ // If c_final is set, it will compulsorily be used at the end.
+ // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past
+ // "p_extra[n_extra]".
+ // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
+ if (n_extra > 0) {
+ if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
+ c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
+ mb_c = c; // doesn't handle non-utf-8 multi-byte!
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ } else {
+ assert(p_extra != NULL);
+ c = *p_extra;
+ mb_c = c;
+ // If the UTF-8 character is more than one byte:
+ // Decode it into "mb_c".
+ mb_l = utfc_ptr2len((char *)p_extra);
+ mb_utf8 = false;
+ if (mb_l > n_extra) {
+ mb_l = 1;
+ } else if (mb_l > 1) {
+ mb_c = utfc_ptr2char((char *)p_extra, u8cc);
+ mb_utf8 = true;
+ c = 0xc0;
+ }
+ if (mb_l == 0) { // at the NUL at end-of-line
+ mb_l = 1;
+ }
+
+ // If a double-width char doesn't fit display a '>' in the last column.
+ if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1))
+ && utf_char2cells(mb_c) == 2) {
+ c = '>';
+ mb_c = c;
+ mb_l = 1;
+ (void)mb_l;
+ multi_attr = win_hl_attr(wp, HLF_AT);
+
+ if (cul_attr) {
+ multi_attr = 0 != line_attr_lowprio
+ ? hl_combine_attr(cul_attr, multi_attr)
+ : hl_combine_attr(multi_attr, cul_attr);
+ }
+
+ // put the pointer back to output the double-width
+ // character at the start of the next line.
+ n_extra++;
+ p_extra--;
+ } else {
+ n_extra -= mb_l - 1;
+ p_extra += mb_l - 1;
+ }
+ p_extra++;
+ }
+ n_extra--;
+ } else if (foldinfo.fi_lines > 0) {
+ // skip writing the buffer line itself
+ c = NUL;
+ XFREE_CLEAR(p_extra_free);
+ } else {
+ int c0;
+
+ XFREE_CLEAR(p_extra_free);
+
+ // Get a character from the line itself.
+ c0 = c = *ptr;
+ mb_c = c;
+ // If the UTF-8 character is more than one byte: Decode it
+ // into "mb_c".
+ mb_l = utfc_ptr2len((char *)ptr);
+ mb_utf8 = false;
+ if (mb_l > 1) {
+ mb_c = utfc_ptr2char((char *)ptr, u8cc);
+ // Overlong encoded ASCII or ASCII with composing char
+ // is displayed normally, except a NUL.
+ if (mb_c < 0x80) {
+ c0 = c = mb_c;
+ }
+ mb_utf8 = true;
+
+ // At start of the line we can have a composing char.
+ // Draw it as a space with a composing char.
+ if (utf_iscomposing(mb_c)) {
+ int i;
+
+ for (i = MAX_MCO - 1; i > 0; i--) {
+ u8cc[i] = u8cc[i - 1];
+ }
+ u8cc[0] = mb_c;
+ mb_c = ' ';
+ }
+ }
+
+ if ((mb_l == 1 && c >= 0x80)
+ || (mb_l >= 1 && mb_c == 0)
+ || (mb_l > 1 && (!vim_isprintc(mb_c)))) {
+ // Illegal UTF-8 byte: display as <xx>.
+ // Non-BMP character : display as ? or fullwidth ?.
+ transchar_hex((char *)extra, mb_c);
+ if (wp->w_p_rl) { // reverse
+ rl_mirror(extra);
+ }
+
+ p_extra = extra;
+ c = *p_extra;
+ mb_c = mb_ptr2char_adv((const char_u **)&p_extra);
+ mb_utf8 = (c >= 0x80);
+ n_extra = (int)STRLEN(p_extra);
+ c_extra = NUL;
+ c_final = NUL;
+ if (area_attr == 0 && search_attr == 0) {
+ n_attr = n_extra + 1;
+ extra_attr = win_hl_attr(wp, HLF_8);
+ saved_attr2 = char_attr; // save current attr
+ }
+ } else if (mb_l == 0) { // at the NUL at end-of-line
+ mb_l = 1;
+ } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) {
+ // Do Arabic shaping.
+ int pc, pc1, nc;
+ int pcc[MAX_MCO];
+
+ // The idea of what is the previous and next
+ // character depends on 'rightleft'.
+ if (wp->w_p_rl) {
+ pc = prev_c;
+ pc1 = prev_c1;
+ nc = utf_ptr2char((char *)ptr + mb_l);
+ prev_c1 = u8cc[0];
+ } else {
+ pc = utfc_ptr2char((char *)ptr + mb_l, pcc);
+ nc = prev_c;
+ pc1 = pcc[0];
+ }
+ prev_c = mb_c;
+
+ mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc);
+ } else {
+ prev_c = mb_c;
+ }
+ // If a double-width char doesn't fit display a '>' in the
+ // last column; the character is displayed at the start of the
+ // next line.
+ if ((wp->w_p_rl ? (col <= 0) :
+ (col >= grid->cols - 1))
+ && utf_char2cells(mb_c) == 2) {
+ c = '>';
+ mb_c = c;
+ mb_utf8 = false;
+ mb_l = 1;
+ multi_attr = win_hl_attr(wp, HLF_AT);
+ // Put pointer back so that the character will be
+ // displayed at the start of the next line.
+ ptr--;
+ did_decrement_ptr = true;
+ } else if (*ptr != NUL) {
+ ptr += mb_l - 1;
+ }
+
+ // If a double-width char doesn't fit at the left side display a '<' in
+ // the first column. Don't do this for unprintable characters.
+ if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
+ n_extra = 1;
+ c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
+ c = ' ';
+ if (area_attr == 0 && search_attr == 0) {
+ n_attr = n_extra + 1;
+ extra_attr = win_hl_attr(wp, HLF_AT);
+ saved_attr2 = char_attr; // save current attr
+ }
+ mb_c = c;
+ mb_utf8 = false;
+ mb_l = 1;
+ }
+ ptr++;
+
+ if (extra_check) {
+ bool can_spell = true;
+
+ // Get syntax attribute, unless still at the start of the line
+ // (double-wide char that doesn't fit).
+ v = (ptr - line);
+ if (has_syntax && v > 0) {
+ // Get the syntax attribute for the character. If there
+ // is an error, disable syntax highlighting.
+ save_did_emsg = did_emsg;
+ did_emsg = false;
+
+ syntax_attr = get_syntax_attr((colnr_T)v - 1,
+ has_spell ? &can_spell : NULL, false);
+
+ if (did_emsg) {
+ wp->w_s->b_syn_error = true;
+ has_syntax = false;
+ } else {
+ did_emsg = save_did_emsg;
+ }
+
+ if (wp->w_s->b_syn_slow) {
+ has_syntax = false;
+ }
+
+ // Need to get the line again, a multi-line regexp may
+ // have made it invalid.
+ line = ml_get_buf(wp->w_buffer, lnum, false);
+ ptr = line + v;
+
+ if (!attr_pri) {
+ if (cul_attr) {
+ char_attr = 0 != line_attr_lowprio
+ ? hl_combine_attr(cul_attr, syntax_attr)
+ : hl_combine_attr(syntax_attr, cul_attr);
+ } else {
+ char_attr = syntax_attr;
+ }
+ } else {
+ char_attr = hl_combine_attr(syntax_attr, char_attr);
+ }
+ // no concealing past the end of the line, it interferes
+ // with line highlighting.
+ if (c == NUL) {
+ syntax_flags = 0;
+ } else {
+ syntax_flags = get_syntax_info(&syntax_seqnr);
+ }
+ } else if (!attr_pri) {
+ char_attr = 0;
+ }
+
+ // Check spelling (unless at the end of the line).
+ // Only do this when there is no syntax highlighting, the
+ // @Spell cluster is not used or the current syntax item
+ // contains the @Spell cluster.
+ v = (ptr - line);
+ if (has_spell && v >= word_end && v > cur_checked_col) {
+ spell_attr = 0;
+ if (!attr_pri) {
+ char_attr = syntax_attr;
+ }
+ if (c != 0 && (!has_syntax || can_spell)) {
+ char_u *prev_ptr;
+ char_u *p;
+ int len;
+ hlf_T spell_hlf = HLF_COUNT;
+ prev_ptr = ptr - mb_l;
+ v -= mb_l - 1;
+
+ // Use nextline[] if possible, it has the start of the
+ // next line concatenated.
+ if ((prev_ptr - line) - nextlinecol >= 0) {
+ p = nextline + ((prev_ptr - line) - nextlinecol);
+ } else {
+ p = prev_ptr;
+ }
+ cap_col -= (int)(prev_ptr - line);
+ size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange);
+ assert(tmplen <= INT_MAX);
+ len = (int)tmplen;
+ word_end = (int)v + len;
+
+ // In Insert mode only highlight a word that
+ // doesn't touch the cursor.
+ if (spell_hlf != HLF_COUNT
+ && (State & MODE_INSERT)
+ && wp->w_cursor.lnum == lnum
+ && wp->w_cursor.col >=
+ (colnr_T)(prev_ptr - line)
+ && wp->w_cursor.col < (colnr_T)word_end) {
+ spell_hlf = HLF_COUNT;
+ spell_redraw_lnum = lnum;
+ }
+
+ if (spell_hlf == HLF_COUNT && p != prev_ptr
+ && (p - nextline) + len > nextline_idx) {
+ // Remember that the good word continues at the
+ // start of the next line.
+ checked_lnum = lnum + 1;
+ checked_col = (int)((p - nextline) + len - nextline_idx);
+ }
+
+ // Turn index into actual attributes.
+ if (spell_hlf != HLF_COUNT) {
+ spell_attr = highlight_attr[spell_hlf];
+ }
+
+ if (cap_col > 0) {
+ if (p != prev_ptr
+ && (p - nextline) + cap_col >= nextline_idx) {
+ // Remember that the word in the next line
+ // must start with a capital.
+ capcol_lnum = lnum + 1;
+ cap_col = (int)((p - nextline) + cap_col
+ - nextline_idx);
+ } else {
+ // Compute the actual column.
+ cap_col += (int)(prev_ptr - line);
+ }
+ }
+ }
+ }
+ if (spell_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, spell_attr);
+ } else {
+ char_attr = hl_combine_attr(spell_attr, char_attr);
+ }
+ }
+
+ if (wp->w_buffer->terminal) {
+ char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
+ }
+
+ if (has_decor && v > 0) {
+ bool selected = (area_active || (area_highlighting && noinvcur
+ && (colnr_T)vcol == wp->w_virtcol));
+ int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off,
+ selected, &decor_state);
+ if (extmark_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, extmark_attr);
+ } else {
+ char_attr = hl_combine_attr(extmark_attr, char_attr);
+ }
+ }
+
+ decor_conceal = decor_state.conceal;
+ if (decor_conceal && decor_state.conceal_char) {
+ decor_conceal = 2; // really??
+ }
+ }
+
+ // Found last space before word: check for line break.
+ if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
+ && !vim_isbreak((int)(*ptr))) {
+ int mb_off = utf_head_off(line, ptr - 1);
+ char_u *p = ptr - (mb_off + 1);
+ chartabsize_T cts;
+
+ init_chartabsize_arg(&cts, wp, lnum, (colnr_T)vcol, line, p);
+ n_extra = win_lbr_chartabsize(&cts, NULL) - 1;
+
+ // We have just drawn the showbreak value, no need to add
+ // space for it again.
+ if (vcol == vcol_sbr) {
+ n_extra -= mb_charlen(get_showbreak_value(wp));
+ if (n_extra < 0) {
+ n_extra = 0;
+ }
+ }
+
+ if (c == TAB && n_extra + col > grid->cols) {
+ n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array) - 1;
+ }
+ c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
+ c_final = NUL;
+ if (ascii_iswhite(c)) {
+ if (c == TAB) {
+ // See "Tab alignment" below.
+ FIX_FOR_BOGUSCOLS;
+ }
+ if (!wp->w_p_list) {
+ c = ' ';
+ }
+ }
+ clear_chartabsize_arg(&cts);
+ }
+
+ in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
+ if (!in_multispace) {
+ multispace_pos = 0;
+ }
+
+ // 'list': Change char 160 to 'nbsp' and space to 'space'.
+ // But not when the character is followed by a composing
+ // character (use mb_l to check that).
+ if (wp->w_p_list
+ && ((((c == 160 && mb_l == 1)
+ || (mb_utf8
+ && ((mb_c == 160 && mb_l == 2)
+ || (mb_c == 0x202f && mb_l == 3))))
+ && wp->w_p_lcs_chars.nbsp)
+ || (c == ' '
+ && mb_l == 1
+ && (wp->w_p_lcs_chars.space
+ || (in_multispace && wp->w_p_lcs_chars.multispace != NULL))
+ && ptr - line >= leadcol
+ && ptr - line <= trailcol))) {
+ if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) {
+ c = wp->w_p_lcs_chars.multispace[multispace_pos++];
+ if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ } else {
+ c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
+ }
+ n_attr = 1;
+ extra_attr = win_hl_attr(wp, HLF_0);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ }
+
+ if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol)
+ || (leadcol != 0 && ptr < line + leadcol))) {
+ if (leadcol != 0 && in_multispace && ptr < line + leadcol
+ && wp->w_p_lcs_chars.leadmultispace != NULL) {
+ c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
+ if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
+ multispace_pos = 0;
+ }
+ } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
+ c = wp->w_p_lcs_chars.trail;
+ } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
+ c = wp->w_p_lcs_chars.lead;
+ } else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
+ c = wp->w_p_lcs_chars.space;
+ }
+
+ n_attr = 1;
+ extra_attr = win_hl_attr(wp, HLF_0);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ }
+ }
+
+ // Handling of non-printable characters.
+ if (!vim_isprintc(c)) {
+ // when getting a character from the file, we may have to
+ // turn it into something else on the way to putting it on the screen.
+ if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
+ int tab_len = 0;
+ long vcol_adjusted = vcol; // removed showbreak length
+ char_u *const sbr = get_showbreak_value(wp);
+
+ // Only adjust the tab_len, when at the first column after the
+ // showbreak value was drawn.
+ if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) {
+ vcol_adjusted = vcol - mb_charlen(sbr);
+ }
+ // tab amount depends on current column
+ tab_len = tabstop_padding((colnr_T)vcol_adjusted,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array) - 1;
+
+ if (!wp->w_p_lbr || !wp->w_p_list) {
+ n_extra = tab_len;
+ } else {
+ char_u *p;
+ int i;
+ int saved_nextra = n_extra;
+
+ if (vcol_off > 0) {
+ // there are characters to conceal
+ tab_len += vcol_off;
+ }
+ // boguscols before FIX_FOR_BOGUSCOLS macro from above.
+ if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
+ && n_extra > tab_len) {
+ tab_len += n_extra - tab_len;
+ }
+
+ // If n_extra > 0, it gives the number of chars
+ // to use for a tab, else we need to calculate the width
+ // for a tab.
+ int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2));
+ if (wp->w_p_lcs_chars.tab3) {
+ len += utf_char2len(wp->w_p_lcs_chars.tab3);
+ }
+ if (n_extra > 0) {
+ len += n_extra - tab_len;
+ }
+ c = wp->w_p_lcs_chars.tab1;
+ p = xmalloc((size_t)len + 1);
+ memset(p, ' ', (size_t)len);
+ p[len] = NUL;
+ xfree(p_extra_free);
+ p_extra_free = p;
+ for (i = 0; i < tab_len; i++) {
+ if (*p == NUL) {
+ tab_len = i;
+ break;
+ }
+ int lcs = wp->w_p_lcs_chars.tab2;
+
+ // if tab3 is given, use it for the last char
+ if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
+ lcs = wp->w_p_lcs_chars.tab3;
+ }
+ p += utf_char2bytes(lcs, (char *)p);
+ n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
+ }
+ p_extra = p_extra_free;
+
+ // n_extra will be increased by FIX_FOX_BOGUSCOLS
+ // macro below, so need to adjust for that here
+ if (vcol_off > 0) {
+ n_extra -= vcol_off;
+ }
+ }
+
+ {
+ int vc_saved = vcol_off;
+
+ // Tab alignment should be identical regardless of
+ // 'conceallevel' value. So tab compensates of all
+ // previous concealed characters, and thus resets
+ // vcol_off and boguscols accumulated so far in the
+ // line. Note that the tab can be longer than
+ // 'tabstop' when there are concealed characters.
+ FIX_FOR_BOGUSCOLS;
+
+ // Make sure, the highlighting for the tab char will be
+ // correctly set further below (effectively reverts the
+ // FIX_FOR_BOGSUCOLS macro).
+ if (n_extra == tab_len + vc_saved && wp->w_p_list
+ && wp->w_p_lcs_chars.tab1) {
+ tab_len += vc_saved;
+ }
+ }
+
+ mb_utf8 = false; // don't draw as UTF-8
+ if (wp->w_p_list) {
+ c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
+ ? wp->w_p_lcs_chars.tab3
+ : wp->w_p_lcs_chars.tab1;
+ if (wp->w_p_lbr) {
+ c_extra = NUL; // using p_extra from above
+ } else {
+ c_extra = wp->w_p_lcs_chars.tab2;
+ }
+ c_final = wp->w_p_lcs_chars.tab3;
+ n_attr = tab_len + 1;
+ extra_attr = win_hl_attr(wp, HLF_0);
+ saved_attr2 = char_attr; // save current attr
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ } else {
+ c_final = NUL;
+ c_extra = ' ';
+ c = ' ';
+ }
+ } else if (c == NUL
+ && (wp->w_p_list
+ || ((fromcol >= 0 || fromcol_prev >= 0)
+ && tocol > vcol
+ && VIsual_mode != Ctrl_V
+ && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))
+ && !(noinvcur
+ && lnum == wp->w_cursor.lnum
+ && (colnr_T)vcol == wp->w_virtcol)))
+ && lcs_eol_one > 0) {
+ // Display a '$' after the line or highlight an extra
+ // character if the line break is included.
+ // For a diff line the highlighting continues after the "$".
+ if (diff_hlf == (hlf_T)0
+ && line_attr == 0
+ && line_attr_lowprio == 0) {
+ // In virtualedit, visual selections may extend beyond end of line
+ if (area_highlighting && virtual_active()
+ && tocol != MAXCOL && vcol < tocol) {
+ n_extra = 0;
+ } else {
+ p_extra = at_end_str;
+ n_extra = 1;
+ c_extra = NUL;
+ c_final = NUL;
+ }
+ }
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
+ c = wp->w_p_lcs_chars.eol;
+ } else {
+ c = ' ';
+ }
+ lcs_eol_one = -1;
+ ptr--; // put it back at the NUL
+ extra_attr = win_hl_attr(wp, HLF_AT);
+ n_attr = 1;
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ } else if (c != NUL) {
+ p_extra = transchar_buf(wp->w_buffer, c);
+ if (n_extra == 0) {
+ n_extra = byte2cells(c) - 1;
+ }
+ if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
+ rl_mirror(p_extra); // reverse "<12>"
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ if (wp->w_p_lbr) {
+ char_u *p;
+
+ c = *p_extra;
+ p = xmalloc((size_t)n_extra + 1);
+ memset(p, ' ', (size_t)n_extra);
+ STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf)
+ p[n_extra] = NUL;
+ xfree(p_extra_free);
+ p_extra_free = p_extra = p;
+ } else {
+ n_extra = byte2cells(c) - 1;
+ c = *p_extra++;
+ }
+ n_attr = n_extra + 1;
+ extra_attr = win_hl_attr(wp, HLF_8);
+ saved_attr2 = char_attr; // save current attr
+ mb_utf8 = false; // don't draw as UTF-8
+ } else if (VIsual_active
+ && (VIsual_mode == Ctrl_V || VIsual_mode == 'v')
+ && virtual_active()
+ && tocol != MAXCOL
+ && vcol < tocol
+ && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) {
+ c = ' ';
+ ptr--; // put it back at the NUL
+ }
+ }
+
+ if (wp->w_p_cole > 0
+ && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp))
+ && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)
+ && !(lnum_in_visual_area && vim_strchr(wp->w_p_cocu, 'v') == NULL)) {
+ char_attr = conceal_attr;
+ if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
+ || has_match_conc > 1 || decor_conceal > 1)
+ && (syn_get_sub_char() != NUL
+ || (has_match_conc && match_conc)
+ || (decor_conceal && decor_state.conceal_char)
+ || wp->w_p_cole == 1)
+ && wp->w_p_cole != 3) {
+ // First time at this concealed item: display one
+ // character.
+ if (has_match_conc && match_conc) {
+ c = match_conc;
+ } else if (decor_conceal && decor_state.conceal_char) {
+ c = decor_state.conceal_char;
+ if (decor_state.conceal_attr) {
+ char_attr = decor_state.conceal_attr;
+ }
+ } else if (syn_get_sub_char() != NUL) {
+ c = syn_get_sub_char();
+ } else if (wp->w_p_lcs_chars.conceal != NUL) {
+ c = wp->w_p_lcs_chars.conceal;
+ } else {
+ c = ' ';
+ }
+
+ prev_syntax_id = syntax_seqnr;
+
+ if (n_extra > 0) {
+ vcol_off += n_extra;
+ }
+ vcol += n_extra;
+ if (wp->w_p_wrap && n_extra > 0) {
+ if (wp->w_p_rl) {
+ col -= n_extra;
+ boguscols -= n_extra;
+ } else {
+ boguscols += n_extra;
+ col += n_extra;
+ }
+ }
+ n_extra = 0;
+ n_attr = 0;
+ } else if (n_skip == 0) {
+ is_concealing = true;
+ n_skip = 1;
+ }
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ } else {
+ prev_syntax_id = 0;
+ is_concealing = false;
+ }
+
+ if (n_skip > 0 && did_decrement_ptr) {
+ // not showing the '>', put pointer back to avoid getting stuck
+ ptr++;
+ }
+ } // end of printing from buffer content
+
+ // In the cursor line and we may be concealing characters: correct
+ // the cursor column when we reach its position.
+ if (!did_wcol && draw_state == WL_LINE
+ && wp == curwin && lnum == wp->w_cursor.lnum
+ && conceal_cursor_line(wp)
+ && (int)wp->w_virtcol <= vcol + n_skip) {
+ if (wp->w_p_rl) {
+ wp->w_wcol = grid->cols - col + boguscols - 1;
+ } else {
+ wp->w_wcol = col - boguscols;
+ }
+ wp->w_wrow = row;
+ did_wcol = true;
+ wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
+ }
+
+ // Don't override visual selection highlighting.
+ if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) {
+ char_attr = hl_combine_attr(char_attr, extra_attr);
+ }
+
+ // Handle the case where we are in column 0 but not on the first
+ // character of the line and the user wants us to show us a
+ // special character (via 'listchars' option "precedes:<char>".
+ if (lcs_prec_todo != NUL
+ && wp->w_p_list
+ && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0)
+ && filler_todo <= 0
+ && draw_state > WL_NR
+ && c != NUL) {
+ c = wp->w_p_lcs_chars.prec;
+ lcs_prec_todo = NUL;
+ if (utf_char2cells(mb_c) > 1) {
+ // Double-width character being overwritten by the "precedes"
+ // character, need to fill up half the character.
+ c_extra = MB_FILLER_CHAR;
+ c_final = NUL;
+ n_extra = 1;
+ n_attr = 2;
+ extra_attr = win_hl_attr(wp, HLF_AT);
+ }
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ saved_attr3 = char_attr; // save current attr
+ char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
+ n_attr3 = 1;
+ }
+
+ // At end of the text line or just after the last character.
+ if (c == NUL && eol_hl_off == 0) {
+ // flag to indicate whether prevcol equals startcol of search_hl or
+ // one of the matches
+ bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &screen_search_hl,
+ (long)(ptr - line) - 1);
+
+ // Invert at least one char, used for Visual and empty line or
+ // highlight match at end of line. If it's beyond the last
+ // char on the screen, just overwrite that one (tricky!) Not
+ // needed when a '$' was displayed for 'list'.
+ if (wp->w_p_lcs_chars.eol == lcs_eol_one
+ && ((area_attr != 0 && vcol == fromcol
+ && (VIsual_mode != Ctrl_V
+ || lnum == VIsual.lnum
+ || lnum == curwin->w_cursor.lnum))
+ // highlight 'hlsearch' match at end of line
+ || prevcol_hl_flag)) {
+ int n = 0;
+
+ if (wp->w_p_rl) {
+ if (col < 0) {
+ n = 1;
+ }
+ } else {
+ if (col >= grid->cols) {
+ n = -1;
+ }
+ }
+ if (n != 0) {
+ // At the window boundary, highlight the last character
+ // instead (better than nothing).
+ off += n;
+ col += n;
+ } else {
+ // Add a blank character to highlight.
+ schar_from_ascii(linebuf_char[off], ' ');
+ }
+ if (area_attr == 0 && !has_fold) {
+ // Use attributes from match with highest priority among
+ // 'search_hl' and the match list.
+ get_search_match_hl(wp, &screen_search_hl, (long)(ptr - line), &char_attr);
+ }
+
+ int eol_attr = char_attr;
+ if (cul_attr) {
+ eol_attr = hl_combine_attr(cul_attr, eol_attr);
+ }
+ linebuf_attr[off] = eol_attr;
+ if (wp->w_p_rl) {
+ col--;
+ off--;
+ } else {
+ col++;
+ off++;
+ }
+ vcol++;
+ eol_hl_off = 1;
+ }
+ // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
+ if (wp->w_p_wrap) {
+ v = wp->w_skipcol;
+ } else {
+ v = wp->w_leftcol;
+ }
+
+ // check if line ends before left margin
+ if (vcol < v + col - win_col_off(wp)) {
+ vcol = v + col - win_col_off(wp);
+ }
+ // Get rid of the boguscols now, we want to draw until the right
+ // edge for 'cursorcolumn'.
+ col -= boguscols;
+ // boguscols = 0; // Disabled because value never read after this
+
+ if (draw_color_col) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ bool has_virttext = false;
+ // Make sure alignment is the same regardless
+ // if listchars=eol:X is used or not.
+ int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0
+ ? 1 : 0);
+
+ if (has_decor) {
+ has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr,
+ col + eol_skip);
+ }
+
+ if (((wp->w_p_cuc
+ && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
+ && (int)wp->w_virtcol <
+ (long)grid->cols * (row - startrow + 1) + v
+ && lnum != wp->w_cursor.lnum)
+ || draw_color_col || line_attr_lowprio || line_attr
+ || diff_hlf != (hlf_T)0 || has_virttext)) {
+ int rightmost_vcol = 0;
+ int i;
+
+ if (wp->w_p_cuc) {
+ rightmost_vcol = wp->w_virtcol;
+ }
+
+ if (draw_color_col) {
+ // determine rightmost colorcolumn to possibly draw
+ for (i = 0; color_cols[i] >= 0; i++) {
+ if (rightmost_vcol < color_cols[i]) {
+ rightmost_vcol = color_cols[i];
+ }
+ }
+ }
+
+ int cuc_attr = win_hl_attr(wp, HLF_CUC);
+ int mc_attr = win_hl_attr(wp, HLF_MC);
+
+ int diff_attr = 0;
+ if (diff_hlf == HLF_TXD) {
+ diff_hlf = HLF_CHD;
+ }
+ if (diff_hlf != 0) {
+ diff_attr = win_hl_attr(wp, (int)diff_hlf);
+ }
+
+ int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr);
+ if (base_attr || line_attr || has_virttext) {
+ rightmost_vcol = INT_MAX;
+ }
+
+ int col_stride = wp->w_p_rl ? -1 : 1;
+
+ while (wp->w_p_rl ? col >= 0 : col < grid->cols) {
+ schar_from_ascii(linebuf_char[off], ' ');
+ col += col_stride;
+ if (draw_color_col) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ int col_attr = base_attr;
+
+ if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
+ col_attr = cuc_attr;
+ } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ col_attr = mc_attr;
+ }
+
+ col_attr = hl_combine_attr(col_attr, line_attr);
+
+ linebuf_attr[off] = col_attr;
+ off += col_stride;
+
+ if (VCOL_HLC >= rightmost_vcol) {
+ break;
+ }
+
+ vcol += 1;
+ }
+ }
+
+ // TODO(bfredl): integrate with the common beyond-the-end-loop
+ if (wp->w_buffer->terminal) {
+ // terminal buffers may need to highlight beyond the end of the
+ // logical line
+ int n = wp->w_p_rl ? -1 : 1;
+ while (col >= 0 && col < grid->cols) {
+ schar_from_ascii(linebuf_char[off], ' ');
+ linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol];
+ off += n;
+ vcol += n;
+ col += n;
+ }
+ }
+
+ draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
+ grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp, bg_attr, false);
+ row++;
+
+ // Update w_cline_height and w_cline_folded if the cursor line was
+ // updated (saves a call to plines_win() later).
+ if (wp == curwin && lnum == curwin->w_cursor.lnum) {
+ curwin->w_cline_row = startrow;
+ curwin->w_cline_height = row - startrow;
+ curwin->w_cline_folded = foldinfo.fi_lines > 0;
+ curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
+ conceal_cursor_used = conceal_cursor_line(curwin);
+ }
+ break;
+ }
+
+ // Show "extends" character from 'listchars' if beyond the line end and
+ // 'list' is set.
+ if (wp->w_p_lcs_chars.ext != NUL
+ && draw_state == WL_LINE
+ && wp->w_p_list
+ && !wp->w_p_wrap
+ && filler_todo <= 0
+ && (wp->w_p_rl ? col == 0 : col == grid->cols - 1)
+ && !has_fold
+ && (*ptr != NUL
+ || lcs_eol_one > 0
+ || (n_extra && (c_extra != NUL || *p_extra != NUL)))) {
+ c = wp->w_p_lcs_chars.ext;
+ char_attr = win_hl_attr(wp, HLF_AT);
+ mb_c = c;
+ mb_utf8 = check_mb_utf8(&c, u8cc);
+ }
+
+ // advance to the next 'colorcolumn'
+ if (draw_color_col) {
+ draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
+ }
+
+ // Highlight the cursor column if 'cursorcolumn' is set. But don't
+ // highlight the cursor position itself.
+ // Also highlight the 'colorcolumn' if it is different than
+ // 'cursorcolumn'
+ // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'
+ // options are set
+ vcol_save_attr = -1;
+ if ((draw_state == WL_LINE
+ || draw_state == WL_BRI
+ || draw_state == WL_SBR)
+ && !lnum_in_visual_area
+ && search_attr == 0
+ && area_attr == 0
+ && filler_todo <= 0) {
+ if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
+ && lnum != wp->w_cursor.lnum) {
+ vcol_save_attr = char_attr;
+ char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
+ } else if (draw_color_col && VCOL_HLC == *color_cols) {
+ vcol_save_attr = char_attr;
+ char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
+ }
+ }
+
+ // Apply lowest-priority line attr now, so everything can override it.
+ if (draw_state == WL_LINE) {
+ char_attr = hl_combine_attr(line_attr_lowprio, char_attr);
+ }
+
+ // Store character to be displayed.
+ // Skip characters that are left of the screen for 'nowrap'.
+ vcol_prev = vcol;
+ if (draw_state < WL_LINE || n_skip <= 0) {
+ //
+ // Store the character.
+ //
+ if (wp->w_p_rl && utf_char2cells(mb_c) > 1) {
+ // A double-wide character is: put first half in left cell.
+ off--;
+ col--;
+ }
+ if (mb_utf8) {
+ schar_from_cc(linebuf_char[off], mb_c, u8cc);
+ } else {
+ schar_from_ascii(linebuf_char[off], (char)c);
+ }
+ if (multi_attr) {
+ linebuf_attr[off] = multi_attr;
+ multi_attr = 0;
+ } else {
+ linebuf_attr[off] = char_attr;
+ }
+
+ if (utf_char2cells(mb_c) > 1) {
+ // Need to fill two screen columns.
+ off++;
+ col++;
+ // UTF-8: Put a 0 in the second screen char.
+ linebuf_char[off][0] = 0;
+ if (draw_state > WL_NR && filler_todo <= 0) {
+ vcol++;
+ }
+ // When "tocol" is halfway through a character, set it to the end of
+ // the character, otherwise highlighting won't stop.
+ if (tocol == vcol) {
+ tocol++;
+ }
+ if (wp->w_p_rl) {
+ // now it's time to backup one cell
+ off--;
+ col--;
+ }
+ }
+ if (wp->w_p_rl) {
+ off--;
+ col--;
+ } else {
+ off++;
+ col++;
+ }
+ } else if (wp->w_p_cole > 0 && is_concealing) {
+ n_skip--;
+ vcol_off++;
+ if (n_extra > 0) {
+ vcol_off += n_extra;
+ }
+ if (wp->w_p_wrap) {
+ // Special voodoo required if 'wrap' is on.
+ //
+ // Advance the column indicator to force the line
+ // drawing to wrap early. This will make the line
+ // take up the same screen space when parts are concealed,
+ // so that cursor line computations aren't messed up.
+ //
+ // To avoid the fictitious advance of 'col' causing
+ // trailing junk to be written out of the screen line
+ // we are building, 'boguscols' keeps track of the number
+ // of bad columns we have advanced.
+ if (n_extra > 0) {
+ vcol += n_extra;
+ if (wp->w_p_rl) {
+ col -= n_extra;
+ boguscols -= n_extra;
+ } else {
+ col += n_extra;
+ boguscols += n_extra;
+ }
+ n_extra = 0;
+ n_attr = 0;
+ }
+
+ if (utf_char2cells(mb_c) > 1) {
+ // Need to fill two screen columns.
+ if (wp->w_p_rl) {
+ boguscols--;
+ col--;
+ } else {
+ boguscols++;
+ col++;
+ }
+ }
+
+ if (wp->w_p_rl) {
+ boguscols--;
+ col--;
+ } else {
+ boguscols++;
+ col++;
+ }
+ } else {
+ if (n_extra > 0) {
+ vcol += n_extra;
+ n_extra = 0;
+ n_attr = 0;
+ }
+ }
+ } else {
+ n_skip--;
+ }
+
+ // Only advance the "vcol" when after the 'number' or 'relativenumber'
+ // column.
+ if (draw_state > WL_NR
+ && filler_todo <= 0) {
+ vcol++;
+ }
+
+ if (vcol_save_attr >= 0) {
+ char_attr = vcol_save_attr;
+ }
+
+ // restore attributes after "predeces" in 'listchars'
+ if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) {
+ char_attr = saved_attr3;
+ }
+
+ // restore attributes after last 'listchars' or 'number' char
+ if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) {
+ char_attr = saved_attr2;
+ }
+
+ // At end of screen line and there is more to come: Display the line
+ // so far. If there is no more to display it is caught above.
+ if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols))
+ && foldinfo.fi_lines == 0
+ && (draw_state != WL_LINE
+ || *ptr != NUL
+ || filler_todo > 0
+ || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
+ && p_extra != at_end_str)
+ || (n_extra != 0
+ && (c_extra != NUL || *p_extra != NUL)))) {
+ bool wrap = wp->w_p_wrap // Wrapping enabled.
+ && filler_todo <= 0 // Not drawing diff filler lines.
+ && lcs_eol_one != -1 // Haven't printed the lcs_eol character.
+ && row != endrow - 1 // Not the last line being displayed.
+ && (grid->cols == Columns // Window spans the width of the screen,
+ || ui_has(kUIMultigrid)) // or has dedicated grid.
+ && !wp->w_p_rl; // Not right-to-left.
+
+ int draw_col = col - boguscols;
+ if (filler_todo > 0) {
+ int index = filler_todo - (filler_lines - n_virt_lines);
+ if (index > 0) {
+ int i = (int)kv_size(virt_lines) - index;
+ assert(i >= 0);
+ int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset;
+ draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line,
+ kHlModeReplace, grid->cols, offset);
+ }
+ } else {
+ draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
+ }
+
+ grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap);
+ if (wrap) {
+ ScreenGrid *current_grid = grid;
+ int current_row = row, dummy_col = 0; // dummy_col unused
+ grid_adjust(&current_grid, &current_row, &dummy_col);
+
+ // Force a redraw of the first column of the next line.
+ current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
+
+ // Remember that the line wraps, used for modeless copy.
+ current_grid->line_wraps[current_row] = true;
+ }
+
+ boguscols = 0;
+ row++;
+
+ // When not wrapping and finished diff lines, or when displayed
+ // '$' and highlighting until last column, break here.
+ if ((!wp->w_p_wrap && filler_todo <= 0) || lcs_eol_one == -1) {
+ break;
+ }
+
+ // When the window is too narrow draw all "@" lines.
+ if (draw_state != WL_LINE && filler_todo <= 0) {
+ win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT);
+ row = endrow;
+ }
+
+ // When line got too long for screen break here.
+ if (row == endrow) {
+ row++;
+ break;
+ }
+
+ col = 0;
+ off = 0;
+ if (wp->w_p_rl) {
+ col = grid->cols - 1; // col is not used if breaking!
+ off += col;
+ }
+
+ // reset the drawing state for the start of a wrapped line
+ draw_state = WL_START;
+ saved_n_extra = n_extra;
+ saved_p_extra = p_extra;
+ saved_c_extra = c_extra;
+ saved_c_final = c_final;
+ saved_char_attr = char_attr;
+ n_extra = 0;
+ lcs_prec_todo = wp->w_p_lcs_chars.prec;
+ if (filler_todo <= 0) {
+ need_showbreak = true;
+ }
+ filler_todo--;
+ // When the filler lines are actually below the last line of the
+ // file, don't draw the line itself, break here.
+ if (filler_todo == 0 && (wp->w_botfill || end_fill)) {
+ break;
+ }
+ }
+ } // for every character in the line
+
+ // After an empty line check first word for capital.
+ if (*skipwhite((char *)line) == NUL) {
+ capcol_lnum = lnum + 1;
+ cap_col = 0;
+ }
+
+ kv_destroy(virt_lines);
+ xfree(p_extra_free);
+ return row;
+}
diff --git a/src/nvim/drawline.h b/src/nvim/drawline.h
new file mode 100644
index 0000000000..e50969983e
--- /dev/null
+++ b/src/nvim/drawline.h
@@ -0,0 +1,24 @@
+#ifndef NVIM_DRAWLINE_H
+#define NVIM_DRAWLINE_H
+
+#include "nvim/decoration_provider.h"
+#include "nvim/fold.h"
+#include "nvim/screen.h"
+
+// Maximum columns for terminal highlight attributes
+#define TERM_ATTRS_MAX 1024
+
+typedef struct {
+ NS ns_id;
+ uint64_t mark_id;
+ int win_row;
+ int win_col;
+} WinExtmark;
+EXTERN kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE);
+
+EXTERN bool conceal_cursor_used INIT(= false);
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawline.h.generated.h"
+#endif
+#endif // NVIM_DRAWLINE_H
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
new file mode 100644
index 0000000000..29ff261461
--- /dev/null
+++ b/src/nvim/drawscreen.c
@@ -0,0 +1,2176 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// drawscreen.c: Code for updating all the windows on the screen.
+// This is the top level, drawline.c is the middle and grid.c/screen.c the lower level.
+
+// update_screen() is the function that updates all windows and status lines.
+// It is called from the main loop when must_redraw is non-zero. It may be
+// called from other places when an immediate screen update is needed.
+//
+// The part of the buffer that is displayed in a window is set with:
+// - w_topline (first buffer line in window)
+// - w_topfill (filler lines above the first line)
+// - w_leftcol (leftmost window cell in window),
+// - w_skipcol (skipped window cells of first line)
+//
+// Commands that only move the cursor around in a window, do not need to take
+// action to update the display. The main loop will check if w_topline is
+// valid and update it (scroll the window) when needed.
+//
+// Commands that scroll a window change w_topline and must call
+// check_cursor() to move the cursor into the visible part of the window, and
+// call redraw_later(wp, UPD_VALID) to have the window displayed by update_screen()
+// later.
+//
+// Commands that change text in the buffer must call changed_bytes() or
+// changed_lines() to mark the area that changed and will require updating
+// later. The main loop will call update_screen(), which will update each
+// window that shows the changed buffer. This assumes text above the change
+// can remain displayed as it is. Text after the change may need updating for
+// scrolling, folding and syntax highlighting.
+//
+// Commands that change how a window is displayed (e.g., setting 'list') or
+// invalidate the contents of a window in another way (e.g., change fold
+// settings), must call redraw_later(wp, UPD_NOT_VALID) to have the whole window
+// redisplayed by update_screen() later.
+//
+// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
+// must call redraw_curbuf_later(UPD_NOT_VALID) to have all the windows for the
+// buffer redisplayed by update_screen() later.
+//
+// Commands that change highlighting and possibly cause a scroll too must call
+// redraw_later(wp, UPD_SOME_VALID) to update the whole window but still use
+// scrolling to avoid redrawing everything. But the length of displayed lines
+// must not change, use UPD_NOT_VALID then.
+//
+// Commands that move the window position must call redraw_later(wp, UPD_NOT_VALID).
+// TODO(neovim): should minimize redrawing by scrolling when possible.
+//
+// Commands that change everything (e.g., resizing the screen) must call
+// redraw_all_later(UPD_NOT_VALID) or redraw_all_later(UPD_CLEAR).
+//
+// Things that are handled indirectly:
+// - When messages scroll the screen up, msg_scrolled will be set and
+// update_screen() called to redraw.
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
+#include "nvim/ex_getln.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
+#include "nvim/insexpand.h"
+#include "nvim/match.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/plines.h"
+#include "nvim/popupmenu.h"
+#include "nvim/profile.h"
+#include "nvim/regexp.h"
+#include "nvim/statusline.h"
+#include "nvim/syntax.h"
+#include "nvim/ui_compositor.h"
+#include "nvim/undo.h"
+#include "nvim/version.h"
+#include "nvim/window.h"
+
+/// corner value flags for hsep_connected and vsep_connected
+typedef enum {
+ WC_TOP_LEFT = 0,
+ WC_TOP_RIGHT,
+ WC_BOTTOM_LEFT,
+ WC_BOTTOM_RIGHT,
+} WindowCorner;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawscreen.c.generated.h"
+#endif
+
+static bool redraw_popupmenu = false;
+static bool msg_grid_invalid = false;
+static bool resizing = false;
+
+static char *provider_err = NULL;
+
+/// Check if the cursor line needs to be redrawn because of 'concealcursor'.
+///
+/// When cursor is moved at the same time, both lines will be redrawn regardless.
+void conceal_check_cursor_line(void)
+{
+ bool should_conceal = conceal_cursor_line(curwin);
+ if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
+ redrawWinline(curwin, curwin->w_cursor.lnum);
+ // Need to recompute cursor column, e.g., when starting Visual mode
+ // without concealing.
+ curs_columns(curwin, true);
+ }
+}
+
+/// Resize the screen to Rows and Columns.
+///
+/// Allocate default_grid.chars[] and other grid arrays.
+///
+/// There may be some time between setting Rows and Columns and (re)allocating
+/// default_grid arrays. This happens when starting up and when
+/// (manually) changing the screen size. Always use default_grid.rows and
+/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
+/// and Columns for positioning text etc. where the final size of the screen is
+/// needed.
+void screenalloc(void)
+{
+ // It's possible that we produce an out-of-memory message below, which
+ // will cause this function to be called again. To break the loop, just
+ // return here.
+ if (resizing) {
+ return;
+ }
+ resizing = true;
+
+ int retry_count = 0;
+
+retry:
+ // Allocation of the screen buffers is done only when the size changes and
+ // when Rows and Columns have been set and we have started doing full
+ // screen stuff.
+ if ((default_grid.chars != NULL
+ && Rows == default_grid.rows
+ && Columns == default_grid.cols)
+ || Rows == 0
+ || Columns == 0
+ || (!full_screen && default_grid.chars == NULL)) {
+ resizing = false;
+ return;
+ }
+
+ // Note that the window sizes are updated before reallocating the arrays,
+ // thus we must not redraw here!
+ RedrawingDisabled++;
+
+ // win_new_screensize will recompute floats position, but tell the
+ // compositor to not redraw them yet
+ ui_comp_set_screen_valid(false);
+ if (msg_grid.chars) {
+ msg_grid_invalid = true;
+ }
+
+ win_new_screensize(); // fit the windows in the new sized screen
+
+ comp_col(); // recompute columns for shown command and ruler
+
+ // We're changing the size of the screen.
+ // - Allocate new arrays for default_grid
+ // - Move lines from the old arrays into the new arrays, clear extra
+ // lines (unless the screen is going to be cleared).
+ // - Free the old arrays.
+ //
+ // If anything fails, make grid arrays NULL, so we don't do anything!
+ // Continuing with the old arrays may result in a crash, because the
+ // size is wrong.
+
+ grid_alloc(&default_grid, Rows, Columns, true, true);
+ StlClickDefinition *new_tab_page_click_defs =
+ xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
+
+ stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
+ xfree(tab_page_click_defs);
+
+ tab_page_click_defs = new_tab_page_click_defs;
+ tab_page_click_defs_size = Columns;
+
+ default_grid.comp_height = Rows;
+ default_grid.comp_width = Columns;
+
+ default_grid.row_offset = 0;
+ default_grid.col_offset = 0;
+ default_grid.handle = DEFAULT_GRID_HANDLE;
+
+ must_redraw = UPD_CLEAR; // need to clear the screen later
+
+ RedrawingDisabled--;
+
+ // Do not apply autocommands more than 3 times to avoid an endless loop
+ // in case applying autocommands always changes Rows or Columns.
+ if (starting == 0 && ++retry_count <= 3) {
+ apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
+ // In rare cases, autocommands may have altered Rows or Columns,
+ // jump back to check if we need to allocate the screen again.
+ goto retry;
+ }
+
+ resizing = false;
+}
+
+void screenclear(void)
+{
+ check_for_delay(false);
+ screenalloc(); // allocate screen buffers if size changed
+
+ int i;
+
+ if (starting == NO_SCREEN || default_grid.chars == NULL) {
+ return;
+ }
+
+ // blank out the default grid
+ for (i = 0; i < default_grid.rows; i++) {
+ grid_clear_line(&default_grid, default_grid.line_offset[i],
+ default_grid.cols, true);
+ default_grid.line_wraps[i] = false;
+ }
+
+ ui_call_grid_clear(1); // clear the display
+ ui_comp_set_screen_valid(true);
+
+ ns_hl_fast = -1;
+
+ clear_cmdline = false;
+ mode_displayed = false;
+
+ redraw_all_later(UPD_NOT_VALID);
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ redraw_popupmenu = true;
+ pum_invalidate();
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ wp->w_redr_type = UPD_CLEAR;
+ }
+ }
+ if (must_redraw == UPD_CLEAR) {
+ must_redraw = UPD_NOT_VALID; // no need to clear again
+ }
+ compute_cmdrow();
+ msg_row = cmdline_row; // put cursor on last line for messages
+ msg_col = 0;
+ msg_scrolled = 0; // can't scroll back
+ msg_didany = false;
+ msg_didout = false;
+ if (HL_ATTR(HLF_MSG) > 0 && msg_use_grid() && msg_grid.chars) {
+ grid_invalidate(&msg_grid);
+ msg_grid_validate();
+ msg_grid_invalid = false;
+ clear_cmdline = true;
+ }
+}
+
+/// Set dimensions of the Nvim application "screen".
+void screen_resize(int width, int height)
+{
+ // Avoid recursiveness, can happen when setting the window size causes
+ // another window-changed signal.
+ if (updating_screen || resizing_screen) {
+ return;
+ }
+
+ if (width < 0 || height < 0) { // just checking...
+ return;
+ }
+
+ if (State == MODE_HITRETURN || State == MODE_SETWSIZE) {
+ // postpone the resizing
+ State = MODE_SETWSIZE;
+ return;
+ }
+
+ // curwin->w_buffer can be NULL when we are closing a window and the
+ // buffer has already been closed and removing a scrollbar causes a resize
+ // event. Don't resize then, it will happen after entering another buffer.
+ if (curwin->w_buffer == NULL) {
+ return;
+ }
+
+ resizing_screen = true;
+
+ Rows = height;
+ Columns = width;
+ check_screensize();
+ int max_p_ch = Rows - min_rows() + 1;
+ if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) {
+ p_ch = max_p_ch ? max_p_ch : 1;
+ }
+ height = Rows;
+ width = Columns;
+ p_lines = Rows;
+ p_columns = Columns;
+ ui_call_grid_resize(1, width, height);
+
+ /// The window layout used to be adjusted here, but it now happens in
+ /// screenalloc() (also invoked from screenclear()). That is because the
+ /// recursize "resizing_screen" check above may skip this, but not screenalloc().
+
+ if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
+ screenclear();
+ }
+
+ if (starting != NO_SCREEN) {
+ maketitle();
+
+ changed_line_abv_curs();
+ invalidate_botline();
+
+ // We only redraw when it's needed:
+ // - While at the more prompt or executing an external command, don't
+ // redraw, but position the cursor.
+ // - While editing the command line, only redraw that.
+ // - in Ex mode, don't redraw anything.
+ // - Otherwise, redraw right now, and position the cursor.
+ // Always need to call update_screen() or screenalloc(), to make
+ // sure Rows/Columns and the size of the screen is correct!
+ if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
+ || exmode_active) {
+ screenalloc();
+ if (msg_grid.chars) {
+ msg_grid_validate();
+ }
+ // TODO(bfredl): sometimes messes up the output. Implement clear+redraw
+ // also for the pager? (or: what if the pager was just a modal window?)
+ ui_comp_set_screen_valid(true);
+ repeat_message();
+ } else {
+ if (curwin->w_p_scb) {
+ do_check_scrollbind(true);
+ }
+ if (State & MODE_CMDLINE) {
+ redraw_popupmenu = false;
+ update_screen(UPD_NOT_VALID);
+ redrawcmdline();
+ if (pum_drawn()) {
+ cmdline_pum_display(false);
+ }
+ } else {
+ update_topline(curwin);
+ if (pum_drawn()) {
+ // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
+ // For now make sure the nested update_screen(0) won't redraw the
+ // pum at the old position. Try to untangle this later.
+ redraw_popupmenu = false;
+ ins_compl_show_pum();
+ }
+ update_screen(UPD_NOT_VALID);
+ if (redrawing()) {
+ setcursor();
+ }
+ }
+ }
+ ui_flush();
+ }
+ resizing_screen = false;
+}
+
+/// Redraw the parts of the screen that is marked for redraw.
+///
+/// Most code shouldn't call this directly, rather use redraw_later() and
+/// and redraw_all_later() to mark parts of the screen as needing a redraw.
+///
+/// @param type set to a UPD_NOT_VALID to force redraw of entire screen
+int update_screen(int type)
+{
+ static bool did_intro = false;
+ bool is_stl_global = global_stl_height() > 0;
+
+ // Don't do anything if the screen structures are (not yet) valid.
+ // A VimResized autocmd can invoke redrawing in the middle of a resize,
+ // which would bypass the checks in screen_resize for popupmenu etc.
+ if (!default_grid.chars || resizing) {
+ return FAIL;
+ }
+
+ // May have postponed updating diffs.
+ if (need_diff_redraw) {
+ diff_redraw(true);
+ }
+
+ if (must_redraw) {
+ if (type < must_redraw) { // use maximal type
+ type = must_redraw;
+ }
+
+ // must_redraw is reset here, so that when we run into some weird
+ // reason to redraw while busy redrawing (e.g., asynchronous
+ // scrolling), or update_topline() in win_update() will cause a
+ // scroll, or a decoration provider requires a redraw, the screen
+ // will be redrawn later or in win_update().
+ must_redraw = 0;
+ }
+
+ // Need to update w_lines[].
+ if (curwin->w_lines_valid == 0 && type < UPD_NOT_VALID) {
+ type = UPD_NOT_VALID;
+ }
+
+ // Postpone the redrawing when it's not needed and when being called
+ // recursively.
+ if (!redrawing() || updating_screen) {
+ must_redraw = type;
+ if (type > UPD_INVERTED_ALL) {
+ curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
+ }
+ return FAIL;
+ }
+ updating_screen = 1;
+
+ display_tick++; // let syntax code know we're in a next round of
+ // display updating
+
+ // Tricky: vim code can reset msg_scrolled behind our back, so need
+ // separate bookkeeping for now.
+ if (msg_did_scroll) {
+ msg_did_scroll = false;
+ msg_scrolled_at_flush = 0;
+ }
+
+ if (type >= UPD_CLEAR || !default_grid.valid) {
+ ui_comp_set_screen_valid(false);
+ }
+
+ // if the screen was scrolled up when displaying a message, scroll it down
+ if (msg_scrolled || msg_grid_invalid) {
+ clear_cmdline = true;
+ int valid = MAX(Rows - msg_scrollsize(), 0);
+ if (msg_grid.chars) {
+ // non-displayed part of msg_grid is considered invalid.
+ for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
+ grid_clear_line(&msg_grid, msg_grid.line_offset[i],
+ msg_grid.cols, false);
+ }
+ }
+ if (msg_use_msgsep()) {
+ msg_grid.throttled = false;
+ // UPD_CLEAR is already handled
+ if (type == UPD_NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
+ ui_comp_set_screen_valid(false);
+ for (int i = valid; i < Rows - p_ch; i++) {
+ grid_clear_line(&default_grid, default_grid.line_offset[i],
+ Columns, false);
+ }
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ continue;
+ }
+ if (W_ENDROW(wp) > valid) {
+ wp->w_redr_type = MAX(wp->w_redr_type, UPD_NOT_VALID);
+ }
+ if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
+ wp->w_redr_status = true;
+ }
+ }
+ if (is_stl_global && Rows - p_ch - 1 > valid) {
+ curwin->w_redr_status = true;
+ }
+ }
+ msg_grid_set_pos(Rows - (int)p_ch, false);
+ msg_grid_invalid = false;
+ } else if (type != UPD_CLEAR) {
+ if (msg_scrolled > Rows - 5) { // redrawing is faster
+ type = UPD_NOT_VALID;
+ } else {
+ check_for_delay(false);
+ grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_floating) {
+ continue;
+ }
+ if (wp->w_winrow < msg_scrolled) {
+ if (W_ENDROW(wp) > msg_scrolled
+ && wp->w_redr_type < UPD_REDRAW_TOP
+ && wp->w_lines_valid > 0
+ && wp->w_topline == wp->w_lines[0].wl_lnum) {
+ wp->w_upd_rows = msg_scrolled - wp->w_winrow;
+ wp->w_redr_type = UPD_REDRAW_TOP;
+ } else {
+ wp->w_redr_type = UPD_NOT_VALID;
+ if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
+ wp->w_redr_status = true;
+ }
+ }
+ }
+ }
+ if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
+ curwin->w_redr_status = true;
+ }
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ }
+ }
+ msg_scrolled = 0;
+ msg_scrolled_at_flush = 0;
+ need_wait_return = false;
+ }
+
+ win_ui_flush();
+ msg_ext_check_clear();
+
+ // reset cmdline_row now (may have been changed temporarily)
+ compute_cmdrow();
+
+ bool hl_changed = false;
+ // Check for changed highlighting
+ if (need_highlight_changed) {
+ highlight_changed();
+ hl_changed = true;
+ }
+
+ if (type == UPD_CLEAR) { // first clear screen
+ screenclear(); // will reset clear_cmdline
+ cmdline_screen_cleared(); // clear external cmdline state
+ type = UPD_NOT_VALID;
+ // must_redraw may be set indirectly, avoid another redraw later
+ must_redraw = 0;
+ } else if (!default_grid.valid) {
+ grid_invalidate(&default_grid);
+ default_grid.valid = true;
+ }
+
+ // After disabling msgsep the grid might not have been deallocated yet,
+ // hence we also need to check msg_grid.chars
+ if (type == UPD_NOT_VALID && (msg_use_grid() || msg_grid.chars)) {
+ grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
+ }
+
+ ui_comp_set_screen_valid(true);
+
+ DecorProviders providers;
+ decor_providers_start(&providers, type, &provider_err);
+
+ // "start" callback could have changed highlights for global elements
+ if (win_check_ns_hl(NULL)) {
+ redraw_cmdline = true;
+ redraw_tabline = true;
+ }
+
+ if (clear_cmdline) { // going to clear cmdline (done below)
+ check_for_delay(false);
+ }
+
+ // Force redraw when width of 'number' or 'relativenumber' column
+ // changes.
+ if (curwin->w_redr_type < UPD_NOT_VALID
+ && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
+ ? number_width(curwin) : 0)) {
+ curwin->w_redr_type = UPD_NOT_VALID;
+ }
+
+ // Only start redrawing if there is really something to do.
+ if (type == UPD_INVERTED) {
+ update_curswant();
+ }
+ if (curwin->w_redr_type < type
+ && !((type == UPD_VALID
+ && curwin->w_lines[0].wl_valid
+ && curwin->w_topfill == curwin->w_old_topfill
+ && curwin->w_botfill == curwin->w_old_botfill
+ && curwin->w_topline == curwin->w_lines[0].wl_lnum)
+ || (type == UPD_INVERTED
+ && VIsual_active
+ && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
+ && curwin->w_old_visual_mode == VIsual_mode
+ && (curwin->w_valid & VALID_VIRTCOL)
+ && curwin->w_old_curswant == curwin->w_curswant))) {
+ curwin->w_redr_type = type;
+ }
+
+ // Redraw the tab pages line if needed.
+ if (redraw_tabline || type >= UPD_NOT_VALID) {
+ update_window_hl(curwin, type >= UPD_NOT_VALID);
+ FOR_ALL_TABS(tp) {
+ if (tp != curtab) {
+ update_window_hl(tp->tp_curwin, type >= UPD_NOT_VALID);
+ }
+ }
+ draw_tabline();
+ }
+
+ // Correct stored syntax highlighting info for changes in each displayed
+ // buffer. Each buffer must only be done once.
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ update_window_hl(wp, type >= UPD_NOT_VALID || hl_changed);
+
+ buf_T *buf = wp->w_buffer;
+ if (buf->b_mod_set) {
+ if (buf->b_mod_tick_syn < display_tick
+ && syntax_present(wp)) {
+ syn_stack_apply_changes(buf);
+ buf->b_mod_tick_syn = display_tick;
+ }
+
+ if (buf->b_mod_tick_decor < display_tick) {
+ decor_providers_invoke_buf(buf, &providers, &provider_err);
+ buf->b_mod_tick_decor = display_tick;
+ }
+ }
+ }
+
+ // Go from top to bottom through the windows, redrawing the ones that need it.
+ bool did_one = false;
+ screen_search_hl.rm.regprog = NULL;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_redr_type == UPD_CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
+ grid_invalidate(&wp->w_grid_alloc);
+ wp->w_redr_type = UPD_NOT_VALID;
+ }
+
+ win_check_ns_hl(wp);
+
+ // reallocate grid if needed.
+ win_grid_alloc(wp);
+
+ if (wp->w_redr_border || wp->w_redr_type >= UPD_NOT_VALID) {
+ win_redr_border(wp);
+ }
+
+ if (wp->w_redr_type != 0) {
+ if (!did_one) {
+ did_one = true;
+ start_search_hl();
+ }
+ win_update(wp, &providers);
+ }
+
+ // redraw status line and window bar after the window to minimize cursor movement
+ if (wp->w_redr_status) {
+ win_redr_winbar(wp);
+ win_redr_status(wp);
+ }
+ }
+
+ end_search_hl();
+
+ // May need to redraw the popup menu.
+ if (pum_drawn() && must_redraw_pum) {
+ win_check_ns_hl(curwin);
+ pum_redraw();
+ }
+
+ win_check_ns_hl(NULL);
+
+ // Reset b_mod_set flags. Going through all windows is probably faster
+ // than going through all buffers (there could be many buffers).
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ wp->w_buffer->b_mod_set = false;
+ }
+
+ updating_screen = 0;
+
+ // Clear or redraw the command line. Done last, because scrolling may
+ // mess up the command line.
+ if (clear_cmdline || redraw_cmdline || redraw_mode) {
+ showmode();
+ }
+
+ // May put up an introductory message when not editing a file
+ if (!did_intro) {
+ maybe_intro_message();
+ }
+ did_intro = true;
+
+ decor_providers_invoke_end(&providers, &provider_err);
+ kvi_destroy(providers);
+
+ // either cmdline is cleared, not drawn or mode is last drawn
+ cmdline_was_last_drawn = false;
+ return OK;
+}
+
+static void win_redr_border(win_T *wp)
+{
+ wp->w_redr_border = false;
+ if (!(wp->w_floating && wp->w_float_config.border)) {
+ return;
+ }
+
+ ScreenGrid *grid = &wp->w_grid_alloc;
+
+ schar_T *chars = wp->w_float_config.border_chars;
+ int *attrs = wp->w_float_config.border_attr;
+
+ int *adj = wp->w_border_adj;
+ int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
+
+ if (adj[0]) {
+ grid_puts_line_start(grid, 0);
+ if (adj[3]) {
+ grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
+ }
+ for (int i = 0; i < icol; i++) {
+ grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
+ }
+ if (adj[1]) {
+ grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
+ }
+ grid_puts_line_flush(false);
+ }
+
+ for (int i = 0; i < irow; i++) {
+ if (adj[3]) {
+ grid_puts_line_start(grid, i + adj[0]);
+ grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
+ grid_puts_line_flush(false);
+ }
+ if (adj[1]) {
+ int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
+ grid_puts_line_start(grid, i + adj[0]);
+ grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
+ grid_puts_line_flush(false);
+ }
+ }
+
+ if (adj[2]) {
+ grid_puts_line_start(grid, irow + adj[0]);
+ if (adj[3]) {
+ grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
+ }
+ for (int i = 0; i < icol; i++) {
+ int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
+ grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
+ }
+ if (adj[1]) {
+ grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
+ }
+ grid_puts_line_flush(false);
+ }
+}
+
+/// Show current cursor info in ruler and various other places
+///
+/// @param always if false, only show ruler if position has changed.
+void show_cursor_info(bool always)
+{
+ if (!always && !redrawing()) {
+ return;
+ }
+
+ win_check_ns_hl(curwin);
+ if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
+ && (curwin->w_status_height || global_stl_height())) {
+ redraw_custom_statusline(curwin);
+ } else {
+ win_redr_ruler(curwin, always);
+ }
+ if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
+ win_redr_winbar(curwin);
+ }
+
+ if (need_maketitle
+ || (p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))) {
+ maketitle();
+ }
+
+ win_check_ns_hl(NULL);
+ // Redraw the tab pages line if needed.
+ if (redraw_tabline) {
+ draw_tabline();
+ }
+}
+
+static void redraw_win_signcol(win_T *wp)
+{
+ // If we can compute a change in the automatic sizing of the sign column
+ // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
+ // figuring it out here so we can redraw the entire screen for it.
+ int scwidth = wp->w_scwidth;
+ wp->w_scwidth = win_signcol_count(wp);
+ if (wp->w_scwidth != scwidth) {
+ changed_line_abv_curs_win(wp);
+ }
+}
+
+/// Check if horizontal separator of window "wp" at specified window corner is connected to the
+/// horizontal separator of another window
+/// Assumes global statusline is enabled
+static bool hsep_connected(win_T *wp, WindowCorner corner)
+{
+ bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT);
+ int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT)
+ ? wp->w_winrow - 1 : W_ENDROW(wp);
+ frame_T *fr = wp->w_frame;
+
+ while (fr->fr_parent != NULL) {
+ if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) {
+ fr = before ? fr->fr_prev : fr->fr_next;
+ break;
+ }
+ fr = fr->fr_parent;
+ }
+ if (fr->fr_parent == NULL) {
+ return false;
+ }
+ while (fr->fr_layout != FR_LEAF) {
+ fr = fr->fr_child;
+ if (fr->fr_parent->fr_layout == FR_ROW && before) {
+ while (fr->fr_next != NULL) {
+ fr = fr->fr_next;
+ }
+ } else {
+ while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) {
+ fr = fr->fr_next;
+ }
+ }
+ }
+
+ return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win));
+}
+
+/// Check if vertical separator of window "wp" at specified window corner is connected to the
+/// vertical separator of another window
+static bool vsep_connected(win_T *wp, WindowCorner corner)
+{
+ bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT);
+ int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT)
+ ? wp->w_wincol - 1 : W_ENDCOL(wp);
+ frame_T *fr = wp->w_frame;
+
+ while (fr->fr_parent != NULL) {
+ if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) {
+ fr = before ? fr->fr_prev : fr->fr_next;
+ break;
+ }
+ fr = fr->fr_parent;
+ }
+ if (fr->fr_parent == NULL) {
+ return false;
+ }
+ while (fr->fr_layout != FR_LEAF) {
+ fr = fr->fr_child;
+ if (fr->fr_parent->fr_layout == FR_COL && before) {
+ while (fr->fr_next != NULL) {
+ fr = fr->fr_next;
+ }
+ } else {
+ while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) {
+ fr = fr->fr_next;
+ }
+ }
+ }
+
+ return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win));
+}
+
+/// Draw the vertical separator right of window "wp"
+static void draw_vsep_win(win_T *wp)
+{
+ int hl;
+ int c;
+
+ if (wp->w_vsep_width) {
+ // draw the vertical separator right of this window
+ c = fillchar_vsep(wp, &hl);
+ grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
+ W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
+ }
+}
+
+/// Draw the horizontal separator below window "wp"
+static void draw_hsep_win(win_T *wp)
+{
+ int hl;
+ int c;
+
+ if (wp->w_hsep_height) {
+ // draw the horizontal separator below this window
+ c = fillchar_hsep(wp, &hl);
+ grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
+ wp->w_wincol, W_ENDCOL(wp), c, c, hl);
+ }
+}
+
+/// Get the separator connector for specified window corner of window "wp"
+static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
+{
+ // It's impossible for windows to be connected neither vertically nor horizontally
+ // So if they're not vertically connected, assume they're horizontally connected
+ if (vsep_connected(wp, corner)) {
+ if (hsep_connected(wp, corner)) {
+ return wp->w_p_fcs_chars.verthoriz;
+ } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
+ return wp->w_p_fcs_chars.vertright;
+ } else {
+ return wp->w_p_fcs_chars.vertleft;
+ }
+ } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
+ return wp->w_p_fcs_chars.horizdown;
+ } else {
+ return wp->w_p_fcs_chars.horizup;
+ }
+}
+
+/// Draw separator connecting characters on the corners of window "wp"
+static void draw_sep_connectors_win(win_T *wp)
+{
+ // Don't draw separator connectors unless global statusline is enabled and the window has
+ // either a horizontal or vertical separator
+ if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) {
+ return;
+ }
+
+ int hl = win_hl_attr(wp, HLF_C);
+
+ // Determine which edges of the screen the window is located on so we can avoid drawing separators
+ // on corners contained in those edges
+ bool win_at_top;
+ bool win_at_bottom = wp->w_hsep_height == 0;
+ bool win_at_left;
+ bool win_at_right = wp->w_vsep_width == 0;
+ frame_T *frp;
+
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
+ break;
+ }
+ }
+ win_at_top = frp->fr_parent == NULL;
+ for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
+ if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
+ break;
+ }
+ }
+ win_at_left = frp->fr_parent == NULL;
+
+ // Draw the appropriate separator connector in every corner where drawing them is necessary
+ if (!(win_at_top || win_at_left)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
+ wp->w_winrow - 1, wp->w_wincol - 1, hl);
+ }
+ if (!(win_at_top || win_at_right)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
+ wp->w_winrow - 1, W_ENDCOL(wp), hl);
+ }
+ if (!(win_at_bottom || win_at_left)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
+ W_ENDROW(wp), wp->w_wincol - 1, hl);
+ }
+ if (!(win_at_bottom || win_at_right)) {
+ grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
+ W_ENDROW(wp), W_ENDCOL(wp), hl);
+ }
+}
+
+/// Update a single window.
+///
+/// This may cause the windows below it also to be redrawn (when clearing the
+/// screen or scrolling lines).
+///
+/// How the window is redrawn depends on wp->w_redr_type. Each type also
+/// implies the one below it.
+/// UPD_NOT_VALID redraw the whole window
+/// UPD_SOME_VALID redraw the whole window but do scroll when possible
+/// UPD_REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like UPD_VALID
+/// UPD_INVERTED redraw the changed part of the Visual area
+/// UPD_INVERTED_ALL redraw the whole Visual area
+/// UPD_VALID 1. scroll up/down to adjust for a changed w_topline
+/// 2. update lines at the top when scrolled down
+/// 3. redraw changed text:
+/// - if wp->w_buffer->b_mod_set set, update lines between
+/// b_mod_top and b_mod_bot.
+/// - if wp->w_redraw_top non-zero, redraw lines between
+/// wp->w_redraw_top and wp->w_redr_bot.
+/// - continue redrawing when syntax status is invalid.
+/// 4. if scrolled up, update lines at the bottom.
+/// This results in three areas that may need updating:
+/// top: from first row to top_end (when scrolled down)
+/// mid: from mid_start to mid_end (update inversion or changed text)
+/// bot: from bot_start to last row (when scrolled up)
+static void win_update(win_T *wp, DecorProviders *providers)
+{
+ bool called_decor_providers = false;
+win_update_start:
+ ;
+ buf_T *buf = wp->w_buffer;
+ int type;
+ int top_end = 0; // Below last row of the top area that needs
+ // updating. 0 when no top area updating.
+ int mid_start = 999; // first row of the mid area that needs
+ // updating. 999 when no mid area updating.
+ int mid_end = 0; // Below last row of the mid area that needs
+ // updating. 0 when no mid area updating.
+ int bot_start = 999; // first row of the bot area that needs
+ // updating. 999 when no bot area updating
+ bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
+ bool top_to_mod = false; // redraw above mod_top
+
+ int row; // current window row to display
+ linenr_T lnum; // current buffer lnum to display
+ int idx; // current index in w_lines[]
+ int srow; // starting row of the current line
+
+ bool eof = false; // if true, we hit the end of the file
+ bool didline = false; // if true, we finished the last line
+ int i;
+ long j;
+ static bool recursive = false; // being called recursively
+ const linenr_T old_botline = wp->w_botline;
+ // Remember what happened to the previous line.
+#define DID_NONE 1 // didn't update a line
+#define DID_LINE 2 // updated a normal line
+#define DID_FOLD 3 // updated a folded line
+ int did_update = DID_NONE;
+ linenr_T syntax_last_parsed = 0; // last parsed text line
+ linenr_T mod_top = 0;
+ linenr_T mod_bot = 0;
+ int save_got_int;
+
+ type = wp->w_redr_type;
+
+ if (type >= UPD_NOT_VALID) {
+ wp->w_redr_status = true;
+ wp->w_lines_valid = 0;
+ }
+
+ // Window is zero-height: Only need to draw the separator
+ if (wp->w_grid.rows == 0) {
+ // draw the horizontal separator below this window
+ draw_hsep_win(wp);
+ draw_sep_connectors_win(wp);
+ wp->w_redr_type = 0;
+ return;
+ }
+
+ // Window is zero-width: Only need to draw the separator.
+ if (wp->w_grid.cols == 0) {
+ // draw the vertical separator right of this window
+ draw_vsep_win(wp);
+ draw_sep_connectors_win(wp);
+ wp->w_redr_type = 0;
+ return;
+ }
+
+ redraw_win_signcol(wp);
+
+ init_search_hl(wp, &screen_search_hl);
+
+ // Force redraw when width of 'number' or 'relativenumber' column
+ // changes.
+ i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
+ if (wp->w_nrwidth != i) {
+ type = UPD_NOT_VALID;
+ wp->w_nrwidth = i;
+
+ if (buf->terminal) {
+ terminal_check_size(buf->terminal);
+ }
+ } else if (buf->b_mod_set
+ && buf->b_mod_xlines != 0
+ && wp->w_redraw_top != 0) {
+ // When there are both inserted/deleted lines and specific lines to be
+ // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
+ // everything (only happens when redrawing is off for while).
+ type = UPD_NOT_VALID;
+ } else {
+ // Set mod_top to the first line that needs displaying because of
+ // changes. Set mod_bot to the first line after the changes.
+ mod_top = wp->w_redraw_top;
+ if (wp->w_redraw_bot != 0) {
+ mod_bot = wp->w_redraw_bot + 1;
+ } else {
+ mod_bot = 0;
+ }
+ if (buf->b_mod_set) {
+ if (mod_top == 0 || mod_top > buf->b_mod_top) {
+ mod_top = buf->b_mod_top;
+ // Need to redraw lines above the change that may be included
+ // in a pattern match.
+ if (syntax_present(wp)) {
+ mod_top -= buf->b_s.b_syn_sync_linebreaks;
+ if (mod_top < 1) {
+ mod_top = 1;
+ }
+ }
+ }
+ if (mod_bot == 0 || mod_bot < buf->b_mod_bot) {
+ mod_bot = buf->b_mod_bot;
+ }
+
+ // When 'hlsearch' is on and using a multi-line search pattern, a
+ // change in one line may make the Search highlighting in a
+ // previous line invalid. Simple solution: redraw all visible
+ // lines above the change.
+ // Same for a match pattern.
+ if (screen_search_hl.rm.regprog != NULL
+ && re_multiline(screen_search_hl.rm.regprog)) {
+ top_to_mod = true;
+ } else {
+ const matchitem_T *cur = wp->w_match_head;
+ while (cur != NULL) {
+ if (cur->match.regprog != NULL
+ && re_multiline(cur->match.regprog)) {
+ top_to_mod = true;
+ break;
+ }
+ cur = cur->next;
+ }
+ }
+ }
+ if (mod_top != 0 && hasAnyFolding(wp)) {
+ linenr_T lnumt, lnumb;
+
+ // A change in a line can cause lines above it to become folded or
+ // unfolded. Find the top most buffer line that may be affected.
+ // If the line was previously folded and displayed, get the first
+ // line of that fold. If the line is folded now, get the first
+ // folded line. Use the minimum of these two.
+
+ // Find last valid w_lines[] entry above mod_top. Set lnumt to
+ // the line below it. If there is no valid entry, use w_topline.
+ // Find the first valid w_lines[] entry below mod_bot. Set lnumb
+ // to this line. If there is no valid entry, use MAXLNUM.
+ lnumt = wp->w_topline;
+ lnumb = MAXLNUM;
+ for (i = 0; i < wp->w_lines_valid; i++) {
+ if (wp->w_lines[i].wl_valid) {
+ if (wp->w_lines[i].wl_lastlnum < mod_top) {
+ lnumt = wp->w_lines[i].wl_lastlnum + 1;
+ }
+ if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) {
+ lnumb = wp->w_lines[i].wl_lnum;
+ // When there is a fold column it might need updating
+ // in the next line ("J" just above an open fold).
+ if (compute_foldcolumn(wp, 0) > 0) {
+ lnumb++;
+ }
+ }
+ }
+ }
+
+ (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL);
+ if (mod_top > lnumt) {
+ mod_top = lnumt;
+ }
+
+ // Now do the same for the bottom line (one above mod_bot).
+ mod_bot--;
+ (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL);
+ mod_bot++;
+ if (mod_bot < lnumb) {
+ mod_bot = lnumb;
+ }
+ }
+
+ // When a change starts above w_topline and the end is below
+ // w_topline, start redrawing at w_topline.
+ // If the end of the change is above w_topline: do like no change was
+ // made, but redraw the first line to find changes in syntax.
+ if (mod_top != 0 && mod_top < wp->w_topline) {
+ if (mod_bot > wp->w_topline) {
+ mod_top = wp->w_topline;
+ } else if (syntax_present(wp)) {
+ top_end = 1;
+ }
+ }
+
+ // When line numbers are displayed need to redraw all lines below
+ // inserted/deleted lines.
+ if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) {
+ mod_bot = MAXLNUM;
+ }
+ }
+ wp->w_redraw_top = 0; // reset for next time
+ wp->w_redraw_bot = 0;
+
+ // When only displaying the lines at the top, set top_end. Used when
+ // window has scrolled down for msg_scrolled.
+ if (type == UPD_REDRAW_TOP) {
+ j = 0;
+ for (i = 0; i < wp->w_lines_valid; i++) {
+ j += wp->w_lines[i].wl_size;
+ if (j >= wp->w_upd_rows) {
+ top_end = (int)j;
+ break;
+ }
+ }
+ if (top_end == 0) {
+ // not found (cannot happen?): redraw everything
+ type = UPD_NOT_VALID;
+ } else {
+ // top area defined, the rest is UPD_VALID
+ type = UPD_VALID;
+ }
+ }
+
+ // If there are no changes on the screen that require a complete redraw,
+ // handle three cases:
+ // 1: we are off the top of the screen by a few lines: scroll down
+ // 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
+ // 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
+ // w_lines[] that needs updating.
+ if ((type == UPD_VALID || type == UPD_SOME_VALID
+ || type == UPD_INVERTED || type == UPD_INVERTED_ALL)
+ && !wp->w_botfill && !wp->w_old_botfill) {
+ if (mod_top != 0
+ && wp->w_topline == mod_top
+ && (!wp->w_lines[0].wl_valid
+ || wp->w_topline == wp->w_lines[0].wl_lnum)) {
+ // w_topline is the first changed line and window is not scrolled,
+ // the scrolling from changed lines will be done further down.
+ } else if (wp->w_lines[0].wl_valid
+ && (wp->w_topline < wp->w_lines[0].wl_lnum
+ || (wp->w_topline == wp->w_lines[0].wl_lnum
+ && wp->w_topfill > wp->w_old_topfill))) {
+ // New topline is above old topline: May scroll down.
+ if (hasAnyFolding(wp)) {
+ linenr_T ln;
+
+ // count the number of lines we are off, counting a sequence
+ // of folded lines as one
+ j = 0;
+ for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
+ j++;
+ if (j >= wp->w_grid.rows - 2) {
+ break;
+ }
+ (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL);
+ }
+ } else {
+ j = wp->w_lines[0].wl_lnum - wp->w_topline;
+ }
+ if (j < wp->w_grid.rows - 2) { // not too far off
+ i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
+ // insert extra lines for previously invisible filler lines
+ if (wp->w_lines[0].wl_lnum != wp->w_topline) {
+ i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
+ }
+ if (i != 0 && i < wp->w_grid.rows - 2) { // less than a screen off
+ // Try to insert the correct number of lines.
+ // If not the last window, delete the lines at the bottom.
+ // win_ins_lines may fail when the terminal can't do it.
+ win_scroll_lines(wp, 0, i);
+ if (wp->w_lines_valid != 0) {
+ // Need to update rows that are new, stop at the
+ // first one that scrolled down.
+ top_end = i;
+ scrolled_down = true;
+
+ // Move the entries that were scrolled, disable
+ // the entries for the lines to be redrawn.
+ if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) {
+ wp->w_lines_valid = wp->w_grid.rows;
+ }
+ for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
+ wp->w_lines[idx] = wp->w_lines[idx - j];
+ }
+ while (idx >= 0) {
+ wp->w_lines[idx--].wl_valid = false;
+ }
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
+ } else {
+ mid_start = 0; // redraw all lines
+ }
+ } else {
+ // New topline is at or below old topline: May scroll up.
+ // When topline didn't change, find first entry in w_lines[] that
+ // needs updating.
+
+ // try to find wp->w_topline in wp->w_lines[].wl_lnum
+ j = -1;
+ row = 0;
+ for (i = 0; i < wp->w_lines_valid; i++) {
+ if (wp->w_lines[i].wl_valid
+ && wp->w_lines[i].wl_lnum == wp->w_topline) {
+ j = i;
+ break;
+ }
+ row += wp->w_lines[i].wl_size;
+ }
+ if (j == -1) {
+ // if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all
+ // lines
+ mid_start = 0;
+ } else {
+ // Try to delete the correct number of lines.
+ // wp->w_topline is at wp->w_lines[i].wl_lnum.
+
+ // If the topline didn't change, delete old filler lines,
+ // otherwise delete filler lines of the new topline...
+ if (wp->w_lines[0].wl_lnum == wp->w_topline) {
+ row += wp->w_old_topfill;
+ } else {
+ row += win_get_fill(wp, wp->w_topline);
+ }
+ // ... but don't delete new filler lines.
+ row -= wp->w_topfill;
+ if (row > 0) {
+ win_scroll_lines(wp, 0, -row);
+ bot_start = wp->w_grid.rows - row;
+ }
+ if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
+ // Skip the lines (below the deleted lines) that are still
+ // valid and don't need redrawing. Copy their info
+ // upwards, to compensate for the deleted lines. Set
+ // bot_start to the first row that needs redrawing.
+ bot_start = 0;
+ idx = 0;
+ for (;;) {
+ wp->w_lines[idx] = wp->w_lines[j];
+ // stop at line that didn't fit, unless it is still
+ // valid (no lines deleted)
+ if (row > 0 && bot_start + row
+ + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) {
+ wp->w_lines_valid = idx + 1;
+ break;
+ }
+ bot_start += wp->w_lines[idx++].wl_size;
+
+ // stop at the last valid entry in w_lines[].wl_size
+ if (++j >= wp->w_lines_valid) {
+ wp->w_lines_valid = idx;
+ break;
+ }
+ }
+
+ // Correct the first entry for filler lines at the top
+ // when it won't get updated below.
+ if (win_may_fill(wp) && bot_start > 0) {
+ wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true)
+ + wp->w_topfill);
+ }
+ }
+ }
+ }
+
+ // When starting redraw in the first line, redraw all lines.
+ if (mid_start == 0) {
+ mid_end = wp->w_grid.rows;
+ }
+ } else {
+ // Not UPD_VALID or UPD_INVERTED: redraw all lines.
+ mid_start = 0;
+ mid_end = wp->w_grid.rows;
+ }
+
+ if (type == UPD_SOME_VALID) {
+ // UPD_SOME_VALID: redraw all lines.
+ mid_start = 0;
+ mid_end = wp->w_grid.rows;
+ type = UPD_NOT_VALID;
+ }
+
+ // check if we are updating or removing the inverted part
+ if ((VIsual_active && buf == curwin->w_buffer)
+ || (wp->w_old_cursor_lnum != 0 && type != UPD_NOT_VALID)) {
+ linenr_T from, to;
+
+ if (VIsual_active) {
+ if (VIsual_mode != wp->w_old_visual_mode || type == UPD_INVERTED_ALL) {
+ // If the type of Visual selection changed, redraw the whole
+ // selection. Also when the ownership of the X selection is
+ // gained or lost.
+ if (curwin->w_cursor.lnum < VIsual.lnum) {
+ from = curwin->w_cursor.lnum;
+ to = VIsual.lnum;
+ } else {
+ from = VIsual.lnum;
+ to = curwin->w_cursor.lnum;
+ }
+ // redraw more when the cursor moved as well
+ if (wp->w_old_cursor_lnum < from) {
+ from = wp->w_old_cursor_lnum;
+ }
+ if (wp->w_old_cursor_lnum > to) {
+ to = wp->w_old_cursor_lnum;
+ }
+ if (wp->w_old_visual_lnum < from) {
+ from = wp->w_old_visual_lnum;
+ }
+ if (wp->w_old_visual_lnum > to) {
+ to = wp->w_old_visual_lnum;
+ }
+ } else {
+ // Find the line numbers that need to be updated: The lines
+ // between the old cursor position and the current cursor
+ // position. Also check if the Visual position changed.
+ if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum) {
+ from = curwin->w_cursor.lnum;
+ to = wp->w_old_cursor_lnum;
+ } else {
+ from = wp->w_old_cursor_lnum;
+ to = curwin->w_cursor.lnum;
+ if (from == 0) { // Visual mode just started
+ from = to;
+ }
+ }
+
+ if (VIsual.lnum != wp->w_old_visual_lnum
+ || VIsual.col != wp->w_old_visual_col) {
+ if (wp->w_old_visual_lnum < from
+ && wp->w_old_visual_lnum != 0) {
+ from = wp->w_old_visual_lnum;
+ }
+ if (wp->w_old_visual_lnum > to) {
+ to = wp->w_old_visual_lnum;
+ }
+ if (VIsual.lnum < from) {
+ from = VIsual.lnum;
+ }
+ if (VIsual.lnum > to) {
+ to = VIsual.lnum;
+ }
+ }
+ }
+
+ // If in block mode and changed column or curwin->w_curswant:
+ // update all lines.
+ // First compute the actual start and end column.
+ if (VIsual_mode == Ctrl_V) {
+ colnr_T fromc, toc;
+ unsigned int save_ve_flags = curwin->w_ve_flags;
+
+ if (curwin->w_p_lbr) {
+ curwin->w_ve_flags = VE_ALL;
+ }
+
+ getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
+ toc++;
+ curwin->w_ve_flags = save_ve_flags;
+ // Highlight to the end of the line, unless 'virtualedit' has
+ // "block".
+ if (curwin->w_curswant == MAXCOL) {
+ if (get_ve_flags() & VE_BLOCK) {
+ pos_T pos;
+ int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
+
+ // Need to find the longest line.
+ toc = 0;
+ pos.coladd = 0;
+ for (pos.lnum = curwin->w_cursor.lnum;
+ cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum;
+ pos.lnum += cursor_above ? 1 : -1) {
+ colnr_T t;
+
+ pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
+ getvvcol(wp, &pos, NULL, NULL, &t);
+ if (toc < t) {
+ toc = t;
+ }
+ }
+ toc++;
+ } else {
+ toc = MAXCOL;
+ }
+ }
+
+ if (fromc != wp->w_old_cursor_fcol
+ || toc != wp->w_old_cursor_lcol) {
+ if (from > VIsual.lnum) {
+ from = VIsual.lnum;
+ }
+ if (to < VIsual.lnum) {
+ to = VIsual.lnum;
+ }
+ }
+ wp->w_old_cursor_fcol = fromc;
+ wp->w_old_cursor_lcol = toc;
+ }
+ } else {
+ // Use the line numbers of the old Visual area.
+ if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) {
+ from = wp->w_old_cursor_lnum;
+ to = wp->w_old_visual_lnum;
+ } else {
+ from = wp->w_old_visual_lnum;
+ to = wp->w_old_cursor_lnum;
+ }
+ }
+
+ // There is no need to update lines above the top of the window.
+ if (from < wp->w_topline) {
+ from = wp->w_topline;
+ }
+
+ // If we know the value of w_botline, use it to restrict the update to
+ // the lines that are visible in the window.
+ if (wp->w_valid & VALID_BOTLINE) {
+ if (from >= wp->w_botline) {
+ from = wp->w_botline - 1;
+ }
+ if (to >= wp->w_botline) {
+ to = wp->w_botline - 1;
+ }
+ }
+
+ // Find the minimal part to be updated.
+ // Watch out for scrolling that made entries in w_lines[] invalid.
+ // E.g., CTRL-U makes the first half of w_lines[] invalid and sets
+ // top_end; need to redraw from top_end to the "to" line.
+ // A middle mouse click with a Visual selection may change the text
+ // above the Visual area and reset wl_valid, do count these for
+ // mid_end (in srow).
+ if (mid_start > 0) {
+ lnum = wp->w_topline;
+ idx = 0;
+ srow = 0;
+ if (scrolled_down) {
+ mid_start = top_end;
+ } else {
+ mid_start = 0;
+ }
+ while (lnum < from && idx < wp->w_lines_valid) { // find start
+ if (wp->w_lines[idx].wl_valid) {
+ mid_start += wp->w_lines[idx].wl_size;
+ } else if (!scrolled_down) {
+ srow += wp->w_lines[idx].wl_size;
+ }
+ idx++;
+ if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) {
+ lnum = wp->w_lines[idx].wl_lnum;
+ } else {
+ lnum++;
+ }
+ }
+ srow += mid_start;
+ mid_end = wp->w_grid.rows;
+ for (; idx < wp->w_lines_valid; idx++) { // find end
+ if (wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum >= to + 1) {
+ // Only update until first row of this line
+ mid_end = srow;
+ break;
+ }
+ srow += wp->w_lines[idx].wl_size;
+ }
+ }
+ }
+
+ if (VIsual_active && buf == curwin->w_buffer) {
+ wp->w_old_visual_mode = (char)VIsual_mode;
+ wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
+ wp->w_old_visual_lnum = VIsual.lnum;
+ wp->w_old_visual_col = VIsual.col;
+ wp->w_old_curswant = curwin->w_curswant;
+ } else {
+ wp->w_old_visual_mode = 0;
+ wp->w_old_cursor_lnum = 0;
+ wp->w_old_visual_lnum = 0;
+ wp->w_old_visual_col = 0;
+ }
+
+ // reset got_int, otherwise regexp won't work
+ save_got_int = got_int;
+ got_int = 0;
+ // Set the time limit to 'redrawtime'.
+ proftime_T syntax_tm = profile_setlimit(p_rdt);
+ syn_set_timeout(&syntax_tm);
+
+ // Update all the window rows.
+ idx = 0; // first entry in w_lines[].wl_size
+ row = 0;
+ srow = 0;
+ lnum = wp->w_topline; // first line shown in window
+
+ win_extmark_arr.size = 0;
+
+ decor_redraw_reset(buf, &decor_state);
+
+ DecorProviders line_providers;
+ decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
+ (void)win_signcol_count(wp); // check if provider changed signcol width
+ if (must_redraw != 0) {
+ must_redraw = 0;
+ if (!called_decor_providers) {
+ called_decor_providers = true;
+ goto win_update_start;
+ }
+ }
+
+ bool cursorline_standout = win_cursorline_standout(wp);
+
+ win_check_ns_hl(wp);
+
+ for (;;) {
+ // stop updating when reached the end of the window (check for _past_
+ // the end of the window is at the end of the loop)
+ if (row == wp->w_grid.rows) {
+ didline = true;
+ break;
+ }
+
+ // stop updating when hit the end of the file
+ if (lnum > buf->b_ml.ml_line_count) {
+ eof = true;
+ break;
+ }
+
+ // Remember the starting row of the line that is going to be dealt
+ // with. It is used further down when the line doesn't fit.
+ srow = row;
+
+ // Update a line when it is in an area that needs updating, when it
+ // has changes or w_lines[idx] is invalid.
+ // "bot_start" may be halfway a wrapped line after using
+ // win_scroll_lines(), check if the current line includes it.
+ // When syntax folding is being used, the saved syntax states will
+ // already have been updated, we can't see where the syntax state is
+ // the same again, just update until the end of the window.
+ if (row < top_end
+ || (row >= mid_start && row < mid_end)
+ || top_to_mod
+ || idx >= wp->w_lines_valid
+ || (row + wp->w_lines[idx].wl_size > bot_start)
+ || (mod_top != 0
+ && (lnum == mod_top
+ || (lnum >= mod_top
+ && (lnum < mod_bot
+ || did_update == DID_FOLD
+ || (did_update == DID_LINE
+ && syntax_present(wp)
+ && ((foldmethodIsSyntax(wp)
+ && hasAnyFolding(wp))
+ || syntax_check_changed(lnum)))
+ // match in fixed position might need redraw
+ // if lines were inserted or deleted
+ || (wp->w_match_head != NULL
+ && buf->b_mod_xlines != 0)))))
+ || (cursorline_standout && lnum == wp->w_cursor.lnum)
+ || lnum == wp->w_last_cursorline) {
+ if (lnum == mod_top) {
+ top_to_mod = false;
+ }
+
+ // When at start of changed lines: May scroll following lines
+ // up or down to minimize redrawing.
+ // Don't do this when the change continues until the end.
+ // Don't scroll when dollar_vcol >= 0, keep the "$".
+ // Don't scroll when redrawing the top, scrolled already above.
+ if (lnum == mod_top
+ && mod_bot != MAXLNUM
+ && !(dollar_vcol >= 0 && mod_bot == mod_top + 1)
+ && row >= top_end) {
+ int old_rows = 0;
+ int new_rows = 0;
+ int xtra_rows;
+ linenr_T l;
+
+ // Count the old number of window rows, using w_lines[], which
+ // should still contain the sizes for the lines as they are
+ // currently displayed.
+ for (i = idx; i < wp->w_lines_valid; i++) {
+ // Only valid lines have a meaningful wl_lnum. Invalid
+ // lines are part of the changed area.
+ if (wp->w_lines[i].wl_valid
+ && wp->w_lines[i].wl_lnum == mod_bot) {
+ break;
+ }
+ old_rows += wp->w_lines[i].wl_size;
+ if (wp->w_lines[i].wl_valid
+ && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) {
+ // Must have found the last valid entry above mod_bot.
+ // Add following invalid entries.
+ i++;
+ while (i < wp->w_lines_valid
+ && !wp->w_lines[i].wl_valid) {
+ old_rows += wp->w_lines[i++].wl_size;
+ }
+ break;
+ }
+ }
+
+ if (i >= wp->w_lines_valid) {
+ // We can't find a valid line below the changed lines,
+ // need to redraw until the end of the window.
+ // Inserting/deleting lines has no use.
+ bot_start = 0;
+ } else {
+ // Able to count old number of rows: Count new window
+ // rows, and may insert/delete lines
+ j = idx;
+ for (l = lnum; l < mod_bot; l++) {
+ if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
+ new_rows++;
+ } else if (l == wp->w_topline) {
+ new_rows += plines_win_nofill(wp, l, true) + wp->w_topfill;
+ } else {
+ new_rows += plines_win(wp, l, true);
+ }
+ j++;
+ if (new_rows > wp->w_grid.rows - row - 2) {
+ // it's getting too much, must redraw the rest
+ new_rows = 9999;
+ break;
+ }
+ }
+ xtra_rows = new_rows - old_rows;
+ if (xtra_rows < 0) {
+ // May scroll text up. If there is not enough
+ // remaining text or scrolling fails, must redraw the
+ // rest. If scrolling works, must redraw the text
+ // below the scrolled text.
+ if (row - xtra_rows >= wp->w_grid.rows - 2) {
+ mod_bot = MAXLNUM;
+ } else {
+ win_scroll_lines(wp, row, xtra_rows);
+ bot_start = wp->w_grid.rows + xtra_rows;
+ }
+ } else if (xtra_rows > 0) {
+ // May scroll text down. If there is not enough
+ // remaining text of scrolling fails, must redraw the
+ // rest.
+ if (row + xtra_rows >= wp->w_grid.rows - 2) {
+ mod_bot = MAXLNUM;
+ } else {
+ win_scroll_lines(wp, row + old_rows, xtra_rows);
+ if (top_end > row + old_rows) {
+ // Scrolled the part at the top that requires
+ // updating down.
+ top_end += xtra_rows;
+ }
+ }
+ }
+
+ // When not updating the rest, may need to move w_lines[]
+ // entries.
+ if (mod_bot != MAXLNUM && i != j) {
+ if (j < i) {
+ int x = row + new_rows;
+
+ // move entries in w_lines[] upwards
+ for (;;) {
+ // stop at last valid entry in w_lines[]
+ if (i >= wp->w_lines_valid) {
+ wp->w_lines_valid = (int)j;
+ break;
+ }
+ wp->w_lines[j] = wp->w_lines[i];
+ // stop at a line that won't fit
+ if (x + (int)wp->w_lines[j].wl_size
+ > wp->w_grid.rows) {
+ wp->w_lines_valid = (int)j + 1;
+ break;
+ }
+ x += wp->w_lines[j++].wl_size;
+ i++;
+ }
+ if (bot_start > x) {
+ bot_start = x;
+ }
+ } else { // j > i
+ // move entries in w_lines[] downwards
+ j -= i;
+ wp->w_lines_valid += (linenr_T)j;
+ if (wp->w_lines_valid > wp->w_grid.rows) {
+ wp->w_lines_valid = wp->w_grid.rows;
+ }
+ for (i = wp->w_lines_valid; i - j >= idx; i--) {
+ wp->w_lines[i] = wp->w_lines[i - j];
+ }
+
+ // The w_lines[] entries for inserted lines are
+ // now invalid, but wl_size may be used above.
+ // Reset to zero.
+ while (i >= idx) {
+ wp->w_lines[i].wl_size = 0;
+ wp->w_lines[i--].wl_valid = false;
+ }
+ }
+ }
+ }
+ }
+
+ // When lines are folded, display one line for all of them.
+ // Otherwise, display normally (can be several display lines when
+ // 'wrap' is on).
+ foldinfo_T foldinfo = fold_info(wp, lnum);
+
+ if (foldinfo.fi_lines == 0
+ && idx < wp->w_lines_valid
+ && wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum == lnum
+ && lnum > wp->w_topline
+ && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows
+ && win_get_fill(wp, lnum) == 0) {
+ // This line is not going to fit. Don't draw anything here,
+ // will draw "@ " lines below.
+ row = wp->w_grid.rows + 1;
+ } else {
+ prepare_search_hl(wp, &screen_search_hl, lnum);
+ // Let the syntax stuff know we skipped a few lines.
+ if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
+ && syntax_present(wp)) {
+ syntax_end_parsing(syntax_last_parsed + 1);
+ }
+
+ // Display one line
+ row = win_line(wp, lnum, srow,
+ foldinfo.fi_lines ? srow : wp->w_grid.rows,
+ mod_top == 0, false, foldinfo, &line_providers, &provider_err);
+
+ if (foldinfo.fi_lines == 0) {
+ wp->w_lines[idx].wl_folded = false;
+ wp->w_lines[idx].wl_lastlnum = lnum;
+ did_update = DID_LINE;
+ syntax_last_parsed = lnum;
+ } else {
+ foldinfo.fi_lines--;
+ wp->w_lines[idx].wl_folded = true;
+ wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
+ did_update = DID_FOLD;
+ }
+ }
+
+ wp->w_lines[idx].wl_lnum = lnum;
+ wp->w_lines[idx].wl_valid = true;
+
+ if (row > wp->w_grid.rows) { // past end of grid
+ // we may need the size of that too long line later on
+ if (dollar_vcol == -1) {
+ wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true);
+ }
+ idx++;
+ break;
+ }
+ if (dollar_vcol == -1) {
+ wp->w_lines[idx].wl_size = (uint16_t)(row - srow);
+ }
+ idx++;
+ lnum += foldinfo.fi_lines + 1;
+ } else {
+ if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) {
+ // 'relativenumber' set and cursor moved vertically: The
+ // text doesn't need to be drawn, but the number column does.
+ foldinfo_T info = fold_info(wp, lnum);
+ (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true,
+ info, &line_providers, &provider_err);
+ }
+
+ // This line does not need to be drawn, advance to the next one.
+ row += wp->w_lines[idx++].wl_size;
+ if (row > wp->w_grid.rows) { // past end of screen
+ break;
+ }
+ lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
+ did_update = DID_NONE;
+ }
+
+ if (lnum > buf->b_ml.ml_line_count) {
+ eof = true;
+ break;
+ }
+ }
+ // End of loop over all window lines.
+
+ // Now that the window has been redrawn with the old and new cursor line,
+ // update w_last_cursorline.
+ wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0;
+
+ wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
+
+ if (idx > wp->w_lines_valid) {
+ wp->w_lines_valid = idx;
+ }
+
+ // Let the syntax stuff know we stop parsing here.
+ if (syntax_last_parsed != 0 && syntax_present(wp)) {
+ syntax_end_parsing(syntax_last_parsed + 1);
+ }
+
+ // If we didn't hit the end of the file, and we didn't finish the last
+ // line we were working on, then the line didn't fit.
+ wp->w_empty_rows = 0;
+ wp->w_filler_rows = 0;
+ if (!eof && !didline) {
+ int at_attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, HLF_AT));
+ if (lnum == wp->w_topline) {
+ // Single line that does not fit!
+ // Don't overwrite it, it can be edited.
+ wp->w_botline = lnum + 1;
+ } else if (win_get_fill(wp, lnum) >= wp->w_grid.rows - srow) {
+ // Window ends in filler lines.
+ wp->w_botline = lnum;
+ wp->w_filler_rows = wp->w_grid.rows - srow;
+ } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
+ int scr_row = wp->w_grid.rows - 1;
+
+ // Last line isn't finished: Display "@@@" in the last screen line.
+ grid_puts_len(&wp->w_grid, "@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr);
+
+ grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols,
+ '@', ' ', at_attr);
+ set_empty_rows(wp, srow);
+ wp->w_botline = lnum;
+ } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
+ int start_col = wp->w_grid.cols - 3;
+
+ // Last line isn't finished: Display "@@@" at the end.
+ grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
+ MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
+ set_empty_rows(wp, srow);
+ wp->w_botline = lnum;
+ } else {
+ win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT);
+ wp->w_botline = lnum;
+ }
+ } else {
+ if (eof) { // we hit the end of the file
+ wp->w_botline = buf->b_ml.ml_line_count + 1;
+ j = win_get_fill(wp, wp->w_botline);
+ if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) {
+ // Display filler text below last line. win_line() will check
+ // for ml_line_count+1 and only draw filler lines
+ foldinfo_T info = FOLDINFO_INIT;
+ row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
+ false, false, info, &line_providers, &provider_err);
+ }
+ } else if (dollar_vcol == -1) {
+ wp->w_botline = lnum;
+ }
+
+ // Make sure the rest of the screen is blank.
+ // write the "eob" character from 'fillchars' to rows that aren't part
+ // of the file.
+ win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows,
+ HLF_EOB);
+ }
+
+ kvi_destroy(line_providers);
+
+ if (wp->w_redr_type >= UPD_REDRAW_TOP) {
+ draw_vsep_win(wp);
+ draw_hsep_win(wp);
+ draw_sep_connectors_win(wp);
+ }
+ syn_set_timeout(NULL);
+
+ // Reset the type of redrawing required, the window has been updated.
+ wp->w_redr_type = 0;
+ wp->w_old_topfill = wp->w_topfill;
+ wp->w_old_botfill = wp->w_botfill;
+
+ // Send win_extmarks if needed
+ for (size_t n = 0; n < kv_size(win_extmark_arr); n++) {
+ ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle,
+ kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id,
+ kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col);
+ }
+
+ if (dollar_vcol == -1) {
+ // There is a trick with w_botline. If we invalidate it on each
+ // change that might modify it, this will cause a lot of expensive
+ // calls to plines_win() in update_topline() each time. Therefore the
+ // value of w_botline is often approximated, and this value is used to
+ // compute the value of w_topline. If the value of w_botline was
+ // wrong, check that the value of w_topline is correct (cursor is on
+ // the visible part of the text). If it's not, we need to redraw
+ // again. Mostly this just means scrolling up a few lines, so it
+ // doesn't look too bad. Only do this for the current window (where
+ // changes are relevant).
+ wp->w_valid |= VALID_BOTLINE;
+ wp->w_viewport_invalid = true;
+ if (wp == curwin && wp->w_botline != old_botline && !recursive) {
+ recursive = true;
+ curwin->w_valid &= ~VALID_TOPLINE;
+ update_topline(curwin); // may invalidate w_botline again
+ if (must_redraw != 0) {
+ // Don't update for changes in buffer again.
+ i = curbuf->b_mod_set;
+ curbuf->b_mod_set = false;
+ win_update(curwin, providers);
+ must_redraw = 0;
+ curbuf->b_mod_set = i;
+ }
+ recursive = false;
+ }
+ }
+
+ // restore got_int, unless CTRL-C was hit while redrawing
+ if (!got_int) {
+ got_int = save_got_int;
+ }
+}
+
+/// Redraw a window later, with update_screen(type).
+///
+/// Set must_redraw only if not already set to a higher value.
+/// e.g. if must_redraw is UPD_CLEAR, type UPD_NOT_VALID will do nothing.
+void redraw_later(win_T *wp, int type)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!exiting && wp->w_redr_type < type) {
+ wp->w_redr_type = type;
+ if (type >= UPD_NOT_VALID) {
+ wp->w_lines_valid = 0;
+ }
+ if (must_redraw < type) { // must_redraw is the maximum of all windows
+ must_redraw = type;
+ }
+ }
+}
+
+/// Mark all windows to be redrawn later.
+void redraw_all_later(int type)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ redraw_later(wp, type);
+ }
+ // This may be needed when switching tabs.
+ if (must_redraw < type) {
+ must_redraw = type;
+ }
+}
+
+void screen_invalidate_highlights(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ redraw_later(wp, UPD_NOT_VALID);
+ wp->w_grid_alloc.valid = false;
+ }
+}
+
+/// Mark all windows that are editing the current buffer to be updated later.
+void redraw_curbuf_later(int type)
+{
+ redraw_buf_later(curbuf, type);
+}
+
+void redraw_buf_later(buf_T *buf, int type)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf) {
+ redraw_later(wp, type);
+ }
+ }
+}
+
+void redraw_buf_line_later(buf_T *buf, linenr_T line)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf
+ && line >= wp->w_topline && line < wp->w_botline) {
+ redrawWinline(wp, line);
+ }
+ }
+}
+
+void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf
+ && lastline >= wp->w_topline && firstline < wp->w_botline) {
+ if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) {
+ wp->w_redraw_top = firstline;
+ }
+ if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
+ wp->w_redraw_bot = lastline;
+ }
+ redraw_later(wp, UPD_VALID);
+ }
+ }
+}
+
+/// called when the status bars for the buffer 'buf' need to be updated
+void redraw_buf_status_later(buf_T *buf)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf
+ && (wp->w_status_height
+ || (wp == curwin && global_stl_height())
+ || wp->w_winbar_height)) {
+ wp->w_redr_status = true;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
+ }
+ }
+ }
+}
+
+/// Mark all status lines and window bars for redraw; used after first :cd
+void status_redraw_all(void)
+{
+ bool is_stl_global = global_stl_height() != 0;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)
+ || wp->w_winbar_height) {
+ wp->w_redr_status = true;
+ redraw_later(wp, UPD_VALID);
+ }
+ }
+}
+
+/// Marks all status lines and window bars of the current buffer for redraw.
+void status_redraw_curbuf(void)
+{
+ status_redraw_buf(curbuf);
+}
+
+/// Marks all status lines and window bars of the given buffer for redraw.
+void status_redraw_buf(buf_T *buf)
+{
+ bool is_stl_global = global_stl_height() != 0;
+
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height)
+ || (is_stl_global && wp == curwin) || wp->w_winbar_height)) {
+ wp->w_redr_status = true;
+ redraw_later(wp, UPD_VALID);
+ }
+ }
+}
+
+/// Redraw all status lines that need to be redrawn.
+void redraw_statuslines(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_redr_status) {
+ win_check_ns_hl(wp);
+ win_redr_winbar(wp);
+ win_redr_status(wp);
+ }
+ }
+
+ win_check_ns_hl(NULL);
+ if (redraw_tabline) {
+ draw_tabline();
+ }
+}
+
+/// Redraw all status lines at the bottom of frame "frp".
+void win_redraw_last_status(const frame_T *frp)
+ FUNC_ATTR_NONNULL_ARG(1)
+{
+ if (frp->fr_layout == FR_LEAF) {
+ frp->fr_win->w_redr_status = true;
+ } else if (frp->fr_layout == FR_ROW) {
+ FOR_ALL_FRAMES(frp, frp->fr_child) {
+ win_redraw_last_status(frp);
+ }
+ } else {
+ assert(frp->fr_layout == FR_COL);
+ frp = frp->fr_child;
+ while (frp->fr_next != NULL) {
+ frp = frp->fr_next;
+ }
+ win_redraw_last_status(frp);
+ }
+}
+
+/// Changed something in the current window, at buffer line "lnum", that
+/// requires that line and possibly other lines to be redrawn.
+/// Used when entering/leaving Insert mode with the cursor on a folded line.
+/// Used to remove the "$" from a change command.
+/// Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
+/// may become invalid and the whole window will have to be redrawn.
+void redrawWinline(win_T *wp, linenr_T lnum)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (lnum >= wp->w_topline
+ && lnum < wp->w_botline) {
+ if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
+ wp->w_redraw_top = lnum;
+ }
+ if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
+ wp->w_redraw_bot = lnum;
+ }
+ redraw_later(wp, UPD_VALID);
+ }
+}
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
new file mode 100644
index 0000000000..ef99899581
--- /dev/null
+++ b/src/nvim/drawscreen.h
@@ -0,0 +1,25 @@
+#ifndef NVIM_DRAWSCREEN_H
+#define NVIM_DRAWSCREEN_H
+
+#include "nvim/drawline.h"
+
+/// flags for update_screen()
+/// The higher the value, the higher the priority
+enum {
+ UPD_VALID = 10, ///< buffer not changed, or changes marked with b_mod_*
+ UPD_INVERTED = 20, ///< redisplay inverted part that changed
+ UPD_INVERTED_ALL = 25, ///< redisplay whole inverted part
+ UPD_REDRAW_TOP = 30, ///< display first w_upd_rows screen lines
+ UPD_SOME_VALID = 35, ///< like UPD_NOT_VALID but may scroll
+ UPD_NOT_VALID = 40, ///< buffer needs complete redraw
+ UPD_CLEAR = 50, ///< screen messed up, clear it
+};
+
+/// While redrawing the screen this flag is set. It means the screen size
+/// ('lines' and 'rows') must not be changed.
+EXTERN bool updating_screen INIT(= 0);
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "drawscreen.h.generated.h"
+#endif
+#endif // NVIM_DRAWSCREEN_H
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 5861e4c52b..aee8389900 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -16,6 +16,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/event/loop.h"
@@ -25,6 +26,7 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -46,15 +48,16 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/quickfix.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/terminal.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -91,6 +94,10 @@ typedef struct insert_state {
#define BACKSPACE_WORD_NOT_SPACE 3
#define BACKSPACE_LINE 4
+/// Set when doing something for completion that may call edit() recursively,
+/// which is not allowed.
+static bool compl_busy = false;
+
static colnr_T Insstart_textlen; // length of line when insert started
static colnr_T Insstart_blank_vcol; // vcol for first inserted blank
static bool update_Insstart_orig = true; // set Insstart_orig to Insstart
@@ -103,8 +110,6 @@ static int did_restart_edit; // "restart_edit" when calling edit()
static bool can_cindent; // may do cindenting on this line
-static int old_indent = 0; // for ^^D command in insert mode
-
static int revins_on; // reverse insert mode on
static int revins_chars; // how much to skip after edit
static int revins_legal; // was the last char 'legal'?
@@ -114,8 +119,6 @@ static bool ins_need_undo; // call u_save() before inserting a
// char. Set when edit() is called.
// after that arrow_used is used.
-static bool did_add_space = false; // auto_format() added an extra space
- // under the cursor
static TriState dont_sync_undo = kFalse; // CTRL-G U prevents syncing undo
// for the next left/right cursor key
@@ -447,7 +450,7 @@ static int insert_check(VimState *state)
&& (curwin->w_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) {
if (curwin->w_topfill > 0) {
- --curwin->w_topfill;
+ curwin->w_topfill--;
} else if (hasFolding(curwin->w_topline, NULL, &s->old_topline)) {
set_topline(curwin, s->old_topline + 1);
} else {
@@ -623,16 +626,17 @@ static int insert_execute(VimState *state, int key)
}
if (cindent_on() && ctrl_x_mode_none()) {
+ s->line_is_white = inindent(0);
// A key name preceded by a bang means this key is not to be
// inserted. Skip ahead to the re-indenting below.
- // A key name preceded by a star means that indenting has to be
- // done before inserting the key.
- s->line_is_white = inindent(0);
- if (in_cinkeys(s->c, '!', s->line_is_white)) {
- insert_do_cindent(s);
+ if (in_cinkeys(s->c, '!', s->line_is_white)
+ && stop_arrow() == OK) {
+ do_c_expr_indent();
return 1; // continue
}
+ // A key name preceded by a star means that indenting has to be
+ // done before inserting the key.
if (can_cindent && in_cinkeys(s->c, '*', s->line_is_white)
&& stop_arrow() == OK) {
do_c_expr_indent();
@@ -1089,7 +1093,7 @@ check_pum:
// but it is under other ^X modes
if (*curbuf->b_p_cpt == NUL
&& (ctrl_x_mode_normal() || ctrl_x_mode_whole_line())
- && !(compl_cont_status & CONT_LOCAL)) {
+ && !compl_status_local()) {
goto normalchar;
}
@@ -1175,9 +1179,11 @@ normalchar:
static void insert_do_complete(InsertState *s)
{
compl_busy = true;
+ disable_fold_update++; // don't redraw folds here
if (ins_complete(s->c, true) == FAIL) {
- compl_cont_status = 0;
+ compl_status_clear();
}
+ disable_fold_update--;
compl_busy = false;
can_si = may_do_si(); // allow smartindenting
}
@@ -1348,7 +1354,7 @@ void ins_redraw(bool ready)
} else if (clear_cmdline || redraw_cmdline) {
showmode(); // clear cmdline and show mode
}
- showruler(false);
+ show_cursor_info(false);
setcursor();
emsg_on_display = false; // may remove error message now
}
@@ -1517,8 +1523,7 @@ void edit_unputchar(void)
if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT) {
redrawWinline(curwin, curwin->w_cursor.lnum);
} else {
- grid_puts(&curwin->w_grid, pc_bytes, pc_row - msg_scrolled, pc_col,
- pc_attr);
+ grid_puts(&curwin->w_grid, (char *)pc_bytes, pc_row - msg_scrolled, pc_col, pc_attr);
}
}
}
@@ -1553,7 +1558,7 @@ void display_dollar(colnr_T col)
* Call this function before moving the cursor from the normal insert position
* in insert mode.
*/
-static void undisplay_dollar(void)
+void undisplay_dollar(void)
{
if (dollar_vcol >= 0) {
dollar_vcol = -1;
@@ -1567,7 +1572,7 @@ static void undisplay_dollar(void)
/// 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 round if true, round the indent to 'shiftwidth' (only with _INC and _Dec).
/// @param replaced replaced character, put on replace stack
/// @param call_changed_bytes call changed_bytes()
void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes)
@@ -1591,7 +1596,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
// for the following tricks we don't want list mode
save_p_list = curwin->w_p_list;
- curwin->w_p_list = FALSE;
+ curwin->w_p_list = false;
vc = getvcol_nolist(&curwin->w_cursor);
vcol = vc;
@@ -1659,28 +1664,28 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
} else if (!(State & MODE_INSERT)) {
new_cursor_col = curwin->w_cursor.col;
} else {
- /*
- * Compute the screen column where the cursor should be.
- */
+ // Compute the screen column where the cursor should be.
vcol = get_indent() - vcol;
curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);
- /*
- * Advance the cursor until we reach the right screen column.
- */
- vcol = last_vcol = 0;
- new_cursor_col = -1;
+ // Advance the cursor until we reach the right screen column.
+ last_vcol = 0;
ptr = get_cursor_line_ptr();
- while (vcol <= (int)curwin->w_virtcol) {
- last_vcol = vcol;
- if (new_cursor_col >= 0) {
- new_cursor_col += utfc_ptr2len((char *)ptr + new_cursor_col);
- } else {
- new_cursor_col++;
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, 0, 0, ptr, ptr);
+ while (cts.cts_vcol <= (int)curwin->w_virtcol) {
+ last_vcol = cts.cts_vcol;
+ if (cts.cts_vcol > 0) {
+ MB_PTR_ADV(cts.cts_ptr);
+ }
+ if (*cts.cts_ptr == NUL) {
+ break;
}
- vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol);
+ cts.cts_vcol += lbr_chartabsize(&cts);
}
vcol = last_vcol;
+ new_cursor_col = (int)(cts.cts_ptr - cts.cts_line);
+ clear_chartabsize_arg(&cts);
/*
* May need to insert spaces to be able to position the cursor on
@@ -1692,7 +1697,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
ptr = xmallocz(i);
memset(ptr, ' ', i);
new_cursor_col += (int)i;
- ins_str(ptr);
+ ins_str((char *)ptr);
xfree(ptr);
}
@@ -1710,7 +1715,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
} else {
curwin->w_cursor.col = (colnr_T)new_cursor_col;
}
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
changed_cline_bef_curs();
/*
@@ -1747,7 +1752,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
replace_push(replaced);
replaced = NUL;
}
- ++start_col;
+ start_col++;
}
}
@@ -1772,7 +1777,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
backspace_until_column(0);
// Insert new stuff into line again
- ins_bytes(new_line);
+ ins_bytes((char *)new_line);
xfree(new_line);
@@ -1791,7 +1796,7 @@ void change_indent(int type, int amount, int round, int replaced, int call_chang
/// 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.
-void truncate_spaces(char_u *line)
+void truncate_spaces(char *line)
{
int i;
@@ -1913,7 +1918,7 @@ int get_literal(bool no_simplify)
cc = cc * 10 + nc - '0';
}
- ++i;
+ i++;
}
if (cc > 255
@@ -1948,7 +1953,7 @@ int get_literal(bool no_simplify)
cc = '\n';
}
- --no_mapping;
+ no_mapping--;
if (nc) {
vungetc(nc);
// A character typed with i_CTRL-V_digit cannot have modifiers.
@@ -1970,7 +1975,7 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
// inserted with ins_str(), so as not to replace characters in replace
// mode.
// Only use mod_mask for special keys, to avoid things like <S-Space>,
- // unless 'allow_modmask' is TRUE.
+ // unless 'allow_modmask' is true.
if (mod_mask & MOD_MASK_CMD) { // Command-key never produces a normal key.
allow_modmask = true;
}
@@ -1983,7 +1988,7 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
return;
}
p[len - 1] = NUL;
- ins_str(p);
+ ins_str((char *)p);
AppendToRedobuffLit((char *)p, -1);
ctrlv = false;
}
@@ -2004,9 +2009,6 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
*/
#define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^')
-#define WHITECHAR(cc) (ascii_iswhite(cc) \
- && !utf_iscomposing(utf_ptr2char((char *)get_cursor_pos_ptr() + 1)))
-
///
/// "flags": INSCHAR_FORMAT - force formatting
/// INSCHAR_CTRLV - char typed just after CTRL-V
@@ -2022,7 +2024,7 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
/// @param second_indent indent for second line if >= 0
void insertchar(int c, int flags, int second_indent)
{
- char_u *p;
+ char *p;
int force_format = flags & INSCHAR_FORMAT;
const int textwidth = comp_textwidth(force_format);
@@ -2081,13 +2083,13 @@ void insertchar(int c, int flags, int second_indent)
// Need to remove existing (middle) comment leader and insert end
// comment leader. First, check what comment leader we can find.
char_u *line = get_cursor_line_ptr();
- int i = get_leader_len((char *)line, (char **)&p, false, true);
- if (i > 0 && vim_strchr((char *)p, COM_MIDDLE) != NULL) { // Just checking
+ int i = get_leader_len((char *)line, &p, false, true);
+ if (i > 0 && vim_strchr(p, COM_MIDDLE) != NULL) { // Just checking
// Skip middle-comment string
while (*p && p[-1] != ':') { // find end of middle flags
p++;
}
- int middle_len = (int)copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
+ int middle_len = (int)copy_option_part(&p, (char *)lead_end, COM_MAX_LEN, ",");
// Don't count trailing white space for middle_len
while (middle_len > 0 && ascii_iswhite(lead_end[middle_len - 1])) {
middle_len--;
@@ -2097,7 +2099,7 @@ void insertchar(int c, int flags, int second_indent)
while (*p && p[-1] != ':') { // find end of end flags
p++;
}
- int end_len = (int)copy_option_part((char **)&p, (char *)lead_end, COM_MAX_LEN, ",");
+ int end_len = (int)copy_option_part(&p, (char *)lead_end, COM_MAX_LEN, ",");
// Skip white space before the cursor
i = curwin->w_cursor.col;
@@ -2114,7 +2116,7 @@ void insertchar(int c, int flags, int second_indent)
// Insert the end-comment string, except for the last
// character, which will get inserted as normal later.
- ins_bytes_len(lead_end, (size_t)(end_len - 1));
+ ins_bytes_len((char *)lead_end, (size_t)(end_len - 1));
}
}
}
@@ -2174,7 +2176,7 @@ void insertchar(int c, int flags, int second_indent)
do_digraph(-1); // clear digraphs
do_digraph(buf[i - 1]); // may be the start of a digraph
buf[i] = NUL;
- ins_str(buf);
+ ins_str((char *)buf);
if (flags & INSCHAR_CTRLV) {
redo_literal(*buf);
i = 1;
@@ -2192,7 +2194,7 @@ void insertchar(int c, int flags, int second_indent)
utf_char2bytes(c, (char *)buf);
buf[cc] = NUL;
- ins_char_bytes((char_u *)buf, (size_t)cc);
+ ins_char_bytes(buf, (size_t)cc);
AppendCharToRedobuff(c);
} else {
ins_char(c);
@@ -2205,597 +2207,6 @@ void insertchar(int c, int flags, int second_indent)
}
}
-/// Format text at the current insert position.
-///
-/// If the INSCHAR_COM_LIST flag is present, then the value of second_indent
-/// will be the comment leader length sent to open_line().
-///
-/// @param c character to be inserted (can be NUL)
-static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c)
-{
- int cc;
- int save_char = NUL;
- bool haveto_redraw = false;
- const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
- const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK);
- const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
- const bool fo_white_par = has_format_option(FO_WHITE_PAR);
- bool first_line = true;
- colnr_T leader_len;
- bool no_leader = false;
- int do_comments = (flags & INSCHAR_DO_COM);
- int has_lbr = curwin->w_p_lbr;
-
- // make sure win_lbr_chartabsize() counts correctly
- curwin->w_p_lbr = false;
-
- /*
- * When 'ai' is off we don't want a space under the cursor to be
- * deleted. Replace it with an 'x' temporarily.
- */
- if (!curbuf->b_p_ai
- && !(State & VREPLACE_FLAG)) {
- cc = gchar_cursor();
- if (ascii_iswhite(cc)) {
- save_char = cc;
- pchar_cursor('x');
- }
- }
-
- /*
- * Repeat breaking lines, until the current line is not too long.
- */
- while (!got_int) {
- int startcol; // Cursor column at entry
- int wantcol; // column at textwidth border
- int foundcol; // column for start of spaces
- int end_foundcol = 0; // column for start of word
- colnr_T len;
- colnr_T virtcol;
- int orig_col = 0;
- char_u *saved_text = NULL;
- colnr_T col;
- colnr_T end_col;
- bool did_do_comment = false;
-
- virtcol = get_nolist_virtcol()
- + char2cells(c != NUL ? c : gchar_cursor());
- if (virtcol <= (colnr_T)textwidth) {
- break;
- }
-
- if (no_leader) {
- do_comments = false;
- } else if (!(flags & INSCHAR_FORMAT)
- && has_format_option(FO_WRAP_COMS)) {
- do_comments = true;
- }
-
- // Don't break until after the comment leader
- if (do_comments) {
- char_u *line = get_cursor_line_ptr();
- leader_len = get_leader_len((char *)line, NULL, false, true);
- if (leader_len == 0 && curbuf->b_p_cin) {
- // Check for a line comment after code.
- int comment_start = check_linecomment(line);
- if (comment_start != MAXCOL) {
- leader_len = get_leader_len((char *)line + comment_start, NULL, false, true);
- if (leader_len != 0) {
- leader_len += comment_start;
- }
- }
- }
- } else {
- leader_len = 0;
- }
-
- // If the line doesn't start with a comment leader, then don't
- // start one in a following broken line. Avoids that a %word
- // moved to the start of the next line causes all following lines
- // to start with %.
- if (leader_len == 0) {
- no_leader = true;
- }
- if (!(flags & INSCHAR_FORMAT)
- && leader_len == 0
- && !has_format_option(FO_WRAP)) {
- break;
- }
- if ((startcol = curwin->w_cursor.col) == 0) {
- break;
- }
-
- // find column of textwidth border
- coladvance((colnr_T)textwidth);
- wantcol = curwin->w_cursor.col;
-
- curwin->w_cursor.col = startcol;
- foundcol = 0;
- int skip_pos = 0;
-
- /*
- * Find position to break at.
- * Stop at first entered white when 'formatoptions' has 'v'
- */
- while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
- || (flags & INSCHAR_FORMAT)
- || curwin->w_cursor.lnum != Insstart.lnum
- || curwin->w_cursor.col >= Insstart.col) {
- if (curwin->w_cursor.col == startcol && c != NUL) {
- cc = c;
- } else {
- cc = gchar_cursor();
- }
- if (WHITECHAR(cc)) {
- // remember position of blank just before text
- end_col = curwin->w_cursor.col;
-
- // find start of sequence of blanks
- int wcc = 0; // counter for whitespace chars
- while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
- dec_cursor();
- cc = gchar_cursor();
-
- // Increment count of how many whitespace chars in this
- // group; we only need to know if it's more than one.
- if (wcc < 2) {
- wcc++;
- }
- }
- if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
- break; // only spaces in front of text
- }
-
- // Don't break after a period when 'formatoptions' has 'p' and
- // there are less than two spaces.
- if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
- continue;
- }
-
- // Don't break until after the comment leader
- if (curwin->w_cursor.col < leader_len) {
- break;
- }
-
- if (has_format_option(FO_ONE_LETTER)) {
- // do not break after one-letter words
- if (curwin->w_cursor.col == 0) {
- break; // one-letter word at begin
- }
- // do not break "#a b" when 'tw' is 2
- if (curwin->w_cursor.col <= leader_len) {
- break;
- }
- col = curwin->w_cursor.col;
- dec_cursor();
- cc = gchar_cursor();
-
- if (WHITECHAR(cc)) {
- continue; // one-letter, continue
- }
- curwin->w_cursor.col = col;
- }
-
- inc_cursor();
-
- end_foundcol = end_col + 1;
- foundcol = curwin->w_cursor.col;
- if (curwin->w_cursor.col <= (colnr_T)wantcol) {
- break;
- }
- } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) {
- int ncc;
- bool allow_break;
-
- // Break after or before a multi-byte character.
- if (curwin->w_cursor.col != startcol) {
- // Don't break until after the comment leader
- if (curwin->w_cursor.col < leader_len) {
- break;
- }
- col = curwin->w_cursor.col;
- inc_cursor();
- ncc = gchar_cursor();
- allow_break = utf_allow_break(cc, ncc);
-
- // If we have already checked this position, skip!
- if (curwin->w_cursor.col != skip_pos && allow_break) {
- foundcol = curwin->w_cursor.col;
- end_foundcol = foundcol;
- if (curwin->w_cursor.col <= (colnr_T)wantcol) {
- break;
- }
- }
- curwin->w_cursor.col = col;
- }
-
- if (curwin->w_cursor.col == 0) {
- break;
- }
-
- ncc = cc;
- col = curwin->w_cursor.col;
-
- dec_cursor();
- cc = gchar_cursor();
-
- if (WHITECHAR(cc)) {
- continue; // break with space
- }
- // Don't break until after the comment leader.
- if (curwin->w_cursor.col < leader_len) {
- break;
- }
-
- curwin->w_cursor.col = col;
- skip_pos = curwin->w_cursor.col;
-
- allow_break = utf_allow_break(cc, ncc);
-
- // Must handle this to respect line break prohibition.
- if (allow_break) {
- foundcol = curwin->w_cursor.col;
- end_foundcol = foundcol;
- }
- if (curwin->w_cursor.col <= (colnr_T)wantcol) {
- const bool ncc_allow_break = utf_allow_break_before(ncc);
-
- if (allow_break) {
- break;
- }
- if (!ncc_allow_break && !fo_rigor_tw) {
- // Enable at most 1 punct hang outside of textwidth.
- if (curwin->w_cursor.col == startcol) {
- // We are inserting a non-breakable char, postpone
- // line break check to next insert.
- end_foundcol = foundcol = 0;
- break;
- }
-
- // Neither cc nor ncc is NUL if we are here, so
- // it's safe to inc_cursor.
- col = curwin->w_cursor.col;
-
- inc_cursor();
- cc = ncc;
- ncc = gchar_cursor();
- // handle insert
- ncc = (ncc != NUL) ? ncc : c;
-
- allow_break = utf_allow_break(cc, ncc);
-
- if (allow_break) {
- // Break only when we are not at end of line.
- end_foundcol = foundcol = ncc == NUL? 0 : curwin->w_cursor.col;
- break;
- }
- curwin->w_cursor.col = col;
- }
- }
- }
- if (curwin->w_cursor.col == 0) {
- break;
- }
- dec_cursor();
- }
-
- if (foundcol == 0) { // no spaces, cannot break line
- curwin->w_cursor.col = startcol;
- break;
- }
-
- // Going to break the line, remove any "$" now.
- undisplay_dollar();
-
- // Offset between cursor position and line break is used by replace
- // stack functions. MODE_VREPLACE does not use this, and backspaces
- // over the text instead.
- if (State & VREPLACE_FLAG) {
- orig_col = startcol; // Will start backspacing from here
- } else {
- replace_offset = startcol - end_foundcol;
- }
-
- /*
- * adjust startcol for spaces that will be deleted and
- * characters that will remain on top line
- */
- curwin->w_cursor.col = foundcol;
- while ((cc = gchar_cursor(), WHITECHAR(cc))
- && (!fo_white_par || curwin->w_cursor.col < startcol)) {
- inc_cursor();
- }
- startcol -= curwin->w_cursor.col;
- if (startcol < 0) {
- startcol = 0;
- }
-
- if (State & VREPLACE_FLAG) {
- // In MODE_VREPLACE state, we will backspace over the text to be
- // wrapped, so save a copy now to put on the next line.
- saved_text = vim_strsave(get_cursor_pos_ptr());
- curwin->w_cursor.col = orig_col;
- saved_text[startcol] = NUL;
-
- // Backspace over characters that will move to the next line
- if (!fo_white_par) {
- backspace_until_column(foundcol);
- }
- } else {
- // put cursor after pos. to break line
- if (!fo_white_par) {
- curwin->w_cursor.col = foundcol;
- }
- }
-
- /*
- * Split the line just before the margin.
- * Only insert/delete lines, but don't really redraw the window.
- */
- open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
- + (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
- + (do_comments ? OPENLINE_DO_COM : 0)
- + OPENLINE_FORMAT
- + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0),
- ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent),
- &did_do_comment);
- if (!(flags & INSCHAR_COM_LIST)) {
- old_indent = 0;
- }
-
- // If a comment leader was inserted, may also do this on a following
- // line.
- if (did_do_comment) {
- no_leader = false;
- }
-
- replace_offset = 0;
- if (first_line) {
- if (!(flags & INSCHAR_COM_LIST)) {
- // This section is for auto-wrap of numeric lists. When not
- // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
- // flag will be set and open_line() will handle it (as seen
- // above). The code here (and in get_number_indent()) will
- // recognize comments if needed...
- if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) {
- second_indent = get_number_indent(curwin->w_cursor.lnum - 1);
- }
- if (second_indent >= 0) {
- if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, second_indent, false, NUL, true);
- } else if (leader_len > 0 && second_indent - leader_len > 0) {
- int padding = second_indent - leader_len;
-
- // We started at the first_line of a numbered list
- // that has a comment. the open_line() function has
- // inserted the proper comment leader and positioned
- // the cursor at the end of the split line. Now we
- // add the additional whitespace needed after the
- // comment leader for the numbered list.
- for (int i = 0; i < padding; i++) {
- ins_str((char_u *)" ");
- }
- changed_bytes(curwin->w_cursor.lnum, leader_len);
- } else {
- (void)set_indent(second_indent, SIN_CHANGED);
- }
- }
- }
- first_line = false;
- }
-
- if (State & VREPLACE_FLAG) {
- // In MODE_VREPLACE state we have backspaced over the text to be
- // moved, now we re-insert it into the new line.
- ins_bytes(saved_text);
- xfree(saved_text);
- } else {
- /*
- * Check if cursor is not past the NUL off the line, cindent
- * may have added or removed indent.
- */
- curwin->w_cursor.col += startcol;
- len = (colnr_T)STRLEN(get_cursor_line_ptr());
- if (curwin->w_cursor.col > len) {
- curwin->w_cursor.col = len;
- }
- }
-
- haveto_redraw = true;
- can_cindent = true;
- // moved the cursor, don't autoindent or cindent now
- did_ai = false;
- did_si = false;
- can_si = false;
- can_si_back = false;
- line_breakcheck();
- }
-
- if (save_char != NUL) { // put back space after cursor
- pchar_cursor((char_u)save_char);
- }
-
- curwin->w_p_lbr = has_lbr;
-
- if (!format_only && haveto_redraw) {
- update_topline(curwin);
- redraw_curbuf_later(VALID);
- }
-}
-
-/// Called after inserting or deleting text: When 'formatoptions' includes the
-/// 'a' flag format from the current line until the end of the paragraph.
-/// Keep the cursor at the same position relative to the text.
-/// The caller must have saved the cursor line for undo, following ones will be
-/// saved here.
-///
-/// @param trailblank when true also format with trailing blank
-/// @param prev_line may start in previous line
-void auto_format(bool trailblank, bool prev_line)
-{
- pos_T pos;
- colnr_T len;
- char_u *old;
- char_u *new, *pnew;
- int wasatend;
- int cc;
-
- if (!has_format_option(FO_AUTO)) {
- return;
- }
-
- pos = curwin->w_cursor;
- old = get_cursor_line_ptr();
-
- // may remove added space
- check_auto_format(false);
-
- // Don't format in Insert mode when the cursor is on a trailing blank, the
- // user might insert normal text next. Also skip formatting when "1" is
- // in 'formatoptions' and there is a single character before the cursor.
- // Otherwise the line would be broken and when typing another non-white
- // next they are not joined back together.
- wasatend = (pos.col == (colnr_T)STRLEN(old));
- if (*old != NUL && !trailblank && wasatend) {
- dec_cursor();
- cc = gchar_cursor();
- if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
- && has_format_option(FO_ONE_LETTER)) {
- dec_cursor();
- }
- cc = gchar_cursor();
- if (WHITECHAR(cc)) {
- curwin->w_cursor = pos;
- return;
- }
- curwin->w_cursor = pos;
- }
-
- // With the 'c' flag in 'formatoptions' and 't' missing: only format
- // comments.
- if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
- && get_leader_len((char *)old, NULL, false, true) == 0) {
- return;
- }
-
- /*
- * May start formatting in a previous line, so that after "x" a word is
- * moved to the previous line if it fits there now. Only when this is not
- * the start of a paragraph.
- */
- if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) {
- --curwin->w_cursor.lnum;
- if (u_save_cursor() == FAIL) {
- return;
- }
- }
-
- /*
- * Do the formatting and restore the cursor position. "saved_cursor" will
- * be adjusted for the text formatting.
- */
- saved_cursor = pos;
- format_lines((linenr_T) - 1, false);
- curwin->w_cursor = saved_cursor;
- saved_cursor.lnum = 0;
-
- if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
- // "cannot happen"
- curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
- coladvance(MAXCOL);
- } else {
- check_cursor_col();
- }
-
- // Insert mode: If the cursor is now after the end of the line while it
- // previously wasn't, the line was broken. Because of the rule above we
- // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
- // formatted.
- if (!wasatend && has_format_option(FO_WHITE_PAR)) {
- new = get_cursor_line_ptr();
- len = (colnr_T)STRLEN(new);
- if (curwin->w_cursor.col == len) {
- pnew = vim_strnsave(new, (size_t)len + 2);
- pnew[len] = ' ';
- pnew[len + 1] = NUL;
- ml_replace(curwin->w_cursor.lnum, (char *)pnew, false);
- // remove the space later
- did_add_space = true;
- } else {
- // may remove added space
- check_auto_format(false);
- }
- }
-
- check_cursor();
-}
-
-/// When an extra space was added to continue a paragraph for auto-formatting,
-/// delete it now. The space must be under the cursor, just after the insert
-/// position.
-///
-/// @param end_insert true when ending Insert mode
-static void check_auto_format(bool end_insert)
-{
- int c = ' ';
- int cc;
-
- if (did_add_space) {
- cc = gchar_cursor();
- if (!WHITECHAR(cc)) {
- // Somehow the space was removed already.
- did_add_space = false;
- } else {
- if (!end_insert) {
- inc_cursor();
- c = gchar_cursor();
- dec_cursor();
- }
- if (c != NUL) {
- // The space is no longer at the end of the line, delete it.
- del_char(false);
- did_add_space = false;
- }
- }
- }
-}
-
-/// Find out textwidth to be used for formatting:
-/// if 'textwidth' option is set, use it
-/// else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin'
-/// if invalid value, use 0.
-/// Set default to window width (maximum 79) for "gq" operator.
-///
-/// @param ff force formatting (for "gq" command)
-int comp_textwidth(bool ff)
-{
- int textwidth = (int)curbuf->b_p_tw;
- if (textwidth == 0 && curbuf->b_p_wm) {
- // The width is the window width minus 'wrapmargin' minus all the
- // things that add to the margin.
- textwidth = curwin->w_width_inner - (int)curbuf->b_p_wm;
- if (cmdwin_type != 0) {
- textwidth -= 1;
- }
- textwidth -= win_fdccol_count(curwin);
- textwidth -= win_signcol_count(curwin);
-
- if (curwin->w_p_nu || curwin->w_p_rnu) {
- textwidth -= 8;
- }
- }
- if (textwidth < 0) {
- textwidth = 0;
- }
- if (ff && textwidth == 0) {
- textwidth = curwin->w_width_inner - 1;
- if (textwidth > 79) {
- textwidth = 79;
- }
- }
- return textwidth;
-}
-
/*
* Put a character in the redo buffer, for when just after a CTRL-V.
*/
@@ -2985,7 +2396,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
check_cursor_col(); // make sure it is not past the line
for (;;) {
if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) {
- --curwin->w_cursor.col;
+ curwin->w_cursor.col--;
}
cc = gchar_cursor();
if (!ascii_iswhite(cc)) {
@@ -3002,7 +2413,7 @@ static void stop_insert(pos_T *end_insert_pos, int esc, int nomove)
tpos = curwin->w_cursor;
tpos.col++;
if (cc != NUL && gchar_pos(&tpos) == NUL) {
- ++curwin->w_cursor.col; // put cursor back on the NUL
+ curwin->w_cursor.col++; // put cursor back on the NUL
}
}
@@ -3074,11 +2485,11 @@ void beginline(int flags)
char_u *ptr;
for (ptr = get_cursor_line_ptr(); ascii_iswhite(*ptr)
- && !((flags & BL_FIX) && ptr[1] == NUL); ++ptr) {
- ++curwin->w_cursor.col;
+ && !((flags & BL_FIX) && ptr[1] == NUL); ptr++) {
+ curwin->w_cursor.col++;
}
}
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
}
@@ -3122,7 +2533,7 @@ int oneright(void)
}
curwin->w_cursor.col += l;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
return OK;
}
@@ -3157,7 +2568,7 @@ int oneleft(void)
}
}
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
return OK;
}
@@ -3165,8 +2576,8 @@ int oneleft(void)
return FAIL;
}
- curwin->w_set_curswant = TRUE;
- --curwin->w_cursor.col;
+ curwin->w_set_curswant = true;
+ curwin->w_cursor.col--;
// if the character on the left of the current cursor is a multi-byte
// character, move to its first byte
@@ -3174,7 +2585,7 @@ int oneleft(void)
return OK;
}
-/// @oaram upd_topline When TRUE: update topline
+/// @oaram upd_topline When true: update topline
int cursor_up(long n, int upd_topline)
{
linenr_T lnum;
@@ -3229,7 +2640,7 @@ int cursor_up(long n, int upd_topline)
/// Cursor down a number of logical lines.
///
-/// @param upd_topline When TRUE: update topline
+/// @param upd_topline When true: update topline
int cursor_down(long n, int upd_topline)
{
linenr_T lnum;
@@ -3253,7 +2664,7 @@ int cursor_down(long n, int upd_topline)
if (hasFolding(lnum, NULL, &last)) {
lnum = last + 1;
} else {
- ++lnum;
+ lnum++;
}
if (lnum >= curbuf->b_ml.ml_line_count) {
break;
@@ -3287,12 +2698,12 @@ int cursor_down(long n, int upd_topline)
/// @param no_esc Don't add an ESC at the end
int stuff_inserted(int c, long count, int no_esc)
{
- char_u *esc_ptr;
- char_u *ptr;
- char_u *last_ptr;
- char_u last = NUL;
+ char *esc_ptr;
+ char *ptr;
+ char *last_ptr;
+ char last = NUL;
- ptr = get_last_insert();
+ ptr = (char *)get_last_insert();
if (ptr == NULL) {
emsg(_(e_noinstext));
return FAIL;
@@ -3302,7 +2713,7 @@ int stuff_inserted(int c, long count, int no_esc)
if (c != NUL) {
stuffcharReadbuff(c);
}
- if ((esc_ptr = STRRCHR(ptr, ESC)) != NULL) {
+ if ((esc_ptr = strrchr(ptr, ESC)) != NULL) {
// remove the ESC.
*esc_ptr = NUL;
}
@@ -3433,20 +2844,19 @@ void replace_push(int c)
memmove(p + 1, p, (size_t)replace_offset);
}
*p = (char_u)c;
- ++replace_stack_nr;
+ replace_stack_nr++;
}
-/*
- * Push a character onto the replace stack. Handles a multi-byte character in
- * reverse byte order, so that the first byte is popped off first.
- * Return the number of bytes done (includes composing characters).
- */
-int replace_push_mb(char_u *p)
+/// Push a character onto the replace stack. Handles a multi-byte character in
+/// reverse byte order, so that the first byte is popped off first.
+///
+/// @return the number of bytes done (includes composing characters).
+int replace_push_mb(char *p)
{
- int l = utfc_ptr2len((char *)p);
+ int l = utfc_ptr2len(p);
int j;
- for (j = l - 1; j >= 0; --j) {
+ for (j = l - 1; j >= 0; j--) {
replace_push(p[j]);
}
return l;
@@ -3468,7 +2878,7 @@ static void replace_join(int off)
{
for (ssize_t i = replace_stack_nr; --i >= 0;) {
if (replace_stack[i] == NUL && off-- <= 0) {
- --replace_stack_nr;
+ replace_stack_nr--;
memmove(replace_stack + i, replace_stack + i + 1,
(size_t)(replace_stack_nr - i));
return;
@@ -3507,7 +2917,7 @@ static void mb_replace_pop_ins(int cc)
for (i = 1; i < n; i++) {
buf[i] = (char_u)replace_pop();
}
- ins_bytes_len(buf, (size_t)n);
+ ins_bytes_len((char *)buf, (size_t)n);
} else {
ins_char(cc);
}
@@ -3530,7 +2940,7 @@ static void mb_replace_pop_ins(int cc)
buf[i] = (char_u)replace_pop();
}
if (utf_iscomposing(utf_ptr2char((char *)buf))) {
- ins_bytes_len(buf, (size_t)n);
+ ins_bytes_len((char *)buf, (size_t)n);
} else {
// Not a composing char, put it back.
for (i = n - 1; i >= 0; i--) {
@@ -3579,7 +2989,7 @@ static void replace_do_bs(int limit_col)
// Get the number of screen cells used by the character we are
// going to delete.
getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
- orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol);
+ orig_vcols = win_chartabsize(curwin, (char *)get_cursor_pos_ptr(), start_vcol);
}
(void)del_char_after_col(limit_col);
if (l_State & VREPLACE_FLAG) {
@@ -3594,7 +3004,7 @@ static void replace_do_bs(int limit_col)
ins_len = (int)STRLEN(p) - orig_len;
vcol = start_vcol;
for (i = 0; i < ins_len; i++) {
- vcol += win_chartabsize(curwin, p + i, vcol);
+ vcol += win_chartabsize(curwin, (char *)p + i, vcol);
i += utfc_ptr2len((char *)p) - 1;
}
vcol -= start_vcol;
@@ -3656,7 +3066,7 @@ void fix_indent(void)
/// 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 '*' (indent afterwards)
+/// when == ' ': Only if key is not preceded with '*' or '!' (indent afterwards)
///
/// "keytyped" can have a few special values:
/// KEY_OPEN_FORW :
@@ -3681,9 +3091,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
}
if (*curbuf->b_p_inde != NUL) {
- look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
+ look = (char_u *)curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
} else {
- look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
+ look = (char_u *)curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
}
while (*look) {
/*
@@ -3696,7 +3106,7 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
case '!':
try_match = (*look == '!'); break;
default:
- try_match = (*look != '*'); break;
+ try_match = (*look != '*') && (*look != '!'); break;
}
if (*look == '*' || *look == '!') {
look++;
@@ -3794,12 +3204,9 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
while (*look == '>') {
look++;
}
- }
- /*
- * Is it a word: "=word"?
- */
- else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
- ++look;
+ // Is it a word: "=word"?
+ } else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
+ look++;
if (*look == '~') {
icase = true;
look++;
@@ -3873,10 +3280,8 @@ bool in_cinkeys(int keytyped, int when, bool line_is_empty)
}
}
- /*
- * Skip over ", ".
- */
- look = skip_to_option_part(look);
+ // Skip over ", ".
+ look = (char_u *)skip_to_option_part((char *)look);
}
return false;
}
@@ -4002,13 +3407,13 @@ static void ins_reg(void)
no_mapping++;
allow_keys++;
regname = plain_vgetc();
- LANGMAP_ADJUST(regname, TRUE);
+ LANGMAP_ADJUST(regname, true);
if (regname == Ctrl_R || regname == Ctrl_O || regname == Ctrl_P) {
// Get a third key for literal register insertion
literally = regname;
add_to_showcmd_c(literally);
regname = plain_vgetc();
- LANGMAP_ADJUST(regname, TRUE);
+ LANGMAP_ADJUST(regname, true);
}
no_mapping--;
allow_keys--;
@@ -4046,7 +3451,7 @@ static void ins_reg(void)
need_redraw = true; // remove the '"'
} else if (stop_insert_mode) {
// When the '=' register was used and a function was invoked that
- // did ":stopinsert" then stuff_empty() returns FALSE but we won't
+ // did ":stopinsert" then stuff_empty() returns false but we won't
// insert anything, need to remove the '"'
need_redraw = true;
}
@@ -4197,7 +3602,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Repeat the insert
return false;
}
- stop_insert(&curwin->w_cursor, TRUE, nomove);
+ stop_insert(&curwin->w_cursor, true, nomove);
undisplay_dollar();
}
@@ -4251,7 +3656,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
// Otherwise remove the mode message.
if (reg_recording != 0 || restart_edit != NUL) {
showmode();
- } else if (p_smd) {
+ } else if (p_smd && (got_int || !skip_showmode())) {
msg("");
}
// Exit Insert mode
@@ -4266,7 +3671,7 @@ static void ins_ctrl_(void)
{
if (revins_on && revins_chars && revins_scol >= 0) {
while (gchar_cursor() != NUL && revins_chars--) {
- ++curwin->w_cursor.col;
+ curwin->w_cursor.col++;
}
}
p_ri = !p_ri;
@@ -4395,9 +3800,9 @@ static void ins_shift(int c, int lastc)
if (lastc == '^') {
old_indent = get_indent(); // remember curr. indent
}
- change_indent(INDENT_SET, 0, TRUE, 0, TRUE);
+ change_indent(INDENT_SET, 0, true, 0, true);
} else {
- change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, TRUE, 0, TRUE);
+ change_indent(c == Ctrl_D ? INDENT_DEC : INDENT_INC, 0, true, 0, true);
}
if (did_ai && *skipwhite((char *)get_cursor_line_ptr()) != NUL) {
@@ -4569,7 +3974,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
}
}
- do_join(2, FALSE, FALSE, FALSE, false);
+ do_join(2, false, false, false, false);
if (temp == NUL && gchar_cursor() != NUL) {
inc_cursor();
}
@@ -4671,7 +4076,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
if (State & VREPLACE_FLAG) {
ins_char(' ');
} else {
- ins_str((char_u *)" ");
+ ins_str(" ");
if ((State & REPLACE_FLAG)) {
replace_push(NUL);
}
@@ -4715,7 +4120,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
} else {
const int l_p_deco = p_deco;
if (l_p_deco) {
- (void)utfc_ptr2char(get_cursor_pos_ptr(), cpc);
+ (void)utfc_ptr2char((char *)get_cursor_pos_ptr(), cpc);
}
(void)del_char(false);
// If there are combining characters and 'delcombine' is set
@@ -4851,7 +4256,7 @@ static void ins_mousescroll(int dir)
}
}
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -4882,7 +4287,7 @@ static void ins_left(void)
revins_legal++;
}
revins_chars++;
- } else if (vim_strchr((char *)p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
+ } else if (vim_strchr(p_ww, '[') != NULL && curwin->w_cursor.lnum > 1) {
// if 'whichwrap' set for cursor in insert mode may go to previous line.
// always break undo when moving upwards/downwards, else undo may break
start_arrow(&tpos);
@@ -4975,7 +4380,7 @@ static void ins_right(void)
if (revins_chars) {
revins_chars--;
}
- } else if (vim_strchr((char *)p_ww, ']') != NULL
+ } else if (vim_strchr(p_ww, ']') != NULL
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
// if 'whichwrap' set for cursor in insert mode, may move the
// cursor to the next line
@@ -5019,13 +4424,13 @@ static void ins_up(bool startcol)
undisplay_dollar();
tpos = curwin->w_cursor;
- if (cursor_up(1L, TRUE) == OK) {
+ if (cursor_up(1L, true) == OK) {
if (startcol) {
coladvance(getvcol_nolist(&Insstart));
}
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill) {
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
start_arrow(&tpos);
can_cindent = true;
@@ -5067,13 +4472,13 @@ static void ins_down(bool startcol)
undisplay_dollar();
tpos = curwin->w_cursor;
- if (cursor_down(1L, TRUE) == OK) {
+ if (cursor_down(1L, true) == OK) {
if (startcol) {
coladvance(getvcol_nolist(&Insstart));
}
if (old_topline != curwin->w_topline
|| old_topfill != curwin->w_topfill) {
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
start_arrow(&tpos);
can_cindent = true;
@@ -5177,7 +4582,7 @@ static bool ins_tab(void)
if (State & VREPLACE_FLAG) {
ins_char(' ');
} else {
- ins_str((char_u *)" ");
+ ins_str(" ");
if (State & REPLACE_FLAG) { // no char replaced
replace_push(NUL);
}
@@ -5219,8 +4624,8 @@ static bool ins_tab(void)
// Find first white before the cursor
fpos = curwin->w_cursor;
while (fpos.col > 0 && ascii_iswhite(ptr[-1])) {
- --fpos.col;
- --ptr;
+ fpos.col--;
+ ptr--;
}
// In Replace mode, don't change characters before the insert point.
@@ -5235,11 +4640,15 @@ static bool ins_tab(void)
getvcol(curwin, &fpos, &vcol, NULL, NULL);
getvcol(curwin, cursor, &want_vcol, NULL, NULL);
+ char_u *tab = (char_u *)"\t";
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, 0, vcol, tab, tab);
+
// Use as many TABs as possible. Beware of 'breakindent', 'showbreak'
// and 'linebreak' adding extra virtual columns.
while (ascii_iswhite(*ptr)) {
- i = lbr_chartabsize(NULL, (char_u *)"\t", vcol);
- if (vcol + i > want_vcol) {
+ i = lbr_chartabsize(&cts);
+ if (cts.cts_vcol + i > want_vcol) {
break;
}
if (*ptr != TAB) {
@@ -5252,21 +4661,26 @@ static bool ins_tab(void)
}
}
}
- ++fpos.col;
- ++ptr;
- vcol += i;
+ fpos.col++;
+ ptr++;
+ cts.cts_vcol += i;
}
+ vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (change_col >= 0) {
int repl_off = 0;
- char_u *line = ptr;
-
// Skip over the spaces we need.
- while (vcol < want_vcol && *ptr == ' ') {
- vcol += lbr_chartabsize(line, ptr, vcol);
- ++ptr;
- ++repl_off;
+ init_chartabsize_arg(&cts, curwin, 0, vcol, ptr, ptr);
+ while (cts.cts_vcol < want_vcol && *cts.cts_ptr == ' ') {
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr++;
+ repl_off++;
}
+ ptr = (char_u *)cts.cts_ptr;
+ vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
+
if (vcol > want_vcol) {
// Must have a char with 'showbreak' just before it.
ptr--;
@@ -5302,7 +4716,7 @@ static bool ins_tab(void)
// Insert each char in saved_line from changed_col to
// ptr-cursor
- ins_bytes_len(saved_line + change_col, (size_t)(cursor->col - change_col));
+ ins_bytes_len((char *)saved_line + change_col, (size_t)(cursor->col - change_col));
}
}
@@ -5402,7 +4816,7 @@ static int ins_digraph(void)
if (IS_SPECIAL(c) || mod_mask) { // special key
clear_showcmd();
- insert_special(c, TRUE, FALSE);
+ insert_special(c, true, false);
return NUL;
}
if (c != ESC) {
@@ -5446,7 +4860,6 @@ static int ins_digraph(void)
int ins_copychar(linenr_T lnum)
{
int c;
- int temp;
char_u *ptr, *prev_ptr;
char_u *line;
@@ -5456,17 +4869,23 @@ int ins_copychar(linenr_T lnum)
}
// try to advance to the cursor column
- temp = 0;
- line = ptr = ml_get(lnum);
- prev_ptr = ptr;
+ line = ml_get(lnum);
+ prev_ptr = line;
validate_virtcol();
- while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL) {
- prev_ptr = ptr;
- temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp);
+
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, lnum, 0, line, line);
+ while (cts.cts_vcol < curwin->w_virtcol && *cts.cts_ptr != NUL) {
+ prev_ptr = (char_u *)cts.cts_ptr;
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- if ((colnr_T)temp > curwin->w_virtcol) {
+
+ if (cts.cts_vcol > curwin->w_virtcol) {
ptr = prev_ptr;
+ } else {
+ ptr = (char_u *)cts.cts_ptr;
}
+ clear_chartabsize_arg(&cts);
c = utf_ptr2char((char *)ptr);
if (c == NUL) {
@@ -5488,7 +4907,7 @@ static int ins_ctrl_ey(int tc)
} else {
scrollup_clamp();
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
} else {
c = ins_copychar(curwin->w_cursor.lnum + (c == Ctrl_Y ? -1 : 1));
if (c != NUL) {
@@ -5503,7 +4922,7 @@ static int ins_ctrl_ey(int tc)
}
tw_save = curbuf->b_p_tw;
curbuf->b_p_tw = -1;
- insert_special(c, TRUE, FALSE);
+ insert_special(c, true, false);
curbuf->b_p_tw = tw_save;
revins_chars++;
revins_legal++;
@@ -5552,7 +4971,7 @@ static void ins_try_si(int c)
i = get_indent();
curwin->w_cursor = old_pos;
if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, i, FALSE, NUL, TRUE);
+ change_indent(INDENT_SET, i, false, NUL, true);
} else {
(void)set_indent(i, SIN_CHANGED);
}
@@ -5577,7 +4996,7 @@ static void ins_try_si(int c)
curwin->w_cursor = old_pos;
}
if (temp) {
- shift_line(TRUE, FALSE, 1, TRUE);
+ shift_line(true, false, 1, true);
}
}
}
@@ -5655,11 +5074,16 @@ static char_u *do_insert_char_pre(int c)
return res;
}
-bool can_cindent_get(void)
+bool get_can_cindent(void)
{
return can_cindent;
}
+void set_can_cindent(bool val)
+{
+ can_cindent = val;
+}
+
/// Trigger "event" and take care of fixing undo.
int ins_apply_autocmds(event_T event)
{
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 5911a93099..2dbaa2f8ac 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -1,24 +1,18 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * eval.c: Expression evaluation.
- */
+// eval.c: Expression evaluation.
#include <math.h>
#include <stdlib.h>
-#include "auto/config.h"
-
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
-
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
@@ -28,23 +22,29 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/highlight_group.h"
+#include "nvim/locale.h"
#include "nvim/lua/executor.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
+#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sign.h"
@@ -71,19 +71,15 @@ static char * const namespace_char = "abglstvw";
/// Variable used for g:
static ScopeDictDictItem globvars_var;
-/*
- * Old Vim variables such as "v:version" are also available without the "v:".
- * Also in functions. We need a special hashtable for them.
- */
+/// Old Vim variables such as "v:version" are also available without the "v:".
+/// Also in functions. We need a special hashtable for them.
static hashtab_T compat_hashtab;
/// Used for checking if local variables or arguments used in a lambda.
bool *eval_lavars_used = NULL;
-/*
- * Array to hold the hashtab with variables local to each sourced script.
- * Each item holds a variable (nameless) that points to the dict_T.
- */
+/// Array to hold the hashtab with variables local to each sourced script.
+/// Each item holds a variable (nameless) that points to the dict_T.
typedef struct {
ScopeDictDictItem sv_var;
dict_T sv_dict;
@@ -98,11 +94,9 @@ static int echo_attr = 0; // attributes used for ":echo"
// The names of packages that once were loaded are remembered.
static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL };
-/*
- * Info used by a ":for" loop.
- */
+/// Info used by a ":for" loop.
typedef struct {
- int fi_semicolon; // TRUE if ending in '; var]'
+ int fi_semicolon; // true if ending in '; var]'
int fi_varcount; // nr of variables in the list
listwatch_T fi_lw; // keep an eye on the item used.
list_T *fi_list; // list being used
@@ -356,8 +350,6 @@ void eval_init(void)
{
vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
- struct vimvar *p;
-
init_var_dict(&globvardict, &globvars_var, VAR_DEF_SCOPE);
init_var_dict(&vimvardict, &vimvars_var, VAR_SCOPE);
vimvardict.dv_lock = VAR_FIXED;
@@ -365,7 +357,7 @@ void eval_init(void)
func_init();
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
- p = &vimvars[i];
+ struct vimvar *p = &vimvars[i];
assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN);
STRCPY(p->vv_di.di_key, p->vv_name);
if (p->vv_flags & VV_RO) {
@@ -447,10 +439,8 @@ void eval_init(void)
#if defined(EXITFREE)
void eval_clear(void)
{
- struct vimvar *p;
-
for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) {
- p = &vimvars[i];
+ struct vimvar *p = &vimvars[i];
if (p->vv_di.di_tv.v_type == VAR_STRING) {
XFREE_CLEAR(p->vv_str);
} else if (p->vv_di.di_tv.v_type == VAR_LIST) {
@@ -473,13 +463,13 @@ void eval_clear(void)
// autoloaded script names
ga_clear_strings(&ga_loaded);
- /* Script-local variables. First clear all the variables and in a second
- * loop free the scriptvar_T, because a variable in one script might hold
- * a reference to the whole scope of another script. */
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
+ // Script-local variables. First clear all the variables and in a second
+ // loop free the scriptvar_T, because a variable in one script might hold
+ // a reference to the whole scope of another script.
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
vars_clear(&SCRIPT_VARS(i));
}
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
xfree(SCRIPT_SV(i));
}
ga_clear(&ga_scripts);
@@ -518,10 +508,6 @@ static char *redir_varname = NULL;
/// @return OK if successfully completed the setup. FAIL otherwise.
int var_redir_start(char *name, int append)
{
- int save_emsg;
- int err;
- typval_T tv;
-
// Catch a bad name early.
if (!eval_isnamec1(*name)) {
emsg(_(e_invarg));
@@ -553,10 +539,11 @@ int var_redir_start(char *name, int append)
return FAIL;
}
- /* check if we can write to the variable: set it to or append an empty
- * string */
- save_emsg = did_emsg;
- did_emsg = FALSE;
+ // check if we can write to the variable: set it to or append an empty
+ // string
+ int save_emsg = did_emsg;
+ did_emsg = false;
+ typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = "";
if (append) {
@@ -565,7 +552,7 @@ int var_redir_start(char *name, int append)
set_var_lval(redir_lval, redir_endp, &tv, true, false, "=");
}
clear_lval(redir_lval);
- err = did_emsg;
+ int err = did_emsg;
did_emsg |= save_emsg;
if (err) {
redir_endp = NULL; // don't store a value, only cleanup
@@ -585,12 +572,11 @@ int var_redir_start(char *name, int append)
/// :redir END
void var_redir_str(char *value, int value_len)
{
- int len;
-
if (redir_lval == NULL) {
return;
}
+ int len;
if (value_len == -1) {
len = (int)STRLEN(value); // Append the entire string
} else {
@@ -606,12 +592,11 @@ void var_redir_str(char *value, int value_len)
/// Frees the allocated memory.
void var_redir_stop(void)
{
- typval_T tv;
-
if (redir_lval != NULL) {
// If there was no error: assign the text to the variable.
if (redir_endp != NULL) {
ga_append(&redir_ga, NUL); // Append the trailing NUL.
+ typval_T tv;
tv.v_type = VAR_STRING;
tv.vval.v_string = redir_ga.ga_data;
// Call get_lval() again, if it's inside a Dict or List it may
@@ -641,7 +626,7 @@ int eval_charconvert(const char *const enc_from, const char *const enc_to,
set_vim_var_string(VV_CC_TO, enc_to, -1);
set_vim_var_string(VV_FNAME_IN, fname_from, -1);
set_vim_var_string(VV_FNAME_OUT, fname_to, -1);
- if (eval_to_bool((char *)p_ccv, &err, NULL, false)) {
+ if (eval_to_bool(p_ccv, &err, NULL, false)) {
err = true;
}
set_vim_var_string(VV_CC_FROM, NULL, -1);
@@ -661,7 +646,7 @@ int eval_printexpr(const char *const fname, const char *const args)
set_vim_var_string(VV_FNAME_IN, fname, -1);
set_vim_var_string(VV_CMDARG, args, -1);
- if (eval_to_bool((char *)p_pexpr, &err, NULL, false)) {
+ if (eval_to_bool(p_pexpr, &err, NULL, false)) {
err = true;
}
set_vim_var_string(VV_FNAME_IN, NULL, -1);
@@ -681,7 +666,7 @@ void eval_diff(const char *const origfile, const char *const newfile, const char
set_vim_var_string(VV_FNAME_IN, origfile, -1);
set_vim_var_string(VV_FNAME_NEW, newfile, -1);
set_vim_var_string(VV_FNAME_OUT, outfile, -1);
- (void)eval_to_bool((char *)p_dex, &err, NULL, false);
+ (void)eval_to_bool(p_dex, &err, NULL, false);
set_vim_var_string(VV_FNAME_IN, NULL, -1);
set_vim_var_string(VV_FNAME_NEW, NULL, -1);
set_vim_var_string(VV_FNAME_OUT, NULL, -1);
@@ -701,11 +686,11 @@ void eval_patch(const char *const origfile, const char *const difffile, const ch
}
/// Top level evaluation function, returning a boolean.
-/// Sets "error" to TRUE if there was an error.
+/// Sets "error" to true if there was an error.
///
/// @param skip only parse, don't execute
///
-/// @return TRUE or FALSE.
+/// @return true or false.
int eval_to_bool(char *arg, bool *error, char **nextcmd, int skip)
{
typval_T tv;
@@ -905,7 +890,7 @@ char *eval_to_string(char *arg, char **nextcmd, bool convert)
/// Call eval_to_string() without using current local variables and using
/// textlock.
///
-/// @param use_sandbox when TRUE, use the sandbox.
+/// @param use_sandbox when true, use the sandbox.
char *eval_to_string_safe(char *arg, char **nextcmd, int use_sandbox)
{
char *retval;
@@ -935,7 +920,7 @@ varnumber_T eval_to_number(char *expr)
varnumber_T retval;
char *p = skipwhite(expr);
- ++emsg_off;
+ emsg_off++;
if (eval1(&p, &rettv, true) == FAIL) {
retval = -1;
@@ -943,7 +928,7 @@ varnumber_T eval_to_number(char *expr)
retval = tv_get_number_chk(&rettv, NULL);
tv_clear(&rettv);
}
- --emsg_off;
+ emsg_off--;
return retval;
}
@@ -1000,11 +985,9 @@ void prepare_vimvar(int idx, typval_T *save_tv)
/// When no longer defined, remove the variable from the v: hashtable.
void restore_vimvar(int idx, typval_T *save_tv)
{
- hashitem_T *hi;
-
vimvars[idx].vv_tv = *save_tv;
if (vimvars[idx].vv_type == VAR_UNKNOWN) {
- hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
+ hashitem_T *hi = hash_find(&vimvarht, (char *)vimvars[idx].vv_di.di_key);
if (HASHITEM_EMPTY(hi)) {
internal_error("restore_vimvar()");
} else {
@@ -1040,7 +1023,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
vimvars[VV_VAL].vv_type = VAR_STRING;
vimvars[VV_VAL].vv_str = badword;
if (p_verbose == 0) {
- ++emsg_off;
+ emsg_off++;
}
if (eval1(&p, &rettv, true) == OK) {
@@ -1052,7 +1035,7 @@ list_T *eval_spell_expr(char *badword, char *expr)
}
if (p_verbose == 0) {
- --emsg_off;
+ emsg_off--;
}
restore_vimvar(VV_VAL, &save_val);
@@ -1134,12 +1117,11 @@ varnumber_T call_func_retnr(const char *func, int argc, typval_T *argv)
FUNC_ATTR_NONNULL_ALL
{
typval_T rettv;
- varnumber_T retval;
if (call_vim_function((char *)func, argc, argv, &rettv) == FAIL) {
return -1;
}
- retval = tv_get_number_chk(&rettv, NULL);
+ varnumber_T retval = tv_get_number_chk(&rettv, NULL);
tv_clear(&rettv);
return retval;
}
@@ -1191,42 +1173,6 @@ void *call_func_retlist(const char *func, int argc, typval_T *argv)
return rettv.vval.v_list;
}
-/// Prepare profiling for entering a child or something else that is not
-/// counted for the script/function itself.
-/// Should always be called in pair with prof_child_exit().
-///
-/// @param tm place to store waittime
-void prof_child_enter(proftime_T *tm)
-{
- funccall_T *fc = get_current_funccal();
-
- if (fc != NULL && fc->func->uf_profiling) {
- fc->prof_child = profile_start();
- }
-
- script_prof_save(tm);
-}
-
-/// Take care of time spent in a child.
-/// Should always be called after prof_child_enter().
-///
-/// @param tm where waittime was stored
-void prof_child_exit(proftime_T *tm)
-{
- funccall_T *fc = get_current_funccal();
-
- if (fc != NULL && fc->func->uf_profiling) {
- fc->prof_child = profile_end(fc->prof_child);
- // don't count waiting time
- fc->prof_child = profile_sub_wait(*tm, fc->prof_child);
- fc->func->uf_tm_children =
- profile_add(fc->func->uf_tm_children, fc->prof_child);
- fc->func->uf_tml_children =
- profile_add(fc->func->uf_tml_children, fc->prof_child);
- }
- script_prof_restore(tm);
-}
-
/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding
/// it in "*cp". Doesn't give error messages.
int eval_foldexpr(char *arg, int *cp)
@@ -1235,11 +1181,11 @@ int eval_foldexpr(char *arg, int *cp)
varnumber_T retval;
int use_sandbox = was_set_insecurely(curwin, "foldexpr", OPT_LOCAL);
- ++emsg_off;
+ emsg_off++;
if (use_sandbox) {
- ++sandbox;
+ sandbox++;
}
- ++textlock;
+ textlock++;
*cp = NUL;
if (eval0(arg, &tv, NULL, true) == FAIL) {
retval = 0;
@@ -1260,11 +1206,11 @@ int eval_foldexpr(char *arg, int *cp)
}
tv_clear(&tv);
}
- --emsg_off;
+ emsg_off--;
if (use_sandbox) {
- --sandbox;
+ sandbox--;
}
- --textlock;
+ textlock--;
return (int)retval;
}
@@ -1298,16 +1244,11 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
const bool skip, const int flags, const int fne_flags)
FUNC_ATTR_NONNULL_ARG(1, 3)
{
- dictitem_T *v;
- typval_T var1;
- typval_T var2;
- int empty1 = FALSE;
- listitem_T *ni;
- hashtab_T *ht = NULL;
+ bool empty1 = false;
int quiet = flags & GLV_QUIET;
// Clear everything in "lp".
- memset(lp, 0, sizeof(lval_T));
+ CLEAR_POINTER(lp);
if (skip) {
// When skipping just find the end of the name.
@@ -1355,11 +1296,13 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
return p;
}
+ hashtab_T *ht = NULL;
+
// Only pass &ht when we would write to the variable, it prevents autoload
// as well.
- v = find_var(lp->ll_name, lp->ll_name_len,
- (flags & GLV_READ_ONLY) ? NULL : &ht,
- flags & GLV_NO_AUTOLOAD);
+ dictitem_T *v = find_var(lp->ll_name, lp->ll_name_len,
+ (flags & GLV_READ_ONLY) ? NULL : &ht,
+ flags & GLV_NO_AUTOLOAD);
if (v == NULL && !quiet) {
semsg(_("E121: Undefined variable: %.*s"),
(int)lp->ll_name_len, lp->ll_name);
@@ -1370,7 +1313,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
// Loop until no more [idx] or .key is following.
lp->ll_tv = &v->di_tv;
+ typval_T var1;
var1.v_type = VAR_UNKNOWN;
+ typval_T var2;
var2.v_type = VAR_UNKNOWN;
while (*p == '[' || (*p == '.' && lp->ll_tv->v_type == VAR_DICT)) {
if (!(lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list != NULL)
@@ -1612,7 +1557,7 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const
lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string.
tv_clear(&var2);
if (lp->ll_n2 < 0) {
- ni = tv_list_find(lp->ll_list, (int)lp->ll_n2);
+ listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2);
if (ni == NULL) {
if (!quiet) {
semsg(_(e_listidx), (int64_t)lp->ll_n2);
@@ -1765,9 +1710,7 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool
ll_n1++;
}
- /*
- * Assign the List values to the list items.
- */
+ // Assign the List values to the list items.
for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) {
if (op != NULL && *op != '=') {
eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op);
@@ -1864,7 +1807,7 @@ notify:
/// Evaluate the expression used in a ":for var in expr" command.
/// "arg" points to "var".
///
-/// @param[out] *errp set to TRUE for an error, FALSE otherwise;
+/// @param[out] *errp set to true for an error, false otherwise;
///
/// @return a pointer that holds the info. Null when there is an error.
void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
@@ -1888,7 +1831,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
}
if (skip) {
- ++emsg_skip;
+ emsg_skip++;
}
if (eval0(skipwhite(expr + 2), &tv, nextcmdp, !skip) == OK) {
*errp = false;
@@ -1930,7 +1873,7 @@ void *eval_for_line(const char *arg, bool *errp, char **nextcmdp, int skip)
}
}
if (skip) {
- --emsg_skip;
+ emsg_skip--;
}
return fi;
@@ -2009,7 +1952,7 @@ void free_for_info(void *fi_void)
void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
FUNC_ATTR_NONNULL_ALL
{
- int got_eq = FALSE;
+ bool got_eq = false;
int c;
char *p;
@@ -2035,7 +1978,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
if (c == '&') {
c = (uint8_t)xp->xp_pattern[1];
if (c == '&') {
- ++xp->xp_pattern;
+ xp->xp_pattern++;
xp->xp_context = cmdidx != CMD_let || got_eq
? EXPAND_EXPRESSION : EXPAND_NOTHING;
} else if (c != ' ') {
@@ -2048,7 +1991,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
// environment variable
xp->xp_context = EXPAND_ENV_VARS;
} else if (c == '=') {
- got_eq = TRUE;
+ got_eq = true;
xp->xp_context = EXPAND_EXPRESSION;
} else if (c == '#'
&& xp->xp_context == EXPAND_EXPRESSION) {
@@ -2073,7 +2016,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
xp->xp_context = EXPAND_NOTHING;
} else if (c == '|') {
if (xp->xp_pattern[1] == '|') {
- ++xp->xp_pattern;
+ xp->xp_pattern++;
xp->xp_context = EXPAND_EXPRESSION;
} else {
xp->xp_context = EXPAND_COMMANDS;
@@ -2099,7 +2042,7 @@ void set_context_for_expression(expand_T *xp, char *arg, cmdidx_T cmdidx)
|| cmdidx == CMD_echomsg)
&& xp->xp_context == EXPAND_EXPRESSION) {
for (;;) {
- char *const n = (char *)skiptowhite((char_u *)arg);
+ char *const n = skiptowhite(arg);
if (n == arg || ascii_iswhite_or_nul(*skipwhite(n))) {
break;
@@ -2123,11 +2066,9 @@ void del_menutrans_vars(void)
hash_unlock(&globvarht);
}
-/*
- * Local string buffer for the next two functions to store a variable name
- * with its prefix. Allocated in cat_prefix_varname(), freed later in
- * get_user_var_name().
- */
+/// Local string buffer for the next two functions to store a variable name
+/// with its prefix. Allocated in cat_prefix_varname(), freed later in
+/// get_user_var_name().
static char *varnamebuf = NULL;
static size_t varnamebuflen = 0;
@@ -2171,10 +2112,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (gdone++ == 0) {
hi = globvarht.ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
if (STRNCMP("g:", xp->xp_pattern, 2) == 0) {
return cat_prefix_varname('g', (char *)hi->hi_key);
@@ -2188,10 +2129,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (bdone++ == 0) {
hi = ht->ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
return cat_prefix_varname('b', (char *)hi->hi_key);
}
@@ -2202,10 +2143,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (wdone++ == 0) {
hi = ht->ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
return cat_prefix_varname('w', (char *)hi->hi_key);
}
@@ -2216,10 +2157,10 @@ char *get_user_var_name(expand_T *xp, int idx)
if (tdone++ == 0) {
hi = ht->ht_array;
} else {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
return cat_prefix_varname('t', (char *)hi->hi_key);
}
@@ -2238,7 +2179,7 @@ char *get_user_var_name(expand_T *xp, int idx)
/// Does not use 'cpo' and always uses 'magic'.
///
-/// @return TRUE if "pat" matches "text".
+/// @return true if "pat" matches "text".
int pattern_match(char *pat, char *text, bool ic)
{
int matches = 0;
@@ -2246,7 +2187,7 @@ int pattern_match(char *pat, char *text, bool ic)
// avoid 'l' flag in 'cpoptions'
char *save_cpo = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = ic;
@@ -2291,7 +2232,7 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
funcexe.evaluate = evaluate;
funcexe.partial = partial;
funcexe.basetv = basetv;
- int ret = get_func_tv((char_u *)s, len, rettv, (char_u **)arg, &funcexe);
+ int ret = get_func_tv((char_u *)s, len, rettv, arg, &funcexe);
xfree(s);
@@ -2317,15 +2258,13 @@ static int eval_func(char **const arg, char *const name, const int name_len, typ
// TODO(ZyX-I): move to eval/expressions
-/*
- * The "evaluate" argument: When FALSE, the argument is only parsed but not
- * executed. The function may return OK, but the rettv will be of type
- * VAR_UNKNOWN. The function still returns FAIL for a syntax error.
- */
+/// The "evaluate" argument: When false, the argument is only parsed but not
+/// executed. The function may return OK, but the rettv will be of type
+/// VAR_UNKNOWN. The function still returns FAIL for a syntax error.
/// Handle zero level expression.
/// This calls eval1() and handles error message and nextcmd.
-/// Put the result in "rettv" when returning OK and "evaluate" is TRUE.
+/// Put the result in "rettv" when returning OK and "evaluate" is true.
/// Note: "rettv.v_lock" is not set.
///
/// @return OK or FAIL.
@@ -2372,18 +2311,16 @@ int eval0(char *arg, typval_T *rettv, char **nextcmd, int evaluate)
/// @return OK or FAIL.
int eval1(char **arg, typval_T *rettv, int evaluate)
{
- int result;
+ bool result;
typval_T var2;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval2(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
if ((*arg)[0] == '?') {
- result = FALSE;
+ result = false;
if (evaluate) {
bool error = false;
@@ -2396,17 +2333,13 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
}
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 1);
if (eval1(arg, rettv, evaluate && result) == FAIL) { // recursive!
return FAIL;
}
- /*
- * Check for the ":".
- */
+ // Check for the ":".
if ((*arg)[0] != ':') {
emsg(_("E109: Missing ':' after '?'"));
if (evaluate && result) {
@@ -2415,9 +2348,7 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
return FAIL;
}
- /*
- * Get the third variable.
- */
+ // Get the third variable.
*arg = skipwhite(*arg + 1);
if (eval1(arg, &var2, evaluate && !result) == FAIL) { // Recursive!
if (evaluate && result) {
@@ -2445,22 +2376,16 @@ int eval1(char **arg, typval_T *rettv, int evaluate)
static int eval2(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
- long result;
- int first;
bool error = false;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval3(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
- /*
- * Repeat until there is no following "||".
- */
- first = TRUE;
- result = FALSE;
+ // Repeat until there is no following "||".
+ bool first = true;
+ bool result = false;
while ((*arg)[0] == '|' && (*arg)[1] == '|') {
if (evaluate && first) {
if (tv_get_number_chk(rettv, &error) != 0) {
@@ -2473,17 +2398,13 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
first = false;
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 2);
if (eval3(arg, &var2, evaluate && !result) == FAIL) {
return FAIL;
}
- /*
- * Compute the result.
- */
+ // Compute the result.
if (evaluate && !result) {
if (tv_get_number_chk(&var2, &error) != 0) {
result = true;
@@ -2514,22 +2435,16 @@ static int eval2(char **arg, typval_T *rettv, int evaluate)
static int eval3(char **arg, typval_T *rettv, int evaluate)
{
typval_T var2;
- long result;
- int first;
bool error = false;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval4(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
- /*
- * Repeat until there is no following "&&".
- */
- first = TRUE;
- result = TRUE;
+ // Repeat until there is no following "&&".
+ bool first = true;
+ bool result = true;
while ((*arg)[0] == '&' && (*arg)[1] == '&') {
if (evaluate && first) {
if (tv_get_number_chk(rettv, &error) == 0) {
@@ -2542,17 +2457,13 @@ static int eval3(char **arg, typval_T *rettv, int evaluate)
first = false;
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 2);
if (eval4(arg, &var2, evaluate && result) == FAIL) {
return FAIL;
}
- /*
- * Compute the result.
- */
+ // Compute the result.
if (evaluate && result) {
if (tv_get_number_chk(&var2, &error) == 0) {
result = false;
@@ -2597,9 +2508,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
int len = 2;
bool ic;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval5(arg, rettv, evaluate) == FAIL) {
return FAIL;
}
@@ -2648,9 +2557,7 @@ static int eval4(char **arg, typval_T *rettv, int evaluate)
break;
}
- /*
- * If there is a comparative operator, use it.
- */
+ // If there is a comparative operator, use it.
if (type != EXPR_UNKNOWN) {
// extra question mark appended: ignore case
if (p[len] == '?') {
@@ -2701,16 +2608,12 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
float_T f1 = 0, f2 = 0;
char *p;
- /*
- * Get the first variable.
- */
- if (eval6(arg, rettv, evaluate, FALSE) == FAIL) {
+ // Get the first variable.
+ if (eval6(arg, rettv, evaluate, false) == FAIL) {
return FAIL;
}
- /*
- * Repeat computing, until no '+', '-' or '.' is following.
- */
+ // Repeat computing, until no '+', '-' or '.' is following.
for (;;) {
op = (char_u)(**arg);
if (op != '+' && op != '-' && op != '.') {
@@ -2732,9 +2635,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
}
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
if (op == '.' && *(*arg + 1) == '.') { // ..string concatenation
(*arg)++;
}
@@ -2745,9 +2646,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
}
if (evaluate) {
- /*
- * Compute the result.
- */
+ // Compute the result.
if (op == '.') {
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
@@ -2759,7 +2658,7 @@ static int eval5(char **arg, typval_T *rettv, int evaluate)
tv_clear(&var2);
return FAIL;
}
- p = (char *)concat_str((const char_u *)s1, (const char_u *)s2);
+ p = concat_str(s1, s2);
tv_clear(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
@@ -2876,16 +2775,12 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
float_T f1 = 0, f2 = 0;
bool error = false;
- /*
- * Get the first variable.
- */
+ // Get the first variable.
if (eval7(arg, rettv, evaluate, want_string) == FAIL) {
return FAIL;
}
- /*
- * Repeat computing, until no '*', '/' or '%' is following.
- */
+ // Repeat computing, until no '*', '/' or '%' is following.
for (;;) {
op = (char_u)(**arg);
if (op != '*' && op != '/' && op != '%') {
@@ -2908,9 +2803,7 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
n1 = 0;
}
- /*
- * Get the second variable.
- */
+ // Get the second variable.
*arg = skipwhite(*arg + 1);
if (eval7(arg, &var2, evaluate, false) == FAIL) {
return FAIL;
@@ -2935,10 +2828,8 @@ static int eval6(char **arg, typval_T *rettv, int evaluate, int want_string)
}
}
- /*
- * Compute the result.
- * When either side is a float the result is a float.
- */
+ // Compute the result.
+ // When either side is a float the result is a float.
if (use_float) {
if (op == '*') {
f1 = f1 * f2;
@@ -3051,9 +2942,9 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
get_float = true;
p = skipdigits(p + 2);
if (*p == 'e' || *p == 'E') {
- ++p;
+ p++;
if (*p == '-' || *p == '+') {
- ++p;
+ p++;
}
if (!ascii_isdigit(*p)) {
get_float = false;
@@ -3147,7 +3038,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Lambda: {arg, arg -> expr}
// Dictionary: {'key': val, 'key': val}
case '{':
- ret = get_lambda_tv((char_u **)arg, rettv, evaluate);
+ ret = get_lambda_tv(arg, rettv, evaluate);
if (ret == NOTDONE) {
ret = dict_get_tv(arg, rettv, evaluate, false);
}
@@ -3164,13 +3055,13 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
// Register contents: @r.
case '@':
- ++*arg;
+ (*arg)++;
if (evaluate) {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = get_reg_contents(**arg, kGRegExprSrc);
}
if (**arg != NUL) {
- ++*arg;
+ (*arg)++;
}
break;
@@ -3179,7 +3070,7 @@ static int eval7(char **arg, typval_T *rettv, int evaluate, int want_string)
*arg = skipwhite(*arg + 1);
ret = eval1(arg, rettv, evaluate); // recursive!
if (**arg == ')') {
- ++*arg;
+ (*arg)++;
} else if (ret == OK) {
emsg(_("E110: Missing ')'"));
tv_clear(rettv);
@@ -3325,7 +3216,7 @@ static int call_func_rettv(char **const arg, typval_T *const rettv, const bool e
funcexe.selfdict = selfdict;
funcexe.basetv = basetv;
const int ret = get_func_tv((char_u *)funcname, is_lua ? (int)(*arg - funcname) : -1, rettv,
- (char_u **)arg, &funcexe);
+ arg, &funcexe);
// Clear the funcref afterwards, so that deleting it while
// evaluating the arguments is possible (see test55).
@@ -3353,7 +3244,7 @@ static int eval_lambda(char **const arg, typval_T *const rettv, const bool evalu
typval_T base = *rettv;
rettv->v_type = VAR_UNKNOWN;
- int ret = get_lambda_tv((char_u **)arg, rettv, evaluate);
+ int ret = get_lambda_tv(arg, rettv, evaluate);
if (ret != OK) {
return FAIL;
} else if (**arg != '(') {
@@ -3503,9 +3394,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
typval_T var1 = TV_INITIAL_VALUE;
typval_T var2 = TV_INITIAL_VALUE;
if (**arg == '.') {
- /*
- * dict.name
- */
+ // dict.name
key = *arg + 1;
for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) {}
if (len == 0) {
@@ -3513,11 +3402,9 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
}
*arg = skipwhite(key + len);
} else {
- /*
- * something[idx]
- *
- * Get the (first) variable from inside the [].
- */
+ // something[idx]
+ //
+ // Get the (first) variable from inside the [].
*arg = skipwhite(*arg + 1);
if (**arg == ':') {
empty1 = true;
@@ -3529,9 +3416,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
return FAIL;
}
- /*
- * Get the second variable from inside the [:].
- */
+ // Get the second variable from inside the [:].
if (**arg == ':') {
range = true;
*arg = skipwhite(*arg + 1);
@@ -3772,11 +3657,7 @@ static int eval_index(char **arg, typval_T *rettv, int evaluate, int verbose)
int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate)
FUNC_ATTR_NONNULL_ARG(1)
{
- long numval;
- char *stringval;
- getoption_T opt_type;
bool working = (**arg == '+'); // has("+option")
- int ret = OK;
int opt_flags;
// Isolate the option name and find its value.
@@ -3793,10 +3674,14 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval
return OK;
}
+ long numval;
+ char *stringval;
+ int ret = OK;
+
char c = *option_end;
*option_end = NUL;
- opt_type = get_option_value(*arg, &numval,
- rettv == NULL ? NULL : &stringval, opt_flags);
+ getoption_T opt_type = get_option_value(*arg, &numval,
+ rettv == NULL ? NULL : &stringval, opt_flags);
if (opt_type == gov_unknown) {
if (rettv != NULL) {
@@ -3837,9 +3722,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
char *p;
unsigned int extra = 0;
- /*
- * Find the end of the string, skipping backslashed characters.
- */
+ // Find the end of the string, skipping backslashed characters.
for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
p++;
@@ -3863,10 +3746,8 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
return OK;
}
- /*
- * Copy the string into allocated memory, handling backslashed
- * characters.
- */
+ // Copy the string into allocated memory, handling backslashed
+ // characters.
const int len = (int)(p - *arg + extra);
char *name = xmalloc((size_t)len);
rettv->v_type = VAR_STRING;
@@ -3905,7 +3786,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
}
nr = 0;
while (--n >= 0 && ascii_isxdigit(p[1])) {
- ++p;
+ p++;
nr = (nr << 4) + hex2nr(*p);
}
p++;
@@ -3935,7 +3816,7 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
*name = (char)((*name << 3) + *p++ - '0');
}
}
- ++name;
+ name++;
break;
// Special key, e.g.: "\<C-W>"
@@ -3957,11 +3838,11 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
FALLTHROUGH;
default:
- mb_copy_char((const char_u **)&p, (char_u **)&name);
+ mb_copy_char((const char **)&p, &name);
break;
}
} else {
- mb_copy_char((const char_u **)&p, (char_u **)&name);
+ mb_copy_char((const char **)&p, &name);
}
}
*name = NUL;
@@ -3979,19 +3860,16 @@ static int get_string_tv(char **arg, typval_T *rettv, int evaluate)
static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
{
char *p;
- char *str;
int reduce = 0;
- /*
- * Find the end of the string, skipping ''.
- */
+ // Find the end of the string, skipping ''.
for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p)) {
if (*p == '\'') {
if (p[1] != '\'') {
break;
}
- ++reduce;
- ++p;
+ reduce++;
+ p++;
}
}
@@ -4006,10 +3884,8 @@ static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
return OK;
}
- /*
- * Copy the string into allocated memory, handling '' to ' reduction.
- */
- str = xmalloc((size_t)((p - *arg) - reduce));
+ // Copy the string into allocated memory, handling '' to ' reduction.
+ char *str = xmalloc((size_t)((p - *arg) - reduce));
rettv->v_type = VAR_STRING;
rettv->vval.v_string = str;
@@ -4018,9 +3894,9 @@ static int get_lit_string_tv(char **arg, typval_T *rettv, int evaluate)
if (p[1] != '\'') {
break;
}
- ++p;
+ p++;
}
- mb_copy_char((const char_u **)&p, (char_u **)&str);
+ mb_copy_char((const char **)&p, &str);
}
*str = NUL;
*arg = p + 1;
@@ -4119,16 +3995,14 @@ failret:
/// @param ic ignore case
bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
{
- char_u *s1, *s2;
- dict_T *d1, *d2;
- int a1, a2;
-
// empty and NULL function name considered the same
- s1 = (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial));
+ char_u *s1 =
+ (char_u *)(tv1->v_type == VAR_FUNC ? tv1->vval.v_string : partial_name(tv1->vval.v_partial));
if (s1 != NULL && *s1 == NUL) {
s1 = NULL;
}
- s2 = (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial));
+ char_u *s2 =
+ (char_u *)(tv2->v_type == VAR_FUNC ? tv2->vval.v_string : partial_name(tv2->vval.v_partial));
if (s2 != NULL && *s2 == NUL) {
s2 = NULL;
}
@@ -4141,8 +4015,8 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
}
// empty dict and NULL dict is different
- d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
- d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
+ dict_T *d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
+ dict_T *d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
if (d1 == NULL || d2 == NULL) {
if (d1 != d2) {
return false;
@@ -4152,8 +4026,8 @@ bool func_equal(typval_T *tv1, typval_T *tv2, bool ic)
}
// empty list and no list considered the same
- a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
- a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
+ int a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
+ int a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
if (a1 != a2) {
return false;
}
@@ -4182,25 +4056,23 @@ int get_copyID(void)
return current_copyID;
}
-/*
- * Garbage collection for lists and dictionaries.
- *
- * We use reference counts to be able to free most items right away when they
- * are no longer used. But for composite items it's possible that it becomes
- * unused while the reference count is > 0: When there is a recursive
- * reference. Example:
- * :let l = [1, 2, 3]
- * :let d = {9: l}
- * :let l[1] = d
- *
- * Since this is quite unusual we handle this with garbage collection: every
- * once in a while find out which lists and dicts are not referenced from any
- * variable.
- *
- * Here is a good reference text about garbage collection (refers to Python
- * but it applies to all reference-counting mechanisms):
- * http://python.ca/nas/python/gc/
- */
+/// Garbage collection for lists and dictionaries.
+///
+/// We use reference counts to be able to free most items right away when they
+/// are no longer used. But for composite items it's possible that it becomes
+/// unused while the reference count is > 0: When there is a recursive
+/// reference. Example:
+/// :let l = [1, 2, 3]
+/// :let d = {9: l}
+/// :let l[1] = d
+///
+/// Since this is quite unusual we handle this with garbage collection: every
+/// once in a while find out which lists and dicts are not referenced from any
+/// variable.
+///
+/// Here is a good reference text about garbage collection (refers to Python
+/// but it applies to all reference-counting mechanisms):
+/// http://python.ca/nas/python/gc/
/// Do garbage collection for lists and dicts.
///
@@ -4219,6 +4091,23 @@ bool garbage_collect(bool testing)
garbage_collect_at_exit = false;
}
+ // The execution stack can grow big, limit the size.
+ if (exestack.ga_maxlen - exestack.ga_len > 500) {
+ // Keep 150% of the current size, with a minimum of the growth size.
+ int n = exestack.ga_len / 2;
+ if (n < exestack.ga_growsize) {
+ n = exestack.ga_growsize;
+ }
+
+ // Don't make it bigger though.
+ if (exestack.ga_len + n < exestack.ga_maxlen) {
+ size_t new_len = (size_t)exestack.ga_itemsize * (size_t)(exestack.ga_len + n);
+ char *pp = xrealloc(exestack.ga_data, new_len);
+ exestack.ga_maxlen = exestack.ga_len + n;
+ exestack.ga_data = pp;
+ }
+ }
+
// We advance by two (COPYID_INC) because we add one for items referenced
// through previous_funccal.
const int copyID = get_copyID();
@@ -4232,7 +4121,7 @@ bool garbage_collect(bool testing)
ABORTING(set_ref_in_previous_funccal)(copyID);
// script-local variables
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
ABORTING(set_ref_in_ht)(&SCRIPT_VARS(i), copyID, NULL);
}
@@ -4691,21 +4580,16 @@ static int get_literal_key(char **arg, typval_T *tv)
/// @return OK or FAIL. Returns NOTDONE for {expr}.
static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
{
- dict_T *d = NULL;
- typval_T tvkey;
typval_T tv;
char *key = NULL;
- dictitem_T *item;
char *start = skipwhite(*arg + 1);
char buf[NUMBUFLEN];
- /*
- * First check if it's not a curly-braces thing: {expr}.
- * Must do this without evaluating, otherwise a function may be called
- * twice. Unfortunately this means we need to call eval1() twice for the
- * first item.
- * But {} is an empty Dictionary.
- */
+ // First check if it's not a curly-braces thing: {expr}.
+ // Must do this without evaluating, otherwise a function may be called
+ // twice. Unfortunately this means we need to call eval1() twice for the
+ // first item.
+ // But {} is an empty Dictionary.
if (*start != '}') {
if (eval1(&start, &tv, false) == FAIL) { // recursive!
return FAIL;
@@ -4715,9 +4599,11 @@ static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
}
}
+ dict_T *d = NULL;
if (evaluate) {
d = tv_dict_alloc();
}
+ typval_T tvkey;
tvkey.v_type = VAR_UNKNOWN;
tv.v_type = VAR_UNKNOWN;
@@ -4750,7 +4636,7 @@ static int dict_get_tv(char **arg, typval_T *rettv, int evaluate, bool literal)
goto failret;
}
if (evaluate) {
- item = tv_dict_find(d, (const char *)key, -1);
+ dictitem_T *item = tv_dict_find(d, (const char *)key, -1);
if (item != NULL) {
semsg(_("E721: Duplicate key in Dictionary: \"%s\""), key);
tv_clear(&tvkey);
@@ -4805,8 +4691,6 @@ failret:
size_t string2float(const char *const text, float_T *const ret_value)
FUNC_ATTR_NONNULL_ALL
{
- char *s = NULL;
-
// MS-Windows does not deal with "inf" and "nan" properly
if (STRNICMP(text, "inf", 3) == 0) {
*ret_value = (float_T)INFINITY;
@@ -4820,6 +4704,7 @@ size_t string2float(const char *const text, float_T *const ret_value)
*ret_value = (float_T)NAN;
return 3;
}
+ char *s = NULL;
*ret_value = strtod(text, &s);
return (size_t)(s - text);
}
@@ -4833,23 +4718,18 @@ size_t string2float(const char *const text, float_T *const ret_value)
/// @return FAIL if the name is invalid.
static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
{
- char *name;
- char *string = NULL;
- int len;
- int cc;
-
- ++*arg;
- name = *arg;
- len = get_env_len((const char **)arg);
+ (*arg)++;
+ char *name = *arg;
+ int len = get_env_len((const char **)arg);
if (evaluate) {
if (len == 0) {
return FAIL; // Invalid empty name.
}
- cc = (char_u)name[len];
+ int cc = (int)name[len];
name[len] = NUL;
// First try vim_getenv(), fast for normal environment vars.
- string = vim_getenv(name);
+ char *string = vim_getenv(name);
if (string == NULL || *string == NUL) {
xfree(string);
@@ -4867,18 +4747,6 @@ static int get_env_tv(char **arg, typval_T *rettv, int evaluate)
return OK;
}
-/// Get the argument list for a given window
-void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
-{
- tv_list_alloc_ret(rettv, argcount);
- if (arglist != NULL) {
- for (int idx = 0; idx < argcount; idx++) {
- tv_list_append_string(rettv->vval.v_list,
- (const char *)alist_name(&arglist[idx]), -1);
- }
- }
-}
-
/// Add an assert error to v:errors.
void assert_error(garray_T *gap)
{
@@ -4908,17 +4776,10 @@ win_T *find_win_by_nr_or_id(typval_T *vp)
/// Implementation of map() and filter().
void filter_map(typval_T *argvars, typval_T *rettv, int map)
{
- typval_T *expr;
list_T *l = NULL;
- dictitem_T *di;
- hashtab_T *ht;
- hashitem_T *hi;
dict_T *d = NULL;
- typval_T save_val;
- typval_T save_key;
blob_T *b = NULL;
int rem = false;
- int todo;
char *ermsg = map ? "map()" : "filter()";
const char *const arg_errmsg = (map
? N_("map() argument")
@@ -4949,18 +4810,20 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
return;
}
- expr = &argvars[1];
+ typval_T *expr = &argvars[1];
// On type errors, the preceding call has already displayed an error
// message. Avoid a misleading error message for an empty string that
// was not passed as argument.
if (expr->v_type != VAR_UNKNOWN) {
+ typval_T save_val;
prepare_vimvar(VV_VAL, &save_val);
// We reset "did_emsg" to be able to detect whether an error
// occurred during evaluation of the expression.
save_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
+ typval_T save_key;
prepare_vimvar(VV_KEY, &save_key);
if (argvars[0].v_type == VAR_DICT) {
vimvars[VV_KEY].vv_type = VAR_STRING;
@@ -4969,14 +4832,14 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (map && d->dv_lock == VAR_UNLOCKED) {
d->dv_lock = VAR_LOCKED;
}
- ht = &d->dv_hashtab;
+ hashtab_T *ht = &d->dv_hashtab;
hash_lock(ht);
- todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ int todo = (int)ht->ht_used;
+ for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
- di = TV_DICT_HI2DI(hi);
+ dictitem_T *di = TV_DICT_HI2DI(hi);
if (map
&& (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
|| var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
@@ -5100,7 +4963,7 @@ theend:
return retval;
}
-void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr fptr)
+void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref)
{
char *s;
char *name;
@@ -5125,7 +4988,7 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr
if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) {
name = s;
- trans_name = (char *)trans_function_name((char_u **)&name, false,
+ trans_name = (char *)trans_function_name(&name, false,
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
| TFN_NO_DEREF, NULL, NULL);
if (*name != NUL) {
@@ -5329,29 +5192,6 @@ linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf)
return (linenr_T)tv_get_number_chk(tv, NULL);
}
-void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
-{
- if (what_arg->v_type == VAR_UNKNOWN) {
- tv_list_alloc_ret(rettv, kListLenMayKnow);
- if (is_qf || wp != NULL) {
- (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
- }
- } else {
- tv_dict_alloc_ret(rettv);
- if (is_qf || wp != NULL) {
- if (what_arg->v_type == VAR_DICT) {
- dict_T *d = what_arg->vval.v_dict;
-
- if (d != NULL) {
- qf_get_properties(wp, d, rettv->vval.v_dict);
- }
- } else {
- emsg(_(e_dictreq));
- }
- }
- }
-}
-
/// @return information (variables, options, etc.) about a tab page
/// as a dictionary.
dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
@@ -5383,12 +5223,12 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
tv_dict_add_nr(dict, S_LEN("tabnr"), tpnr);
tv_dict_add_nr(dict, S_LEN("winnr"), winnr);
tv_dict_add_nr(dict, S_LEN("winid"), wp->handle);
- tv_dict_add_nr(dict, S_LEN("height"), wp->w_height);
+ tv_dict_add_nr(dict, S_LEN("height"), wp->w_height_inner);
tv_dict_add_nr(dict, S_LEN("winrow"), wp->w_winrow + 1);
tv_dict_add_nr(dict, S_LEN("topline"), wp->w_topline);
tv_dict_add_nr(dict, S_LEN("botline"), wp->w_botline - 1);
tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height);
- tv_dict_add_nr(dict, S_LEN("width"), wp->w_width);
+ tv_dict_add_nr(dict, S_LEN("width"), wp->w_width_inner);
tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum);
tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1);
tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp));
@@ -5688,11 +5528,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
FUNC_ATTR_NONNULL_ARG(4, 5)
{
linenr_T lnum = lnum_arg + (append ? 1 : 0);
- const char *line = NULL;
- list_T *l = NULL;
- listitem_T *li = NULL;
long added = 0;
- linenr_T append_lnum;
buf_T *curbuf_save = NULL;
win_T *curwin_save = NULL;
const bool is_curbuf = buf == curbuf;
@@ -5714,6 +5550,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
find_win_for_curbuf();
}
+ linenr_T append_lnum;
if (append) {
// appendbufline() uses the line number below which we insert
append_lnum = lnum - 1;
@@ -5723,6 +5560,9 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
append_lnum = curbuf->b_ml.ml_line_count;
}
+ list_T *l = NULL;
+ listitem_T *li = NULL;
+ const char *line = NULL;
if (lines->v_type == VAR_LIST) {
l = lines->vval.v_list;
li = tv_list_first(l);
@@ -5806,7 +5646,6 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T
void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
FUNC_ATTR_NONNULL_ALL
{
- const void *iter = NULL;
list_T *const list = tv_list_alloc(kListLenShouldKnow);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = list;
@@ -5815,6 +5654,7 @@ void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv)
if (dirs == NULL) {
return;
}
+ const void *iter = NULL;
do {
size_t dir_len;
const char *dir;
@@ -5924,9 +5764,9 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist
#ifdef USE_CRNL
// translate <CR><NL> into <NL>
char *d = res;
- for (char *s = res; *s; ++s) {
+ for (char *s = res; *s; s++) {
if (s[0] == CAR && s[1] == NL) {
- ++s;
+ s++;
}
*d++ = *s;
@@ -5999,7 +5839,17 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co
switch (callback->type) {
case kCallbackFuncref:
name = callback->data.funcref;
- partial = NULL;
+ int len = (int)STRLEN(name);
+ if (len >= 6 && !memcmp(name, "v:lua.", 6)) {
+ name += 6;
+ len = check_luafunc_name(name, false);
+ if (len == 0) {
+ return false;
+ }
+ partial = vvlua_partial;
+ } else {
+ partial = NULL;
+ }
break;
case kCallbackPartial:
@@ -6135,7 +5985,7 @@ void timer_due_cb(TimeWatcher *tw, void *data)
// Handle error message
if (called_emsg > called_emsg_before && did_emsg) {
timer->emsg_count++;
- if (current_exception != NULL) {
+ if (did_throw) {
discard_current_exception();
}
}
@@ -6498,12 +6348,9 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
// Argument can be [lnum, col, coladd].
if (tv->v_type == VAR_LIST) {
- list_T *l;
- int len;
bool error = false;
- listitem_T *li;
- l = tv->vval.v_list;
+ list_T *l = tv->vval.v_list;
if (l == NULL) {
return NULL;
}
@@ -6520,6 +6367,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
if (error) {
return NULL;
}
+ int len;
if (charcol) {
len = mb_charlen(ml_get(pos.lnum));
} else {
@@ -6527,7 +6375,7 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
}
// We accept "$" for the column number: last column.
- li = tv_list_find(l, 1L);
+ listitem_T *li = tv_list_find(l, 1L);
if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING
&& TV_LIST_ITEM_TV(li)->vval.v_string != NULL
&& STRCMP(TV_LIST_ITEM_TV(li)->vval.v_string, "$") == 0) {
@@ -6627,8 +6475,6 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret
int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol)
{
list_T *l;
- int i = 0;
- long n;
// List must be: [fnum, lnum, col, coladd, curswant], where "fnum" is only
// there when "fnump" isn't NULL; "coladd" and "curswant" are optional.
@@ -6639,6 +6485,8 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
return FAIL;
}
+ int i = 0;
+ long n;
if (fnump != NULL) {
n = tv_list_find_nr(l, i++, NULL); // fnum
if (n < 0) {
@@ -6691,15 +6539,13 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool c
/// @return 0 for error.
int get_env_len(const char **arg)
{
- int len;
-
const char *p;
for (p = *arg; vim_isIDc(*p); p++) {}
if (p == *arg) { // No name found.
return 0;
}
- len = (int)(p - *arg);
+ int len = (int)(p - *arg);
*arg = p;
return len;
}
@@ -6747,8 +6593,6 @@ int get_id_len(const char **const arg)
/// 0 if something else is wrong.
int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose)
{
- int len;
-
*alias = NULL; // default to no alias
if ((*arg)[0] == (char)K_SPECIAL && (*arg)[1] == (char)KS_EXTRA
@@ -6757,7 +6601,7 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
*arg += 3;
return get_id_len(arg) + 3;
}
- len = eval_fname_script(*arg);
+ int len = eval_fname_script(*arg);
if (len > 0) {
// literal "<SID>", "s:" or "<SNR>"
*arg += len;
@@ -6775,10 +6619,8 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
return len;
}
- /*
- * Include any <SID> etc in the expanded string:
- * Thus the -len here.
- */
+ // Include any <SID> etc in the expanded string:
+ // Thus the -len here.
char *temp_string = make_expanded_name(*arg - len, expr_start, expr_end, (char *)p);
if (temp_string == NULL) {
return -1;
@@ -6810,10 +6652,6 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo
const char *find_name_end(const char *arg, const char **expr_start, const char **expr_end,
int flags)
{
- int mb_nest = 0;
- int br_nest = 0;
- int len;
-
if (expr_start != NULL) {
*expr_start = NULL;
*expr_end = NULL;
@@ -6824,6 +6662,10 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
return arg;
}
+ int mb_nest = 0;
+ int br_nest = 0;
+ int len;
+
const char *p;
for (p = arg; *p != NUL
&& (eval_isnamec(*p)
@@ -6841,7 +6683,7 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
// skip over "str\"ing" to avoid counting [ and ] inside it.
for (p = p + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
- ++p;
+ p++;
}
}
if (*p == NUL) {
@@ -6859,9 +6701,9 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
if (mb_nest == 0) {
if (*p == '[') {
- ++br_nest;
+ br_nest++;
} else if (*p == ']') {
- --br_nest;
+ br_nest--;
}
}
@@ -6897,20 +6739,19 @@ const char *find_name_end(const char *arg, const char **expr_start, const char *
static char *make_expanded_name(const char *in_start, char *expr_start, char *expr_end,
char *in_end)
{
- char c1;
- char *retval = NULL;
- char *temp_result;
- char *nextcmd = NULL;
-
if (expr_end == NULL || in_end == NULL) {
return NULL;
}
+
+ char *retval = NULL;
+ char *nextcmd = NULL;
+
*expr_start = NUL;
*expr_end = NUL;
- c1 = *in_end;
+ char c1 = *in_end;
*in_end = NUL;
- temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
+ char *temp_result = eval_to_string(expr_start + 1, &nextcmd, false);
if (temp_result != NULL && nextcmd == NULL) {
retval = xmalloc(STRLEN(temp_result) + (size_t)(expr_start - in_start)
+ (size_t)(in_end - expr_end) + 1);
@@ -6940,14 +6781,14 @@ static char *make_expanded_name(const char *in_start, char *expr_start, char *ex
return retval;
}
-/// @return TRUE if character "c" can be used in a variable or function name.
+/// @return true if character "c" can be used in a variable or function name.
/// Does not include '{' or '}' for magic braces.
int eval_isnamec(int c)
{
return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR;
}
-/// @return TRUE if character "c" can be used as the first character in a
+/// @return true if character "c" can be used as the first character in a
/// variable or function name (excluding '{' and '}').
int eval_isnamec1(int c)
{
@@ -7000,7 +6841,7 @@ void set_vim_var_char(int c)
/// Set v:count to "count" and v:count1 to "count1".
///
-/// @param set_prevcount if TRUE, first set v:prevcount from v:count.
+/// @param set_prevcount if true, first set v:prevcount from v:count.
void set_vcount(long count, long count1, int set_prevcount)
{
if (set_prevcount) {
@@ -7353,7 +7194,7 @@ int handle_subscript(const char **const arg, typval_T *rettv, int evaluate, int
if (rettv->v_type == VAR_DICT) {
selfdict = rettv->vval.v_dict;
if (selfdict != NULL) {
- ++selfdict->dv_refcount;
+ selfdict->dv_refcount++;
}
} else {
selfdict = NULL;
@@ -7430,8 +7271,6 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
const size_t varname_len, int no_autoload)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- hashitem_T *hi;
-
if (varname_len == 0) {
// Must be something like "s:", otherwise "ht" would be NULL.
switch (htname) {
@@ -7455,7 +7294,7 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
return NULL;
}
- hi = hash_find_len(ht, varname, varname_len);
+ hashitem_T *hi = hash_find_len(ht, varname, varname_len);
if (HASHITEM_EMPTY(hi)) {
// For global variables we may try auto-loading the script. If it
// worked find the variable again. Don't auto-load a script if it was
@@ -7490,7 +7329,6 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va
hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char **varname,
dict_T **d)
{
- hashitem_T *hi;
funccall_T *funccal = get_funccal();
*d = NULL;
@@ -7506,7 +7344,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
*varname = name;
// "version" is "v:version" in all scopes
- hi = hash_find_len(&compat_hashtab, name, name_len);
+ hashitem_T *hi = hash_find_len(&compat_hashtab, name, name_len);
if (!HASHITEM_EMPTY(hi)) {
return &compat_hashtab;
}
@@ -7562,7 +7400,7 @@ hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, const char
bool should_free;
// should_free is ignored as script_sctx will be resolved to a fnmae
// & new_script_item will consume it.
- char *sc_name = (char *)get_scriptname(last_set, &should_free);
+ char *sc_name = get_scriptname(last_set, &should_free);
new_script_item(sc_name, &current_sctx.sc_sid);
}
}
@@ -7595,28 +7433,26 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var
/// sourcing this script and when executing functions defined in the script.
void new_script_vars(scid_T id)
{
- hashtab_T *ht;
scriptvar_T *sv;
ga_grow(&ga_scripts, id - ga_scripts.ga_len);
- {
- /* Re-allocating ga_data means that an ht_array pointing to
- * ht_smallarray becomes invalid. We can recognize this: ht_mask is
- * at its init value. Also reset "v_dict", it's always the same. */
- for (int i = 1; i <= ga_scripts.ga_len; ++i) {
- ht = &SCRIPT_VARS(i);
- if (ht->ht_mask == HT_INIT_SIZE - 1) {
- ht->ht_array = ht->ht_smallarray;
- }
- sv = SCRIPT_SV(i);
- sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
- }
- while (ga_scripts.ga_len < id) {
- sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T));
- init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
- ++ga_scripts.ga_len;
+ // Re-allocating ga_data means that an ht_array pointing to
+ // ht_smallarray becomes invalid. We can recognize this: ht_mask is
+ // at its init value. Also reset "v_dict", it's always the same.
+ for (int i = 1; i <= ga_scripts.ga_len; i++) {
+ hashtab_T *ht = &SCRIPT_VARS(i);
+ if (ht->ht_mask == HT_INIT_SIZE - 1) {
+ ht->ht_array = ht->ht_smallarray;
}
+ sv = SCRIPT_SV(i);
+ sv->sv_var.di_tv.vval.v_dict = &sv->sv_dict;
+ }
+
+ while (ga_scripts.ga_len < id) {
+ sv = SCRIPT_SV(ga_scripts.ga_len + 1) = xcalloc(1, sizeof(scriptvar_T));
+ init_var_dict(&sv->sv_dict, &sv->sv_var, VAR_SCOPE);
+ ga_scripts.ga_len++;
}
}
@@ -7640,8 +7476,8 @@ void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, ScopeType scope)
/// Unreference a dictionary initialized by init_var_dict().
void unref_var_dict(dict_T *dict)
{
- /* Now the dict needs to be freed if no one else is using it, go back to
- * normal reference counting. */
+ // Now the dict needs to be freed if no one else is using it, go back to
+ // normal reference counting.
dict->dv_refcount -= DO_NOT_FREE_CNT - 1;
tv_dict_unref(dict);
}
@@ -7673,7 +7509,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
emsg(_("E698: variable nested too deep for making a copy"));
return FAIL;
}
- ++recurse;
+ recurse++;
switch (from->v_type) {
case VAR_NUMBER:
@@ -7691,9 +7527,9 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
} else {
to->v_type = VAR_STRING;
to->v_lock = VAR_UNLOCKED;
- if ((to->vval.v_string = (char *)string_convert((vimconv_T *)conv,
- (char_u *)from->vval.v_string,
- NULL))
+ if ((to->vval.v_string = string_convert((vimconv_T *)conv,
+ from->vval.v_string,
+ NULL))
== NULL) {
to->vval.v_string = xstrdup(from->vval.v_string);
}
@@ -7726,7 +7562,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
} else if (copyID != 0 && from->vval.v_dict->dv_copyID == copyID) {
// use the copy made earlier
to->vval.v_dict = from->vval.v_dict->dv_copydict;
- ++to->vval.v_dict->dv_refcount;
+ to->vval.v_dict->dv_refcount++;
} else {
to->vval.v_dict = tv_dict_copy(conv, from->vval.v_dict, deep, copyID);
}
@@ -7738,7 +7574,7 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c
internal_error("var_item_copy(UNKNOWN)");
ret = FAIL;
}
- --recurse;
+ recurse--;
return ret;
}
@@ -7755,7 +7591,7 @@ void ex_echo(exarg_T *eap)
const int called_emsg_before = called_emsg;
if (eap->skip) {
- ++emsg_skip;
+ emsg_skip++;
}
while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) {
// If eval1() causes an error message the text from the command may
@@ -7835,12 +7671,11 @@ void ex_execute(exarg_T *eap)
typval_T rettv;
int ret = OK;
garray_T ga;
- int save_did_emsg;
ga_init(&ga, 1, 80);
if (eap->skip) {
- ++emsg_skip;
+ emsg_skip++;
}
while (*arg != NUL && *arg != '|' && *arg != '\n') {
ret = eval1_emsg(&arg, &rettv, !eap->skip);
@@ -7884,7 +7719,7 @@ void ex_execute(exarg_T *eap)
ui_flush();
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
- save_did_emsg = did_emsg;
+ int save_did_emsg = did_emsg;
msg_ext_set_kind("echoerr");
emsg(ga.ga_data);
if (!force_abort) {
@@ -7898,7 +7733,7 @@ void ex_execute(exarg_T *eap)
ga_clear(&ga);
if (eap->skip) {
- --emsg_skip;
+ emsg_skip--;
}
eap->nextcmd = (char *)check_nextcmd((char_u *)arg);
@@ -7914,7 +7749,7 @@ const char *find_option_end(const char **const arg, int *const opt_flags)
{
const char *p = *arg;
- ++p;
+ p++;
if (*p == 'g' && p[1] == ':') {
*opt_flags = OPT_GLOBAL;
p += 2;
@@ -7940,174 +7775,6 @@ const char *find_option_end(const char **const arg, int *const opt_flags)
return p;
}
-/// Start profiling function "fp".
-void func_do_profile(ufunc_T *fp)
-{
- int len = fp->uf_lines.ga_len;
-
- if (!fp->uf_prof_initialized) {
- if (len == 0) {
- len = 1; // avoid getting error for allocating zero bytes
- }
- fp->uf_tm_count = 0;
- fp->uf_tm_self = profile_zero();
- fp->uf_tm_total = profile_zero();
-
- if (fp->uf_tml_count == NULL) {
- fp->uf_tml_count = xcalloc((size_t)len, sizeof(int));
- }
-
- if (fp->uf_tml_total == NULL) {
- fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T));
- }
-
- if (fp->uf_tml_self == NULL) {
- fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T));
- }
-
- fp->uf_tml_idx = -1;
- fp->uf_prof_initialized = true;
- }
-
- fp->uf_profiling = TRUE;
-}
-
-/// Dump the profiling results for all functions in file "fd".
-void func_dump_profile(FILE *fd)
-{
- hashitem_T *hi;
- int todo;
- ufunc_T *fp;
- ufunc_T **sorttab;
- int st_len = 0;
-
- todo = (int)func_hashtab.ht_used;
- if (todo == 0) {
- return; // nothing to dump
- }
-
- sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo);
-
- for (hi = func_hashtab.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- --todo;
- fp = HI2UF(hi);
- if (fp->uf_prof_initialized) {
- sorttab[st_len++] = fp;
-
- if (fp->uf_name[0] == K_SPECIAL) {
- fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3);
- } else {
- fprintf(fd, "FUNCTION %s()\n", fp->uf_name);
- }
- if (fp->uf_script_ctx.sc_sid != 0) {
- bool should_free;
- const LastSet last_set = (LastSet){
- .script_ctx = fp->uf_script_ctx,
- .channel_id = 0,
- };
- char *p = (char *)get_scriptname(last_set, &should_free);
- fprintf(fd, " Defined: %s:%" PRIdLINENR "\n",
- p, fp->uf_script_ctx.sc_lnum);
- if (should_free) {
- xfree(p);
- }
- }
- if (fp->uf_tm_count == 1) {
- fprintf(fd, "Called 1 time\n");
- } else {
- fprintf(fd, "Called %d times\n", fp->uf_tm_count);
- }
- fprintf(fd, "Total time: %s\n", profile_msg(fp->uf_tm_total));
- fprintf(fd, " Self time: %s\n", profile_msg(fp->uf_tm_self));
- fprintf(fd, "\n");
- fprintf(fd, "count total (s) self (s)\n");
-
- for (int i = 0; i < fp->uf_lines.ga_len; ++i) {
- if (FUNCLINE(fp, i) == NULL) {
- continue;
- }
- prof_func_line(fd, fp->uf_tml_count[i],
- &fp->uf_tml_total[i], &fp->uf_tml_self[i], TRUE);
- fprintf(fd, "%s\n", FUNCLINE(fp, i));
- }
- fprintf(fd, "\n");
- }
- }
- }
-
- if (st_len > 0) {
- qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
- prof_total_cmp);
- prof_sort_list(fd, sorttab, st_len, "TOTAL", FALSE);
- qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
- prof_self_cmp);
- prof_sort_list(fd, sorttab, st_len, "SELF", TRUE);
- }
-
- xfree(sorttab);
-}
-
-/// @param prefer_self when equal print only self time
-static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, int prefer_self)
-{
- int i;
- ufunc_T *fp;
-
- fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
- fprintf(fd, "count total (s) self (s) function\n");
- for (i = 0; i < 20 && i < st_len; ++i) {
- fp = sorttab[i];
- prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
- prefer_self);
- if (fp->uf_name[0] == K_SPECIAL) {
- fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
- } else {
- fprintf(fd, " %s()\n", fp->uf_name);
- }
- }
- fprintf(fd, "\n");
-}
-
-/// Print the count and times for one function or function line.
-///
-/// @param prefer_self when equal print only self time
-static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self,
- int prefer_self)
-{
- if (count > 0) {
- fprintf(fd, "%5d ", count);
- if (prefer_self && profile_equal(*total, *self)) {
- fprintf(fd, " ");
- } else {
- fprintf(fd, "%s ", profile_msg(*total));
- }
- if (!prefer_self && profile_equal(*total, *self)) {
- fprintf(fd, " ");
- } else {
- fprintf(fd, "%s ", profile_msg(*self));
- }
- } else {
- fprintf(fd, " ");
- }
-}
-
-/// Compare function for total time sorting.
-static int prof_total_cmp(const void *s1, const void *s2)
-{
- ufunc_T *p1 = *(ufunc_T **)s1;
- ufunc_T *p2 = *(ufunc_T **)s2;
- return profile_cmp(p1->uf_tm_total, p2->uf_tm_total);
-}
-
-/// Compare function for self time sorting.
-static int prof_self_cmp(const void *s1, const void *s2)
-{
- ufunc_T *p1 = *(ufunc_T **)s1;
- ufunc_T *p2 = *(ufunc_T **)s2;
- return profile_cmp(p1->uf_tm_self, p2->uf_tm_self);
-}
-
/// Return the autoload script name for a function or variable name
/// Caller must make sure that "name" contains AUTOLOAD_CHAR.
///
@@ -8182,61 +7849,6 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r
return ret;
}
-/// Called when starting to read a function line.
-/// "sourcing_lnum" must be correct!
-/// When skipping lines it may not actually be executed, but we won't find out
-/// until later and we need to store the time now.
-void func_line_start(void *cookie)
-{
- funccall_T *fcp = (funccall_T *)cookie;
- ufunc_T *fp = fcp->func;
-
- if (fp->uf_profiling && sourcing_lnum >= 1
- && sourcing_lnum <= fp->uf_lines.ga_len) {
- fp->uf_tml_idx = sourcing_lnum - 1;
- // Skip continuation lines.
- while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) {
- fp->uf_tml_idx--;
- }
- fp->uf_tml_execed = false;
- fp->uf_tml_start = profile_start();
- fp->uf_tml_children = profile_zero();
- fp->uf_tml_wait = profile_get_wait();
- }
-}
-
-/// Called when actually executing a function line.
-void func_line_exec(void *cookie)
-{
- funccall_T *fcp = (funccall_T *)cookie;
- ufunc_T *fp = fcp->func;
-
- if (fp->uf_profiling && fp->uf_tml_idx >= 0) {
- fp->uf_tml_execed = TRUE;
- }
-}
-
-/// Called when done with a function line.
-void func_line_end(void *cookie)
-{
- funccall_T *fcp = (funccall_T *)cookie;
- ufunc_T *fp = fcp->func;
-
- if (fp->uf_profiling && fp->uf_tml_idx >= 0) {
- if (fp->uf_tml_execed) {
- ++fp->uf_tml_count[fp->uf_tml_idx];
- fp->uf_tml_start = profile_end(fp->uf_tml_start);
- fp->uf_tml_start = profile_sub_wait(fp->uf_tml_wait, fp->uf_tml_start);
- fp->uf_tml_total[fp->uf_tml_idx] =
- profile_add(fp->uf_tml_total[fp->uf_tml_idx], fp->uf_tml_start);
- fp->uf_tml_self[fp->uf_tml_idx] =
- profile_self(fp->uf_tml_self[fp->uf_tml_idx], fp->uf_tml_start,
- fp->uf_tml_children);
- }
- fp->uf_tml_idx = -1;
- }
-}
-
static var_flavour_T var_flavour(char *varname)
FUNC_ATTR_PURE
{
@@ -8324,11 +7936,9 @@ int store_session_globals(FILE *fd)
}
if ((fprintf(fd, "let %s = %c%s%c",
this_var->di_key,
- ((this_var->di_tv.v_type == VAR_STRING) ? '"'
- : ' '),
+ ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' '),
p,
- ((this_var->di_tv.v_type == VAR_STRING) ? '"'
- : ' ')) < 0)
+ ((this_var->di_tv.v_type == VAR_STRING) ? '"' : ' ')) < 0)
|| put_eol(fd) == FAIL) {
xfree(p);
return FAIL;
@@ -8370,7 +7980,7 @@ void option_last_set_msg(LastSet last_set)
{
if (last_set.script_ctx.sc_sid != 0) {
bool should_free;
- char *p = (char *)get_scriptname(last_set, &should_free);
+ char *p = get_scriptname(last_set, &should_free);
verbose_enter();
msg_puts(_("\n\tLast set from "));
msg_puts(p);
@@ -8413,10 +8023,8 @@ int modify_fname(char *src, bool tilde_file, size_t *usedlen, char **fnamep, cha
size_t *fnamelen)
{
int valid = 0;
- char *tail;
char *s, *p, *pbuf;
char dirname[MAXPATHL];
- int c;
bool has_fullname = false;
bool has_homerelative = false;
@@ -8469,7 +8077,7 @@ repeat:
}
// Append a path separator to a directory.
- if (os_isdir((char_u *)(*fnamep))) {
+ if (os_isdir(*fnamep)) {
// Make room for one or two extra characters.
*fnamep = xstrnsave(*fnamep, STRLEN(*fnamep) + 2);
xfree(*bufp); // free any allocated file name
@@ -8478,6 +8086,8 @@ repeat:
}
}
+ int c;
+
// ":." - path relative to the current directory
// ":~" - path relative to the home directory
// ":8" - shortname path - postponed till after
@@ -8533,6 +8143,7 @@ repeat:
// Only replace it when it starts with '~'
if (*dirname == '~') {
s = xstrdup(dirname);
+ assert(s != NULL); // suppress clang "Argument with 'nonnull' attribute passed null"
*fnamep = s;
xfree(*bufp);
*bufp = s;
@@ -8543,7 +8154,7 @@ repeat:
}
}
- tail = path_tail(*fnamep);
+ char *tail = path_tail(*fnamep);
*fnamelen = STRLEN(*fnamep);
// ":h" - head, remove "/file_name", can be repeated
@@ -8551,7 +8162,7 @@ repeat:
while (src[*usedlen] == ':' && src[*usedlen + 1] == 'h') {
valid |= VALID_HEAD;
*usedlen += 2;
- s = (char *)get_past_head((char_u *)(*fnamep));
+ s = get_past_head(*fnamep);
while (tail > s && after_pathsep(s, tail)) {
MB_PTR_BACK(*fnamep, tail);
}
@@ -8584,10 +8195,9 @@ repeat:
// ":r" - root, without extension, can be repeated
while (src[*usedlen] == ':'
&& (src[*usedlen + 1] == 'e' || src[*usedlen + 1] == 'r')) {
- /* find a '.' in the tail:
- * - for second :e: before the current fname
- * - otherwise: The last '.'
- */
+ // find a '.' in the tail:
+ // - for second :e: before the current fname
+ // - otherwise: The last '.'
const bool is_second_e = *fnamep > tail;
if (src[*usedlen + 1] == 'e' && is_second_e) {
s = (*fnamep) - 2;
@@ -8638,18 +8248,16 @@ repeat:
if (src[*usedlen] == ':'
&& (src[*usedlen + 1] == 's'
|| (src[*usedlen + 1] == 'g' && src[*usedlen + 2] == 's'))) {
- int sep;
- char *flags;
- int didit = false;
+ bool didit = false;
- flags = "";
+ char *flags = "";
s = src + *usedlen + 2;
if (src[*usedlen + 1] == 'g') {
flags = "g";
s++;
}
- sep = (char_u)(*s++);
+ int sep = (char_u)(*s++);
if (sep) {
// find end of pattern
p = vim_strchr(s, sep);
@@ -8667,7 +8275,7 @@ repeat:
*fnamelen = STRLEN(s);
xfree(*bufp);
*bufp = s;
- didit = TRUE;
+ didit = true;
xfree(sub);
xfree(str);
}
@@ -8708,26 +8316,22 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
{
int sublen;
regmatch_T regmatch;
- int do_all;
- char *tail;
- char *end;
garray_T ga;
- char *save_cpo;
char *zero_width = NULL;
// Make 'cpoptions' empty, so that the 'l' flag doesn't work here
- save_cpo = p_cpo;
- p_cpo = (char *)empty_option;
+ char *save_cpo = p_cpo;
+ p_cpo = empty_option;
ga_init(&ga, 1, 200);
- do_all = (flags[0] == 'g');
+ int do_all = (flags[0] == 'g');
regmatch.rm_ic = p_ic;
regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
- tail = str;
- end = str + STRLEN(str);
+ char *tail = str;
+ char *end = str + STRLEN(str);
while (vim_regexec_nl(&regmatch, (char_u *)str, (colnr_T)(tail - str))) {
// Skip empty match except for first match.
if (regmatch.startp[0] == regmatch.endp[0]) {
@@ -8777,11 +8381,16 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, char *flags
char *ret = xstrdup(ga.ga_data == NULL ? str : ga.ga_data);
ga_clear(&ga);
- if ((char_u *)p_cpo == empty_option) {
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating {sub} expression or {expr} changed the value.
- free_string_option((char_u *)save_cpo);
+ // If it's still empty it was changed and restored, need to restore in
+ // the complicated way.
+ if (*p_cpo == NUL) {
+ set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ }
+ free_string_option(save_cpo);
}
return ret;
@@ -8868,8 +8477,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo
struct caller_scope saved_provider_caller_scope = provider_caller_scope;
provider_caller_scope = (struct caller_scope) {
.script_ctx = current_sctx,
- .sourcing_name = sourcing_name,
- .sourcing_lnum = sourcing_lnum,
+ .es_entry = ((estack_T *)exestack.ga_data)[exestack.ga_len - 1],
.autocmd_fname = autocmd_fname,
.autocmd_match = autocmd_match,
.autocmd_bufnr = autocmd_bufnr,
@@ -8968,8 +8576,8 @@ bool eval_has_provider(const char *feat)
/// Writes "<sourcing_name>:<sourcing_lnum>" to `buf[bufsize]`.
void eval_fmt_source_name_line(char *buf, size_t bufsize)
{
- if (sourcing_name) {
- snprintf(buf, bufsize, "%s:%" PRIdLINENR, sourcing_name, sourcing_lnum);
+ if (SOURCING_NAME) {
+ snprintf(buf, bufsize, "%s:%" PRIdLINENR, SOURCING_NAME, SOURCING_LNUM);
} else {
snprintf(buf, bufsize, "?");
}
@@ -8988,7 +8596,7 @@ void ex_checkhealth(exarg_T *eap)
if (vimruntime_env == NULL) {
emsg(_("E5009: $VIMRUNTIME is empty or unset"));
} else {
- bool rtp_ok = NULL != strstr((char *)p_rtp, vimruntime_env);
+ bool rtp_ok = NULL != strstr(p_rtp, vimruntime_env);
if (rtp_ok) {
semsg(_("E5009: Invalid $VIMRUNTIME: %s"), vimruntime_env);
} else {
@@ -9011,8 +8619,6 @@ void invoke_prompt_callback(void)
{
typval_T rettv;
typval_T argv[2];
- char *text;
- char *prompt;
linenr_T lnum = curbuf->b_ml.ml_line_count;
// Add a new line for the prompt before invoking the callback, so that
@@ -9024,8 +8630,8 @@ void invoke_prompt_callback(void)
if (curbuf->b_prompt_callback.type == kCallbackNone) {
return;
}
- text = (char *)ml_get(lnum);
- prompt = (char *)prompt_text();
+ char *text = (char *)ml_get(lnum);
+ char *prompt = (char *)prompt_text();
if (STRLEN(text) >= STRLEN(prompt)) {
text += STRLEN(prompt);
}
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 7dbd18737a..b0cb5fd8c1 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -11,15 +11,6 @@
#define COPYID_INC 2
#define COPYID_MASK (~0x1)
-// All user-defined functions are found in this hashtable.
-extern hashtab_T func_hashtab;
-
-// From user function to hashitem and back.
-EXTERN ufunc_T dumuf;
-#define UF2HIKEY(fp) ((fp)->uf_name)
-#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name)))
-#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
-
/*
* Structure returned by get_lval() and used by set_var_lval().
* For a plain name:
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 6d8776d08b..3e89489459 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -23,7 +23,7 @@ end
return {
funcs={
abs={args=1, base=1},
- acos={args=1, base=1, func="float_op_wrapper", data="&acos"}, -- WJMc
+ acos={args=1, base=1, float_func="acos"}, -- WJMc
add={args=2, base=1},
['and']={args=2, base=1},
api_info={},
@@ -33,7 +33,7 @@ return {
argidx={},
arglistid={args={0, 2}},
argv={args={0, 2}},
- asin={args=1, base=1, func="float_op_wrapper", data="&asin"}, -- WJMc
+ asin={args=1, base=1, float_func="asin"}, -- WJMc
assert_beeps={args=1, base=1},
assert_equal={args={2, 3}, base=2},
assert_equalfile={args={2, 3}, base=1},
@@ -47,7 +47,7 @@ return {
assert_notmatch={args={2, 3}, base=2},
assert_report={args=1, base=1},
assert_true={args={1, 2}, base=1},
- atan={args=1, base=1, func="float_op_wrapper", data="&atan"},
+ atan={args=1, base=1, float_func="atan"},
atan2={args=2, base=1},
browse={args=4},
browsedir={args=2},
@@ -67,11 +67,12 @@ return {
byteidx={args=2, base=1},
byteidxcomp={args=2, base=1},
call={args={2, 3}, base=1},
- ceil={args=1, base=1, func="float_op_wrapper", data="&ceil"},
+ ceil={args=1, base=1, float_func="ceil"},
changenr={},
chanclose={args={1, 2}},
chansend={args=2},
char2nr={args={1, 2}, base=1},
+ charclass={args=1, base=1},
charcol={args=1, base=1},
charidx={args={2, 3}, base=1},
chdir={args=1, base=1},
@@ -84,8 +85,8 @@ return {
complete_info={args={0, 1}, base=1},
confirm={args={1, 4}, base=1},
copy={args=1, base=1},
- cos={args=1, base=1, func="float_op_wrapper", data="&cos"},
- cosh={args=1, base=1, func="float_op_wrapper", data="&cosh"},
+ cos={args=1, base=1, float_func="cos"},
+ cosh={args=1, base=1, float_func="cosh"},
count={args={2, 4}, base=1},
cscope_connection={args={0, 3}},
ctxget={args={0, 1}},
@@ -116,7 +117,7 @@ return {
execute={args={1, 2}, base=1},
exepath={args=1, base=1},
exists={args=1, base=1},
- exp={args=1, base=1, func="float_op_wrapper", data="&exp"},
+ exp={args=1, base=1, float_func="exp"},
expand={args={1, 3}, base=1},
expandcmd={args=1, base=1},
extend={args={2, 3}, base=1},
@@ -129,7 +130,7 @@ return {
findfile={args={1, 3}, base=1},
flatten={args={1, 2}, base=1},
float2nr={args=1, base=1},
- floor={args=1, base=1, func="float_op_wrapper", data="&floor"},
+ floor={args=1, base=1, float_func="floor"},
fmod={args=2, base=1},
fnameescape={args=1, base=1},
fnamemodify={args=2, base=1},
@@ -244,8 +245,8 @@ return {
lispindent={args=1, base=1},
list2str={args={1, 2}, base=1},
localtime={},
- log={args=1, base=1, func="float_op_wrapper", data="&log"},
- log10={args=1, base=1, func="float_op_wrapper", data="&log10"},
+ log={args=1, base=1, float_func="log"},
+ log10={args=1, base=1, float_func="log10"},
luaeval={args={1, 2}, base=1},
map={args=2, base=1},
maparg={args={1, 4}, base=1},
@@ -303,7 +304,7 @@ return {
['repeat']={args=2, base=1},
resolve={args=1, base=1},
reverse={args=1, base=1},
- round={args=1, base=1, func="float_op_wrapper", data="&round"},
+ round={args=1, base=1, float_func="round"},
rpcnotify={args=varargs(2)},
rpcrequest={args=varargs(2)},
rpcstart={args={1, 2}},
@@ -327,9 +328,11 @@ return {
serverstop={args=1},
setbufline={args=3, base=3},
setbufvar={args=3, base=3},
+ setcellwidths={args=1, base=1},
setcharpos={args=2, base=2},
setcharsearch={args=1, base=1},
setcmdpos={args=1, base=1},
+ setcmdline={args={1, 2}, base=1},
setcursorcharpos={args={1, 3}, base=1},
setenv={args=2, base=2},
setfperm={args=2, base=1},
@@ -356,8 +359,8 @@ return {
sign_unplace={args={1, 2}, base=1},
sign_unplacelist={args=1, base=1},
simplify={args=1, base=1},
- sin={args=1, base=1, func="float_op_wrapper", data="&sin"},
- sinh={args=1, base=1, func="float_op_wrapper", data="&sinh"},
+ sin={args=1, base=1, float_func="sin"},
+ sinh={args=1, base=1, float_func="sinh"},
sockconnect={args={2,3}},
sort={args={1, 3}, base=1},
soundfold={args=1, base=1},
@@ -365,7 +368,7 @@ return {
spellbadword={args={0, 1}, base=1},
spellsuggest={args={1, 3}, base=1},
split={args={1, 3}, base=1},
- sqrt={args=1, base=1, func="float_op_wrapper", data="&sqrt"},
+ sqrt={args=1, base=1, float_func="sqrt"},
srand={args={0, 1}, base=1},
stdpath={args=1},
str2float={args=1, base=1},
@@ -400,8 +403,8 @@ return {
tabpagewinnr={args={1, 2}, base=1},
tagfiles={},
taglist={args={1, 2}, base=1},
- tan={args=1, base=1, func="float_op_wrapper", data="&tan"},
- tanh={args=1, base=1, func="float_op_wrapper", data="&tanh"},
+ tan={args=1, base=1, float_func="tan"},
+ tanh={args=1, base=1, float_func="tanh"},
tempname={},
termopen={args={1, 2}},
test_garbagecollect_now={},
@@ -415,7 +418,7 @@ return {
toupper={args=1, base=1},
tr={args=3, base=1},
trim={args={1, 3}, base=1},
- trunc={args=1, base=1, func="float_op_wrapper", data="&trunc"},
+ trunc={args=1, base=1, float_func="trunc"},
type={args=1, base=1},
undofile={args=1, base=1},
undotree={},
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 090939666d..bb514fba47 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -30,12 +30,12 @@
#include "nvim/vim.h" // For _()
const char *const encode_bool_var_names[] = {
- [kBoolVarTrue] = "true",
- [kBoolVarFalse] = "false",
+ [kBoolVarTrue] = "v:true",
+ [kBoolVarFalse] = "v:false",
};
const char *const encode_special_var_names[] = {
- [kSpecialVarNull] = "null",
+ [kSpecialVarNull] = "v:null",
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index b461456a3a..0e0d0fe696 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -108,8 +108,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *cons
const char *tvs = tv_get_string(tv1);
char numbuf[NUMBUFLEN];
char *const s =
- (char *)concat_str((const char_u *)tvs, (const char_u *)tv_get_string_buf(tv2,
- numbuf));
+ concat_str(tvs, tv_get_string_buf(tv2, numbuf));
tv_clear(tv1);
tv1->v_type = VAR_STRING;
tv1->vval.v_string = s;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 060dc50f52..aa328d50ef 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -7,12 +7,15 @@
#include "nvim/api/private/converter.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdhist.h"
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
@@ -26,11 +29,14 @@
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
@@ -45,25 +51,29 @@
#include "nvim/match.h"
#include "nvim/math.h"
#include "nvim/memline.h"
+#include "nvim/menu.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/dl.h"
-#include "nvim/os/input.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
+#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sha256.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
+#include "nvim/spellsuggest.h"
#include "nvim/state.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
@@ -117,13 +127,12 @@ static va_list dummy_ap;
char *get_function_name(expand_T *xp, int idx)
{
static int intidx = -1;
- char_u *name;
if (idx == 0) {
intidx = -1;
}
if (intidx < 0) {
- name = (char_u *)get_user_func_name(xp, idx);
+ char_u *name = (char_u *)get_user_func_name(xp, idx);
if (name != NULL) {
if (*name != NUL && *name != '<'
&& STRNCMP("g:", xp->xp_pattern, 2) == 0) {
@@ -154,13 +163,12 @@ char *get_function_name(expand_T *xp, int idx)
char *get_expr_name(expand_T *xp, int idx)
{
static int intidx = -1;
- char_u *name;
if (idx == 0) {
intidx = -1;
}
if (intidx < 0) {
- name = (char_u *)get_function_name(xp, idx);
+ char_u *name = (char_u *)get_function_name(xp, idx);
if (name != NULL) {
return (char *)name;
}
@@ -226,7 +234,7 @@ int call_internal_method(const char_u *const fname, const int argcount, typval_T
return ERROR_NONE;
}
-/// @return TRUE for a non-zero Number and a non-empty String.
+/// @return true for a non-zero Number and a non-empty String.
static int non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
@@ -243,26 +251,25 @@ static int non_zero_arg(typval_T *argvars)
/// Some versions of glibc on i386 have an optimization that makes it harder to
/// call math functions indirectly from inside an inlined function, causing
/// compile-time errors. Avoid `inline` in that case. #3072
-static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void float_op_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T f;
- float_T (*function)(float_T) = (float_T (*)(float_T)) fptr;
rettv->v_type = VAR_FLOAT;
if (tv_get_float_chk(argvars, &f)) {
- rettv->vval.v_float = function(f);
+ rettv->vval.v_float = fptr.float_func(f);
} else {
rettv->vval.v_float = 0.0;
}
}
-static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
}
- ApiDispatchWrapper fn = (ApiDispatchWrapper)fptr;
+ MsgpackRpcRequestHandler handler = *fptr.api_handler;
Array args = ARRAY_DICT_INIT;
@@ -271,7 +278,8 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
Error err = ERROR_INIT;
- Object result = fn(VIML_INTERNAL_CALL, args, &err);
+ Arena res_arena = ARENA_EMPTY;
+ Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err);
if (ERROR_SET(&err)) {
semsg_multiline((const char *)e_api_error, err.msg);
@@ -284,20 +292,23 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
end:
api_free_array(args);
- api_free_object(result);
+ if (handler.arena_return) {
+ arena_mem_free(arena_finish(&res_arena));
+ } else {
+ api_free_object(result);
+ }
api_clear_error(&err);
}
/// "abs(expr)" function
-static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_abs(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_FLOAT) {
- float_op_wrapper(argvars, rettv, (FunPtr)&fabs);
+ float_op_wrapper(argvars, rettv, (EvalFuncData){ .float_func = &fabs });
} else {
- varnumber_T n;
bool error = false;
- n = tv_get_number_chk(&argvars[0], &error);
+ varnumber_T n = tv_get_number_chk(&argvars[0], &error);
if (error) {
rettv->vval.v_number = -1;
} else if (n > 0) {
@@ -309,7 +320,7 @@ static void f_abs(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "add(list, item)" function
-static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 1; // Default: failed.
if (argvars[0].v_type == VAR_LIST) {
@@ -337,22 +348,21 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "and(expr, expr)" function
-static void f_and(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
& tv_get_number_chk(&argvars[1], NULL);
}
/// "api_info()" function
-static void f_api_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
Dictionary metadata = api_metadata();
(void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL);
- api_free_dictionary(metadata);
}
/// "append(lnum, string/list)" function
-static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_append(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const linenr_T lnum = tv_get_lnum(&argvars[0]);
@@ -360,7 +370,7 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "appendbufline(buf, lnum, string/list)" function
-static void f_appendbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_appendbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *const buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
@@ -371,79 +381,8 @@ static void f_appendbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- if (argvars[0].v_type == VAR_UNKNOWN) {
- // use the current window
- rettv->vval.v_number = ARGCOUNT;
- } else if (argvars[0].v_type == VAR_NUMBER
- && tv_get_number(&argvars[0]) == -1) {
- // use the global argument list
- rettv->vval.v_number = GARGCOUNT;
- } else {
- // use the argument list of the specified window
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- if (wp != NULL) {
- rettv->vval.v_number = WARGCOUNT(wp);
- } else {
- rettv->vval.v_number = -1;
- }
- }
-}
-
-/// "argidx()" function
-static void f_argidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = curwin->w_arg_idx;
-}
-
-/// "arglistid" function
-static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = -1;
- win_T *wp = find_tabwin(&argvars[0], &argvars[1]);
- if (wp != NULL) {
- rettv->vval.v_number = wp->w_alist->id;
- }
-}
-
-/// "argv(nr)" function
-static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- aentry_T *arglist = NULL;
- int argcount = -1;
-
- if (argvars[0].v_type != VAR_UNKNOWN) {
- if (argvars[1].v_type == VAR_UNKNOWN) {
- arglist = ARGLIST;
- argcount = ARGCOUNT;
- } else if (argvars[1].v_type == VAR_NUMBER
- && tv_get_number(&argvars[1]) == -1) {
- arglist = GARGLIST;
- argcount = GARGCOUNT;
- } else {
- win_T *wp = find_win_by_nr_or_id(&argvars[1]);
- if (wp != NULL) {
- // Use the argument list of the specified window
- arglist = WARGLIST(wp);
- argcount = WARGCOUNT(wp);
- }
- }
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- int idx = (int)tv_get_number_chk(&argvars[0], NULL);
- if (arglist != NULL && idx >= 0 && idx < argcount) {
- rettv->vval.v_string = xstrdup((const char *)alist_name(&arglist[idx]));
- } else if (idx == -1) {
- get_arglist_as_rettv(arglist, argcount, rettv);
- }
- } else {
- get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
- }
-}
-
/// "atan2()" function
-static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_atan2(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T fx;
float_T fy;
@@ -457,16 +396,16 @@ static void f_atan2(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "browse(save, title, initdir, default)" function
-static void f_browse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_browse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
}
/// "browsedir(title, initdir)" function
-static void f_browsedir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_browsedir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- f_browse(argvars, rettv, NULL);
+ f_browse(argvars, rettv, fptr);
}
/// Find a buffer by number or exact name.
@@ -479,8 +418,8 @@ static buf_T *find_buffer(typval_T *avar)
} else if (avar->v_type == VAR_STRING && avar->vval.v_string != NULL) {
buf = buflist_findname_exp(avar->vval.v_string);
if (buf == NULL) {
- /* No full path name match, try a match with a URL or a "nofile"
- * buffer, these don't use the full path. */
+ // No full path name match, try a match with a URL or a "nofile"
+ // buffer, these don't use the full path.
FOR_ALL_BUFFERS(bp) {
if (bp->b_fname != NULL
&& (path_with_url(bp->b_fname) || bt_nofilename(bp))
@@ -495,7 +434,7 @@ static buf_T *find_buffer(typval_T *avar)
}
/// "bufadd(expr)" function
-static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char_u *name = (char_u *)tv_get_string(&argvars[0]);
@@ -503,13 +442,13 @@ static void f_bufadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "bufexists(expr)" function
-static void f_bufexists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = (find_buffer(&argvars[0]) != NULL);
}
/// "buflisted(expr)" function
-static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_buflisted(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *buf;
@@ -518,7 +457,7 @@ static void f_buflisted(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "bufload(expr)" function
-static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr)
+static void f_bufload(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
{
buf_T *buf = get_buf_arg(&argvars[0]);
@@ -533,7 +472,7 @@ static void f_bufload(typval_T *argvars, typval_T *unused, FunPtr fptr)
}
/// "bufloaded(expr)" function
-static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufloaded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *buf;
@@ -542,7 +481,7 @@ static void f_bufloaded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "bufname(expr)" function
-static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const buf_T *buf;
rettv->v_type = VAR_STRING;
@@ -558,7 +497,7 @@ static void f_bufname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "bufnr(expr)" function
-static void f_bufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const buf_T *buf;
bool error = false;
@@ -618,13 +557,13 @@ static void buf_win_common(typval_T *argvars, typval_T *rettv, bool get_nr)
}
/// "bufwinid(nr)" function
-static void f_bufwinid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufwinid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_win_common(argvars, rettv, false);
}
/// "bufwinnr(nr)" function
-static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_bufwinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_win_common(argvars, rettv, true);
}
@@ -632,17 +571,15 @@ static void f_bufwinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Get buffer by number or pattern.
buf_T *tv_get_buf(typval_T *tv, int curtab_only)
{
- char_u *name = (char_u *)tv->vval.v_string;
- int save_magic;
- char *save_cpo;
- buf_T *buf;
-
if (tv->v_type == VAR_NUMBER) {
return buflist_findnr((int)tv->vval.v_number);
}
if (tv->v_type != VAR_STRING) {
return NULL;
}
+
+ char_u *name = (char_u *)tv->vval.v_string;
+
if (name == NULL || *name == NUL) {
return curbuf;
}
@@ -651,13 +588,13 @@ buf_T *tv_get_buf(typval_T *tv, int curtab_only)
}
// Ignore 'magic' and 'cpoptions' here to make scripts portable
- save_magic = p_magic;
- p_magic = TRUE;
- save_cpo = p_cpo;
- p_cpo = "";
+ int save_magic = p_magic;
+ p_magic = true;
+ char *save_cpo = p_cpo;
+ p_cpo = empty_option;
- buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name),
- true, false, curtab_only));
+ buf_T *buf = buflist_findnr(buflist_findpat((char *)name, (char *)name + STRLEN(name),
+ true, false, curtab_only));
p_magic = save_magic;
p_cpo = save_cpo;
@@ -686,10 +623,8 @@ buf_T *tv_get_buf_from_arg(typval_T *const tv) FUNC_ATTR_NONNULL_ALL
/// valid.
buf_T *get_buf_arg(typval_T *arg)
{
- buf_T *buf;
-
emsg_off++;
- buf = tv_get_buf(arg, false);
+ buf_T *buf = tv_get_buf(arg, false);
emsg_off--;
if (buf == NULL) {
semsg(_("E158: Invalid buffer name: %s"), tv_get_string(arg));
@@ -698,7 +633,7 @@ buf_T *get_buf_arg(typval_T *arg)
}
/// "byte2line(byte)" function
-static void f_byte2line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
long boff = tv_get_number(&argvars[0]) - 1;
if (boff < 0) {
@@ -733,19 +668,19 @@ static void byteidx(typval_T *argvars, typval_T *rettv, int comp)
}
/// "byteidx()" function
-static void f_byteidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_byteidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- byteidx(argvars, rettv, FALSE);
+ byteidx(argvars, rettv, false);
}
/// "byteidxcomp()" function
-static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_byteidxcomp(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- byteidx(argvars, rettv, TRUE);
+ byteidx(argvars, rettv, true);
}
/// "call(func, arglist [, dict])" function
-static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[1].v_type != VAR_LIST) {
emsg(_(e_listreq));
@@ -758,7 +693,6 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool owned = false;
char_u *func;
partial_T *partial = NULL;
- dict_T *selfdict = NULL;
if (argvars[0].v_type == VAR_FUNC) {
func = (char_u *)argvars[0].vval.v_string;
} else if (argvars[0].v_type == VAR_PARTIAL) {
@@ -776,6 +710,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; // type error or empty name
}
+ dict_T *selfdict = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_DICT) {
emsg(_(e_dictreq));
@@ -794,13 +729,13 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "changenr()" function
-static void f_changenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_changenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = curbuf->b_u_seq_cur;
}
/// "chanclose(id[, stream])" function
-static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_chanclose(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -839,7 +774,7 @@ static void f_chanclose(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "chansend(id, data)" function
-static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_chansend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -880,7 +815,7 @@ static void f_chansend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "char2nr(string)" function
-static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_char2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[1].v_type != VAR_UNKNOWN) {
if (!tv_check_num(&argvars[1])) {
@@ -898,10 +833,9 @@ static void f_char2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
{
colnr_T col = 0;
- pos_T *fp;
int fnum = curbuf->b_fnum;
- fp = var2fpos(&argvars[0], false, &fnum, charcol);
+ pos_T *fp = var2fpos(&argvars[0], false, &fnum, charcol);
if (fp != NULL && fnum == curbuf->b_fnum) {
if (fp->col == MAXCOL) {
// '> can be MAXCOL, get the length of the line then
@@ -915,11 +849,12 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
// col(".") when the cursor is on the NUL at the end of the line
// because of "coladd" can be seen as an extra column.
if (virtual_active() && fp == &curwin->w_cursor) {
- char_u *p = get_cursor_pos_ptr();
+ char *p = (char *)get_cursor_pos_ptr();
if (curwin->w_cursor.coladd >=
- (colnr_T)win_chartabsize(curwin, p, curwin->w_virtcol - curwin->w_cursor.coladd)) {
+ (colnr_T)win_chartabsize(curwin, p,
+ curwin->w_virtcol - curwin->w_cursor.coladd)) {
int l;
- if (*p != NUL && p[(l = utfc_ptr2len((char *)p))] == NUL) {
+ if (*p != NUL && p[(l = utfc_ptr2len(p))] == NUL) {
col += l;
}
}
@@ -930,20 +865,21 @@ static void get_col(typval_T *argvars, typval_T *rettv, bool charcol)
}
/// "charcol()" function
-static void f_charcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_col(argvars, rettv, true);
}
/// "charidx()" function
-static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
if (argvars[0].v_type != VAR_STRING
|| argvars[1].v_type != VAR_NUMBER
|| (argvars[2].v_type != VAR_UNKNOWN
- && argvars[2].v_type != VAR_NUMBER)) {
+ && argvars[2].v_type != VAR_NUMBER
+ && argvars[2].v_type != VAR_BOOL)) {
emsg(_(e_invarg));
return;
}
@@ -982,11 +918,8 @@ static void f_charidx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "chdir(dir)" function
-static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char_u *cwd;
- CdScope scope = kCdScopeGlobal;
-
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -997,7 +930,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// Return the current directory
- cwd = xmalloc(MAXPATHL);
+ char_u *cwd = xmalloc(MAXPATHL);
if (os_dirname(cwd, MAXPATHL) != FAIL) {
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(cwd);
@@ -1006,6 +939,7 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
xfree(cwd);
+ CdScope scope = kCdScopeGlobal;
if (curwin->w_localdir != NULL) {
scope = kCdScopeWindow;
} else if (curtab->tp_localdir != NULL) {
@@ -1019,13 +953,10 @@ static void f_chdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "cindent(lnum)" function
-static void f_cindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- pos_T pos;
- linenr_T lnum;
-
- pos = curwin->w_cursor;
- lnum = tv_get_lnum(argvars);
+ 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();
@@ -1050,24 +981,22 @@ win_T *get_optional_window(typval_T *argvars, int idx)
}
/// "col(string)" function
-static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_col(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_col(argvars, rettv, false);
}
/// "confirm(message, buttons[, default [, type]])" function
-static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
char buf2[NUMBUFLEN];
- const char *message;
const char *buttons = NULL;
int def = 1;
int type = VIM_GENERIC;
- const char *typestr;
bool error = false;
- message = tv_get_string_chk(&argvars[0]);
+ const char *message = tv_get_string_chk(&argvars[0]);
if (message == NULL) {
error = true;
}
@@ -1079,7 +1008,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[2].v_type != VAR_UNKNOWN) {
def = (int)tv_get_number_chk(&argvars[2], &error);
if (argvars[3].v_type != VAR_UNKNOWN) {
- typestr = tv_get_string_buf_chk(&argvars[3], buf2);
+ const char *typestr = tv_get_string_buf_chk(&argvars[3], buf2);
if (typestr == NULL) {
error = true;
} else {
@@ -1111,13 +1040,13 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "copy()" function
-static void f_copy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_copy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
var_item_copy(NULL, &argvars[0], rettv, false, 0);
}
/// "count()" function
-static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_count(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
long n = 0;
int ic = 0;
@@ -1152,15 +1081,13 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
} else if (argvars[0].v_type == VAR_LIST) {
- listitem_T *li;
- list_T *l;
- long idx;
+ list_T *l = argvars[0].vval.v_list;
- if ((l = argvars[0].vval.v_list) != NULL) {
- li = tv_list_first(l);
+ if (l != NULL) {
+ listitem_T *li = tv_list_first(l);
if (argvars[2].v_type != VAR_UNKNOWN) {
if (argvars[3].v_type != VAR_UNKNOWN) {
- idx = tv_get_number_chk(&argvars[3], &error);
+ long idx = tv_get_number_chk(&argvars[3], &error);
if (!error) {
li = tv_list_find(l, (int)idx);
if (li == NULL) {
@@ -1180,19 +1107,17 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
} else if (argvars[0].v_type == VAR_DICT) {
- int todo;
- dict_T *d;
- hashitem_T *hi;
+ dict_T *d = argvars[0].vval.v_dict;
- if ((d = argvars[0].vval.v_dict) != NULL) {
+ if (d != NULL) {
if (argvars[2].v_type != VAR_UNKNOWN) {
if (argvars[3].v_type != VAR_UNKNOWN) {
emsg(_(e_invarg));
}
}
- todo = error ? 0 : (int)d->dv_hashtab.ht_used;
- for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) {
+ int todo = error ? 0 : (int)d->dv_hashtab.ht_used;
+ for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
todo--;
if (tv_equal(&TV_DICT_HI2DI(hi)->di_tv, &argvars[1], ic, false)) {
@@ -1210,7 +1135,7 @@ static void f_count(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
///
/// Checks the existence of a cscope connection.
-static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_cscope_connection(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int num = 0;
const char *dbpath = NULL;
@@ -1231,7 +1156,7 @@ static void f_cscope_connection(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxget([{index}])" function
-static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
size_t index = 0;
if (argvars[0].v_type == VAR_NUMBER) {
@@ -1255,7 +1180,7 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxpop()" function
-static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxpop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (!ctx_restore(NULL, kCtxAll)) {
emsg(_("Context stack is empty"));
@@ -1263,7 +1188,7 @@ static void f_ctxpop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxpush([{types}])" function
-static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxpush(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int types = kCtxAll;
if (argvars[0].v_type == VAR_LIST) {
@@ -1294,7 +1219,7 @@ static void f_ctxpush(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxset({context}[, {index}])" function
-static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
semsg(_(e_invarg2), "expected dictionary as first argument");
@@ -1334,7 +1259,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "ctxsize()" function
-static void f_ctxsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_ctxsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = (varnumber_T)ctx_size();
@@ -1406,18 +1331,16 @@ static void set_cursorpos(typval_T *argvars, typval_T *rettv, bool charcol)
/// Moves the cursor to the specified line and column.
///
/// @return 0 when the position could be set, -1 otherwise.
-static void f_cursor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_cursor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_cursorpos(argvars, rettv, false);
}
/// "debugbreak()" function
-static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_debugbreak(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int pid;
-
rettv->vval.v_number = FAIL;
- pid = (int)tv_get_number(&argvars[0]);
+ int pid = (int)tv_get_number(&argvars[0]);
if (pid == 0) {
emsg(_(e_invarg));
} else {
@@ -1436,7 +1359,7 @@ static void f_debugbreak(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "deepcopy()" function
-static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int noref = 0;
@@ -1453,7 +1376,7 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "delete()" function
-static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_delete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
if (check_secure()) {
@@ -1489,7 +1412,7 @@ static void f_delete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// dictwatcheradd(dict, key, funcref) function
-static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -1527,7 +1450,7 @@ static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// dictwatcherdel(dict, key, funcref) function
-static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -1562,12 +1485,8 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "deletebufline()" function
-static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_deletebufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- linenr_T last;
- buf_T *curbuf_save = NULL;
- win_T *curwin_save = NULL;
-
buf_T *const buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
rettv->vval.v_number = 1; // FAIL
@@ -1576,6 +1495,7 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const bool is_curbuf = buf == curbuf;
const bool save_VIsual_active = VIsual_active;
+ linenr_T last;
const linenr_T first = tv_get_lnum_buf(&argvars[1], buf);
if (argvars[2].v_type != VAR_UNKNOWN) {
last = tv_get_lnum_buf(&argvars[2], buf);
@@ -1589,6 +1509,8 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ buf_T *curbuf_save = NULL;
+ win_T *curwin_save = NULL;
if (!is_curbuf) {
VIsual_active = false;
curbuf_save = curbuf;
@@ -1639,19 +1561,19 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "did_filetype()" function
-static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = did_filetype;
}
/// "diff_filler()" function
-static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_diff_filler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars)));
}
/// "diff_hlID()" function
-static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_diff_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum = tv_get_lnum(argvars);
static linenr_T prev_lnum = 0;
@@ -1660,8 +1582,6 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static int change_start = 0;
static int change_end = 0;
static hlf_T hlID = (hlf_T)0;
- int filler_lines;
- int col;
if (lnum < 0) { // ignore type error in {lnum} arg
lnum = 0;
@@ -1670,7 +1590,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|| changedtick != buf_get_changedtick(curbuf)
|| fnum != curbuf->b_fnum) {
// New line, buffer, change: need to get the values.
- filler_lines = diff_check(curwin, lnum);
+ int filler_lines = diff_check(curwin, lnum);
if (filler_lines < 0) {
if (filler_lines == -1) {
change_start = MAXCOL;
@@ -1692,7 +1612,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (hlID == HLF_CHD || hlID == HLF_TXD) {
- col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}.
+ int col = (int)tv_get_number(&argvars[1]) - 1; // Ignore type error in {col}.
if (col >= change_start && col <= change_end) {
hlID = HLF_TXD; // Changed text.
} else {
@@ -1703,7 +1623,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "empty({expr})" function
-static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_empty(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool n = true;
@@ -1753,7 +1673,7 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "environ()" function
-static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_environ(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -1798,7 +1718,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "escape({string}, {chars})" function
-static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_escape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
@@ -1809,7 +1729,7 @@ static void f_escape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getenv()" function
-static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char_u *p = (char_u *)vim_getenv(tv_get_string(&argvars[0]));
@@ -1823,7 +1743,7 @@ static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "eval()" function
-static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *s = tv_get_string_chk(&argvars[0]);
if (s != NULL) {
@@ -1844,13 +1764,13 @@ static void f_eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "eventhandler()" function
-static void f_eventhandler(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_eventhandler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = vgetc_busy;
}
/// "executable()" function
-static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_executable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (tv_check_for_string(&argvars[0]) == FAIL) {
return;
@@ -1879,7 +1799,7 @@ static char *get_list_line(int c, void *cookie, int indent, bool do_concat)
return s == NULL ? NULL : xstrdup(s);
}
-static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int arg_off)
+static void execute_common(typval_T *argvars, typval_T *rettv, int arg_off)
{
const int save_msg_silent = msg_silent;
const int save_emsg_silent = emsg_silent;
@@ -1958,13 +1878,13 @@ static void execute_common(typval_T *argvars, typval_T *rettv, FunPtr fptr, int
}
/// "execute(command)" function
-static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- execute_common(argvars, rettv, fptr, 0);
+ execute_common(argvars, rettv, 0);
}
/// "win_execute(win_id, command)" function
-static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Return an empty string if something fails.
rettv->v_type = VAR_STRING;
@@ -1974,12 +1894,12 @@ static void f_win_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tabpage_T *tp;
win_T *wp = win_id2wp_tp(id, &tp);
if (wp != NULL && tp != NULL) {
- WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, fptr, 1));
+ WIN_EXECUTE(wp, tp, execute_common(argvars, rettv, 1));
}
}
/// "exepath()" function
-static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (tv_check_for_nonempty_string(&argvars[0]) == FAIL) {
return;
@@ -2000,7 +1920,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "exists()" function
-static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n = false;
@@ -2040,14 +1960,10 @@ static void f_exists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "expand()" function
-static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- size_t len;
- char *errormsg;
int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
- expand_T xpc;
bool error = false;
- char_u *result;
#ifdef BACKSLASH_IN_FILENAME
char_u *p_csl_save = p_csl;
@@ -2065,9 +1981,17 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *s = tv_get_string(&argvars[0]);
if (*s == '%' || *s == '#' || *s == '<') {
- emsg_off++;
- result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);
- emsg_off--;
+ if (p_verbose == 0) {
+ emsg_off++;
+ }
+ size_t len;
+ char *errormsg = NULL;
+ char_u *result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL, false);
+ if (p_verbose == 0) {
+ emsg_off--;
+ } else if (errormsg != NULL) {
+ emsg(errormsg);
+ }
if (rettv->v_type == VAR_LIST) {
tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
@@ -2085,6 +2009,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
options |= WILD_KEEP_ALL;
}
if (!error) {
+ expand_T xpc;
ExpandInit(&xpc);
xpc.xp_context = EXPAND_FILES;
if (p_wic) {
@@ -2112,7 +2037,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "menu_get(path [, modes])" function
-static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
int modes = MENU_ALL_MODES;
@@ -2125,7 +2050,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "expandcmd()" function
/// Expand all the special characters in a command string.
-static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *errormsg = NULL;
@@ -2141,18 +2066,16 @@ static void f_expandcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
};
eap.argt |= EX_NOSPC;
+ emsg_off++;
expand_filename(&eap, &cmdstr, &errormsg);
- if (errormsg != NULL && *errormsg != NUL) {
- emsg(errormsg);
- }
+ emsg_off--;
+
rettv->vval.v_string = cmdstr;
}
/// "flatten(list[, {maxdepth}])" function
-static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_flatten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- list_T *list;
- long maxdepth;
bool error = false;
if (argvars[0].v_type != VAR_LIST) {
@@ -2160,6 +2083,7 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ long maxdepth;
if (argvars[1].v_type == VAR_UNKNOWN) {
maxdepth = 999999;
} else {
@@ -2173,7 +2097,7 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- list = argvars[0].vval.v_list;
+ list_T *list = argvars[0].vval.v_list;
if (list != NULL
&& !var_check_lock(tv_list_locked(list),
N_("flatten() argument"),
@@ -2185,12 +2109,11 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "extend(list, list [, idx])" function
/// "extend(dict, dict [, action])" function
-static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_extend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const arg_errmsg = N_("extend() argument");
if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) {
- long before;
bool error = false;
list_T *const l1 = argvars[0].vval.v_list;
@@ -2198,7 +2121,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
- before = (long)tv_get_number_chk(&argvars[2], &error);
+ long before = (long)tv_get_number_chk(&argvars[2], &error);
if (error) {
return; // Type error; errmsg already given.
}
@@ -2262,7 +2185,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "feedkeys()" function
-static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// This is not allowed in the sandbox. If the commands would still be
// executed in the sandbox it would be OK, but it probably happens later,
@@ -2283,17 +2206,17 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "filereadable()" function
-static void f_filereadable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_filereadable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const p = tv_get_string(&argvars[0]);
rettv->vval.v_number =
- (*p && !os_isdir((const char_u *)p) && os_file_is_readable(p));
+ (*p && !os_isdir(p) && os_file_is_readable(p));
}
/// @return 0 for not writable
/// 1 for writable file
/// 2 for a dir which we have rights to write into.
-static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_filewritable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *filename = tv_get_string(&argvars[0]);
rettv->vval.v_number = os_file_is_writable(filename);
@@ -2302,7 +2225,7 @@ static void f_filewritable(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
{
char_u *fresult = NULL;
- char_u *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
+ char_u *path = *curbuf->b_p_path == NUL ? p_path : (char_u *)curbuf->b_p_path;
int count = 1;
bool first = true;
bool error = false;
@@ -2343,7 +2266,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
find_what, (char_u *)curbuf->b_ffname,
(find_what == FINDFILE_DIR
? (char_u *)""
- : curbuf->b_p_sua));
+ : (char_u *)curbuf->b_p_sua));
first = false;
if (fresult != NULL && rettv->v_type == VAR_LIST) {
@@ -2358,25 +2281,25 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
/// "filter()" function
-static void f_filter(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_filter(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- filter_map(argvars, rettv, FALSE);
+ filter_map(argvars, rettv, false);
}
/// "finddir({fname}[, {path}[, {count}]])" function
-static void f_finddir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_finddir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
findfilendir(argvars, rettv, FINDFILE_DIR);
}
/// "findfile({fname}[, {path}[, {count}]])" function
-static void f_findfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_findfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
findfilendir(argvars, rettv, FINDFILE_FILE);
}
/// "float2nr({float})" function
-static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T f;
@@ -2392,7 +2315,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "fmod()" function
-static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_fmod(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T fx;
float_T fy;
@@ -2406,14 +2329,14 @@ static void f_fmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "fnameescape({string})" function
-static void f_fnameescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_fnameescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_string = vim_strsave_fnameescape(tv_get_string(&argvars[0]), VSE_NONE);
rettv->v_type = VAR_STRING;
}
/// "fnamemodify({fname}, {mods})" function
-static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char_u *fbuf = NULL;
size_t len = 0;
@@ -2440,146 +2363,22 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
xfree(fbuf);
}
-/// "foldclosed()" function
-static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end)
-{
- const linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- linenr_T first;
- linenr_T last;
- if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) {
- if (end) {
- rettv->vval.v_number = (varnumber_T)last;
- } else {
- rettv->vval.v_number = (varnumber_T)first;
- }
- return;
- }
- }
- rettv->vval.v_number = -1;
-}
-
-/// "foldclosed()" function
-static void f_foldclosed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- foldclosed_both(argvars, rettv, FALSE);
-}
-
-/// "foldclosedend()" function
-static void f_foldclosedend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- foldclosed_both(argvars, rettv, TRUE);
-}
-
-/// "foldlevel()" function
-static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- rettv->vval.v_number = foldLevel(lnum);
- }
-}
-
-/// "foldtext()" function
-static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- linenr_T foldstart;
- linenr_T foldend;
- char_u *dashes;
- linenr_T lnum;
- char_u *s;
- char_u *r;
- int len;
- char *txt;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
-
- foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
- foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
- dashes = (char_u *)get_vim_var_str(VV_FOLDDASHES);
- if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) {
- // Find first non-empty line in the fold.
- for (lnum = foldstart; lnum < foldend; lnum++) {
- if (!linewhite(lnum)) {
- break;
- }
- }
-
- // Find interesting text in this line.
- s = (char_u *)skipwhite((char *)ml_get(lnum));
- // skip C comment-start
- if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) {
- s = (char_u *)skipwhite((char *)s + 2);
- if (*skipwhite((char *)s) == NUL && lnum + 1 < foldend) {
- s = (char_u *)skipwhite((char *)ml_get(lnum + 1));
- if (*s == '*') {
- s = (char_u *)skipwhite((char *)s + 1);
- }
- }
- }
- unsigned long count = (unsigned long)foldend - (unsigned long)foldstart + 1;
- txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
- r = xmalloc(STRLEN(txt)
- + STRLEN(dashes) // for %s
- + 20 // for %3ld
- + STRLEN(s)); // concatenated
- sprintf((char *)r, txt, dashes, count);
- len = (int)STRLEN(r);
- STRCAT(r, s);
- // remove 'foldmarker' and 'commentstring'
- foldtext_cleanup(r + len);
- rettv->vval.v_string = (char *)r;
- }
-}
-
-/// "foldtextresult(lnum)" function
-static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- char_u *text;
- char_u buf[FOLD_TEXT_LEN];
- static bool entered = false;
-
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = NULL;
- if (entered) {
- return; // reject recursive use
- }
- entered = true;
- linenr_T lnum = tv_get_lnum(argvars);
- // Treat illegal types and illegal string values for {lnum} the same.
- if (lnum < 0) {
- lnum = 0;
- }
-
- foldinfo_T info = fold_info(curwin, lnum);
- if (info.fi_lines > 0) {
- text = get_foldtext(curwin, lnum, lnum + (linenr_T)info.fi_lines - 1, info, buf);
- if (text == buf) {
- text = vim_strsave(text);
- }
- rettv->vval.v_string = (char *)text;
- }
-
- entered = false;
-}
-
/// "foreground()" function
-static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{}
-static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- common_function(argvars, rettv, true, fptr);
+ common_function(argvars, rettv, true);
}
-static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_function(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- common_function(argvars, rettv, false, fptr);
+ common_function(argvars, rettv, false);
}
/// "garbagecollect()" function
-static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_garbagecollect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// This is postponed until we are back at the toplevel, because we may be
// using Lists and Dicts internally. E.g.: ":echo [garbagecollect()]".
@@ -2591,12 +2390,8 @@ static void f_garbagecollect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "get()" function
-static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- listitem_T *li;
- list_T *l;
- dictitem_T *di;
- dict_T *d;
typval_T *tv = NULL;
bool what_is_dict = false;
@@ -2617,17 +2412,19 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
} else if (argvars[0].v_type == VAR_LIST) {
- if ((l = argvars[0].vval.v_list) != NULL) {
+ list_T *l = argvars[0].vval.v_list;
+ if (l != NULL) {
bool error = false;
- li = tv_list_find(l, (int)tv_get_number_chk(&argvars[1], &error));
+ listitem_T *li = tv_list_find(l, (int)tv_get_number_chk(&argvars[1], &error));
if (!error && li != NULL) {
tv = TV_LIST_ITEM_TV(li);
}
}
} else if (argvars[0].v_type == VAR_DICT) {
- if ((d = argvars[0].vval.v_dict) != NULL) {
- di = tv_dict_find(d, tv_get_string(&argvars[1]), -1);
+ dict_T *d = argvars[0].vval.v_dict;
+ if (d != NULL) {
+ dictitem_T *di = tv_dict_find(d, tv_get_string(&argvars[1]), -1);
if (di != NULL) {
tv = &di->di_tv;
}
@@ -2639,7 +2436,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type == VAR_PARTIAL) {
pt = argvars[0].vval.v_partial;
} else {
- memset(&fref_pt, 0, sizeof(fref_pt));
+ CLEAR_FIELD(fref_pt);
fref_pt.pt_name = (char_u *)argvars[0].vval.v_string;
pt = &fref_pt;
}
@@ -2690,7 +2487,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getbufinfo()" function
-static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getbufinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *argbuf = NULL;
bool filtered = false;
@@ -2752,7 +2549,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Get line or list of lines from buffer "buf" into "rettv".
///
-/// @param retlist if TRUE, then the lines are returned as a Vim List.
+/// @param retlist if true, then the lines are returned as a Vim List.
///
/// @return range (from start to end) of lines in rettv from the specified
/// buffer.
@@ -2789,7 +2586,7 @@ static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retli
}
/// "getbufline()" function
-static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
@@ -2802,7 +2599,7 @@ static void f_getbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getchangelist()" function
-static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getchangelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, 2);
@@ -2851,147 +2648,6 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "getchar()" and "getcharstr()" functions
-static void getchar_common(typval_T *argvars, typval_T *rettv)
- FUNC_ATTR_NONNULL_ALL
-{
- varnumber_T n;
- bool error = false;
-
- no_mapping++;
- allow_keys++;
- for (;;) {
- // Position the cursor. Needed after a message that ends in a space,
- // or if event processing caused a redraw.
- ui_cursor_goto(msg_row, msg_col);
-
- if (argvars[0].v_type == VAR_UNKNOWN) {
- // getchar(): blocking wait.
- // TODO(bfredl): deduplicate shared logic with state_enter ?
- if (!char_avail()) {
- (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
- if (!multiqueue_empty(main_loop.events)) {
- state_handle_k_event();
- continue;
- }
- }
- n = safe_vgetc();
- } else if (tv_get_number_chk(&argvars[0], &error) == 1) {
- // getchar(1): only check if char avail
- n = vpeekc_any();
- } else if (error || vpeekc_any() == NUL) {
- // illegal argument or getchar(0) and no char avail: return zero
- n = 0;
- } else {
- // getchar(0) and char avail() != NUL: get a character.
- // Note that vpeekc_any() returns K_SPECIAL for K_IGNORE.
- n = safe_vgetc();
- }
-
- if (n == K_IGNORE
- || n == K_MOUSEMOVE
- || n == K_VER_SCROLLBAR
- || n == K_HOR_SCROLLBAR) {
- continue;
- }
- break;
- }
- no_mapping--;
- allow_keys--;
-
- if (!ui_has_messages()) {
- // redraw the screen after getchar()
- update_screen(CLEAR);
- }
-
- set_vim_var_nr(VV_MOUSE_WIN, 0);
- set_vim_var_nr(VV_MOUSE_WINID, 0);
- set_vim_var_nr(VV_MOUSE_LNUM, 0);
- set_vim_var_nr(VV_MOUSE_COL, 0);
-
- rettv->vval.v_number = n;
- if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
- char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
- int i = 0;
-
- // Turn a special key into three bytes, plus modifier.
- if (mod_mask != 0) {
- temp[i++] = K_SPECIAL;
- temp[i++] = KS_MODIFIER;
- temp[i++] = (char_u)mod_mask;
- }
- if (IS_SPECIAL(n)) {
- temp[i++] = K_SPECIAL;
- temp[i++] = (char_u)K_SECOND(n);
- temp[i++] = K_THIRD(n);
- } else {
- i += utf_char2bytes((int)n, (char *)temp + i);
- }
- assert(i < 10);
- temp[i++] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)vim_strsave(temp);
-
- if (is_mouse_key((int)n)) {
- int row = mouse_row;
- int col = mouse_col;
- int grid = mouse_grid;
- linenr_T lnum;
- win_T *wp;
- int winnr = 1;
-
- if (row >= 0 && col >= 0) {
- // Find the window at the mouse coordinates and compute the
- // text position.
- win_T *const win = mouse_find_win(&grid, &row, &col);
- if (win == NULL) {
- return;
- }
- (void)mouse_comp_pos(win, &row, &col, &lnum);
- for (wp = firstwin; wp != win; wp = wp->w_next) {
- ++winnr;
- }
- set_vim_var_nr(VV_MOUSE_WIN, winnr);
- set_vim_var_nr(VV_MOUSE_WINID, wp->handle);
- set_vim_var_nr(VV_MOUSE_LNUM, lnum);
- set_vim_var_nr(VV_MOUSE_COL, col + 1);
- }
- }
- }
-}
-
-/// "getchar()" function
-static void f_getchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- getchar_common(argvars, rettv);
-}
-
-/// "getcharstr()" function
-static void f_getcharstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- getchar_common(argvars, rettv);
-
- if (rettv->v_type == VAR_NUMBER) {
- char temp[7]; // mbyte-char: 6, NUL: 1
- const varnumber_T n = rettv->vval.v_number;
- int i = 0;
-
- if (n != 0) {
- i += utf_char2bytes((int)n, (char *)temp);
- }
- assert(i < 7);
- temp[i++] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xstrdup(temp);
- }
-}
-
-/// "getcharmod()" function
-static void f_getcharmod(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = mod_mask;
-}
-
static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool charcol)
{
pos_T *fp = NULL;
@@ -3048,13 +2704,13 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool
}
/// "getcharpos()" function
-static void f_getcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, false, true);
}
/// "getcharsearch()" function
-static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -3065,42 +2721,8 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until());
}
-/// "getcmdcompltype()" function
-static void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)get_cmdline_completion();
-}
-
-/// "getcmdline()" function
-static void f_getcmdline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = (char *)get_cmdline_str();
-}
-
-/// "getcmdpos()" function
-static void f_getcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = get_cmdline_pos() + 1;
-}
-
-/// "getcmdscreenpos()" function
-static void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->vval.v_number = get_cmdline_screen_pos() + 1;
-}
-
-/// "getcmdtype()" function
-static void f_getcmdtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xmallocz(1);
- rettv->vval.v_string[0] = (char)get_cmdline_type();
-}
-
/// "getcmdwintype()" function
-static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -3108,84 +2730,6 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string[0] = (char)cmdwin_type;
}
-/// "getcompletion()" function
-static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- char_u *pat;
- expand_T xpc;
- bool filtered = false;
- int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH
- | WILD_NO_BEEP | WILD_HOME_REPLACE;
-
- if (argvars[1].v_type != VAR_STRING) {
- semsg(_(e_invarg2), "type must be a string");
- return;
- }
- const char *const type = tv_get_string(&argvars[1]);
-
- if (argvars[2].v_type != VAR_UNKNOWN) {
- filtered = (bool)tv_get_number_chk(&argvars[2], NULL);
- }
-
- if (p_wic) {
- options |= WILD_ICASE;
- }
-
- // For filtered results, 'wildignore' is used
- if (!filtered) {
- options |= WILD_KEEP_ALL;
- }
-
- if (argvars[0].v_type != VAR_STRING) {
- emsg(_(e_invarg));
- return;
- }
- const char *pattern = tv_get_string(&argvars[0]);
-
- if (strcmp(type, "cmdline") == 0) {
- set_one_cmd_context(&xpc, pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- xpc.xp_col = (int)STRLEN(pattern);
- goto theend;
- }
-
- ExpandInit(&xpc);
- xpc.xp_pattern = (char *)pattern;
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- xpc.xp_context = cmdcomplete_str_to_type(type);
- if (xpc.xp_context == EXPAND_NOTHING) {
- semsg(_(e_invarg2), type);
- return;
- }
-
- if (xpc.xp_context == EXPAND_MENUS) {
- set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- }
-
- if (xpc.xp_context == EXPAND_CSCOPE) {
- set_context_in_cscope_cmd(&xpc, (const char *)xpc.xp_pattern, CMD_cscope);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- }
-
- if (xpc.xp_context == EXPAND_SIGN) {
- set_context_in_sign_cmd(&xpc, (char_u *)xpc.xp_pattern);
- xpc.xp_pattern_len = STRLEN(xpc.xp_pattern);
- }
-
-theend:
- pat = addstar((char_u *)xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
- ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
- tv_list_alloc_ret(rettv, xpc.xp_numfiles);
-
- for (int i = 0; i < xpc.xp_numfiles; i++) {
- tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
- -1);
- }
- xfree(pat);
- ExpandCleanup(&xpc);
-}
-
/// `getcwd([{win}[, {tab}]])` function
///
/// Every scope not specified implies the currently selected scope object.
@@ -3195,7 +2739,7 @@ theend:
/// @pre An argument may not be -1 if preceding arguments are not all -1.
///
/// @post The return value will be a string.
-static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Possible scope of working directory to return.
CdScope scope = kCdScopeInvalid;
@@ -3203,15 +2747,15 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Numbers of the scope objects (window, tab) we want the working directory
// of. A `-1` means to skip this scope, a `0` means the current object.
int scope_number[] = {
- [kCdScopeWindow] = 0, // Number of window to look at.
+ [kCdScopeWindow] = 0, // Number of window to look at.
[kCdScopeTabpage] = 0, // Number of tab to look at.
};
char *cwd = NULL; // Current working directory to print
char *from = NULL; // The original string to copy
- tabpage_T *tp = curtab; // The tabpage to look at.
- win_T *win = curwin; // The window to look at.
+ tabpage_T *tp = curtab; // The tabpage to look at.
+ win_T *win = curwin; // The window to look at.
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -3308,14 +2852,14 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getfontname()" function
-static void f_getfontname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
}
/// "getfperm({fname})" function
-static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *perm = NULL;
char_u flags[] = "rwx";
@@ -3335,7 +2879,7 @@ static void f_getfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getfsize({fname})" function
-static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getfsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *fname = tv_get_string(&argvars[0]);
@@ -3344,7 +2888,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
FileInfo file_info;
if (os_fileinfo(fname, &file_info)) {
uint64_t filesize = os_fileinfo_size(&file_info);
- if (os_isdir((const char_u *)fname)) {
+ if (os_isdir(fname)) {
rettv->vval.v_number = 0;
} else {
rettv->vval.v_number = (varnumber_T)filesize;
@@ -3360,7 +2904,7 @@ static void f_getfsize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getftime({fname})" function
-static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *fname = tv_get_string(&argvars[0]);
@@ -3373,7 +2917,7 @@ static void f_getftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getftype({fname})" function
-static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getftype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char_u *type = NULL;
char *t;
@@ -3407,7 +2951,7 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getjumplist()" function
-static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getjumplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
win_T *const wp = find_tabwin(&argvars[0], &argvars[1]);
@@ -3438,7 +2982,7 @@ static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getline(lnum, [end])" function
-static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T end;
bool retlist;
@@ -3455,15 +2999,8 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
get_buffer_lines(curbuf, lnum, end, retlist, rettv);
}
-/// "getloclist()" function
-static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *wp = find_win_by_nr_or_id(&argvars[0]);
- get_qf_loc_list(false, wp, &argvars[1], rettv);
-}
-
/// "getmarklist()" function
-static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getmarklist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
@@ -3481,10 +3018,8 @@ static void f_getmarklist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getmousepos()" function
-static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getmousepos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- dict_T *d;
- win_T *wp;
int row = mouse_row;
int col = mouse_col;
int grid = mouse_grid;
@@ -3495,12 +3030,12 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
varnumber_T column = 0;
tv_dict_alloc_ret(rettv);
- d = rettv->vval.v_dict;
+ dict_T *d = rettv->vval.v_dict;
tv_dict_add_nr(d, S_LEN("screenrow"), (varnumber_T)mouse_row + 1);
tv_dict_add_nr(d, S_LEN("screencol"), (varnumber_T)mouse_col + 1);
- wp = mouse_find_win(&grid, &row, &col);
+ win_T *wp = mouse_find_win(&grid, &row, &col);
if (wp != NULL) {
int height = wp->w_height + wp->w_hsep_height + wp->w_status_height;
// The height is adjusted by 1 when there is a bottom border. This is not
@@ -3524,34 +3059,28 @@ static void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getpid()" function
-static void f_getpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = os_get_pid();
}
/// "getcurpos(string)" function
-static void f_getcurpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcurpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, true, false);
}
-static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, true, true);
}
/// "getpos(string)" function
-static void f_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getpos_both(argvars, rettv, false, false);
}
-/// "getqflist()" functions
-static void f_getqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- get_qf_loc_list(true, NULL, &argvars[0], rettv);
-}
-
/// Common between getreg(), getreginfo() and getregtype(): get the register
/// name from the first argument.
/// Returns zero on error.
@@ -3573,7 +3102,7 @@ static int getreg_get_regname(typval_T *argvars)
}
/// "getreg()" function
-static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int arg2 = false;
bool return_list = false;
@@ -3609,7 +3138,7 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getregtype()" function
-static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getregtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// on error return an empty string
rettv->v_type = VAR_STRING;
@@ -3629,7 +3158,7 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "gettabinfo()" function
-static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_gettabinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tabpage_T *tparg = NULL;
@@ -3661,7 +3190,7 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "gettagstack()" function
-static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_gettagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = curwin; // default is current window
@@ -3678,7 +3207,7 @@ static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getwininfo()" function
-static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getwininfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wparg = NULL;
@@ -3723,7 +3252,7 @@ static void dummy_timer_close_cb(TimeWatcher *tw, void *data)
}
/// "wait(timeout, condition[, interval])" function
-static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1;
@@ -3777,7 +3306,7 @@ static void f_wait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "win_screenpos()" function
-static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, 2);
const win_T *const wp = find_win_by_nr_or_id(&argvars[0]);
@@ -3788,7 +3317,6 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Move the window wp into a new split of targetwin in a given direction
static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
{
- int dir;
int height = wp->w_height;
win_T *oldwin = curwin;
@@ -3802,6 +3330,7 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags
}
// Remove the old window and frame from the tree of frames
+ int dir;
(void)winframe_remove(wp, &dir, NULL);
win_remove(wp, NULL);
last_status(false); // may need to remove last status line
@@ -3824,14 +3353,10 @@ static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags
}
/// "win_splitmove()" function
-static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_splitmove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- win_T *wp;
- win_T *targetwin;
- int flags = 0, size = 0;
-
- wp = find_win_by_nr_or_id(&argvars[0]);
- targetwin = find_win_by_nr_or_id(&argvars[1]);
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ win_T *targetwin = find_win_by_nr_or_id(&argvars[1]);
if (wp == NULL || targetwin == NULL || wp == targetwin
|| !win_valid(wp) || !win_valid(targetwin)
@@ -3841,6 +3366,8 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ int flags = 0, size = 0;
+
if (argvars[2].v_type != VAR_UNKNOWN) {
dict_T *d;
dictitem_T *di;
@@ -3864,7 +3391,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getwinpos({timeout})" function
-static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getwinpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, 2);
tv_list_append_number(rettv->vval.v_list, -1);
@@ -3872,26 +3399,26 @@ static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getwinposx()" function
-static void f_getwinposx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getwinposx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
}
/// "getwinposy()" function
-static void f_getwinposy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getwinposy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
}
/// "glob()" function
-static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int options = WILD_SILENT|WILD_USE_NL;
expand_T xpc;
bool error = false;
- /* When the optional second argument is non-zero, don't remove matches
- * for 'wildignore' and don't put matches for 'suffixes' at the end. */
+ // When the optional second argument is non-zero, don't remove matches
+ // for 'wildignore' and don't put matches for 'suffixes' at the end.
rettv->v_type = VAR_STRING;
if (argvars[1].v_type != VAR_UNKNOWN) {
if (tv_get_number_chk(&argvars[1], &error)) {
@@ -3933,7 +3460,7 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "globpath()" function
-static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath.
bool error = false;
@@ -3964,7 +3491,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (file != NULL && !error) {
garray_T ga;
ga_init(&ga, (int)sizeof(char_u *), 10);
- globpath((char_u *)tv_get_string(&argvars[0]), (char_u *)file, &ga, flags);
+ globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags);
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
@@ -3983,7 +3510,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "glob2regpat()" function
-static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error
@@ -3992,7 +3519,7 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "has()" function
-static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
static const char *const has_list[] = {
#if defined(BSD) && !defined(__APPLE__)
@@ -4214,7 +3741,7 @@ static bool has_wsl(void)
/// @pre An argument may not be -1 if preceding arguments are not all -1.
///
/// @post The return value will be either the number `1` or `0`.
-static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Possible scope of working directory to return.
CdScope scope = kCdScopeInvalid;
@@ -4303,101 +3830,20 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "histadd()" function
-static void f_histadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- HistoryType histype;
-
- rettv->vval.v_number = false;
- if (check_secure()) {
- return;
- }
- const char *str = tv_get_string_chk(&argvars[0]); // NULL on type error
- histype = str != NULL ? get_histtype(str, strlen(str), false) : HIST_INVALID;
- if (histype != HIST_INVALID) {
- char buf[NUMBUFLEN];
- str = tv_get_string_buf(&argvars[1], buf);
- if (*str != NUL) {
- init_history();
- add_to_history(histype, (char_u *)str, false, NUL);
- rettv->vval.v_number = true;
- return;
- }
- }
-}
-
-/// "histdel()" function
-static void f_histdel(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- int n;
- const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
- if (str == NULL) {
- n = 0;
- } else if (argvars[1].v_type == VAR_UNKNOWN) {
- // only one argument: clear entire history
- n = clr_history(get_histtype(str, strlen(str), false));
- } else if (argvars[1].v_type == VAR_NUMBER) {
- // index given: remove that entry
- n = del_history_idx(get_histtype(str, strlen(str), false),
- (int)tv_get_number(&argvars[1]));
- } else {
- // string given: remove all matching entries
- char buf[NUMBUFLEN];
- n = del_history_entry(get_histtype(str, strlen(str), false),
- (char_u *)tv_get_string_buf(&argvars[1], buf));
- }
- rettv->vval.v_number = n;
-}
-
-/// "histget()" function
-static void f_histget(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- HistoryType type;
- int idx;
-
- const char *const str = tv_get_string_chk(&argvars[0]); // NULL on type error
- if (str == NULL) {
- rettv->vval.v_string = NULL;
- } else {
- type = get_histtype(str, strlen(str), false);
- if (argvars[1].v_type == VAR_UNKNOWN) {
- idx = get_history_idx(type);
- } else {
- idx = (int)tv_get_number_chk(&argvars[1], NULL);
- }
- // -1 on type error
- rettv->vval.v_string = (char *)vim_strsave(get_history_entry(type, idx));
- }
- rettv->v_type = VAR_STRING;
-}
-
-/// "histnr()" function
-static void f_histnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const char *const history = tv_get_string_chk(&argvars[0]);
- HistoryType i = history == NULL
- ? HIST_INVALID
- : get_histtype(history, strlen(history), false);
- if (i != HIST_INVALID) {
- i = get_history_idx(i);
- }
- rettv->vval.v_number = i;
-}
-
/// "highlightID(name)" function
-static void f_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_hlID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = syn_name2id(tv_get_string(&argvars[0]));
}
/// "highlight_exists()" function
-static void f_hlexists(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_hlexists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = highlight_exists(tv_get_string(&argvars[0]));
}
/// "hostname()" function
-static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char hostname[256];
@@ -4407,7 +3853,7 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// iconv() function
-static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_iconv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
vimconv_T vimconv;
@@ -4416,17 +3862,19 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const str = tv_get_string(&argvars[0]);
char buf1[NUMBUFLEN];
- char_u *const from = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[1], buf1)));
+ char_u *const from =
+ (char_u *)enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[1], buf1)));
char buf2[NUMBUFLEN];
- char_u *const to = enc_canonize(enc_skip((char_u *)tv_get_string_buf(&argvars[2], buf2)));
+ char_u *const to =
+ (char_u *)enc_canonize(enc_skip((char *)tv_get_string_buf(&argvars[2], buf2)));
vimconv.vc_type = CONV_NONE;
- convert_setup(&vimconv, from, to);
+ convert_setup(&vimconv, (char *)from, (char *)to);
// If the encodings are equal, no conversion needed.
if (vimconv.vc_type == CONV_NONE) {
rettv->vval.v_string = xstrdup(str);
} else {
- rettv->vval.v_string = (char *)string_convert(&vimconv, (char_u *)str, NULL);
+ rettv->vval.v_string = string_convert(&vimconv, (char *)str, NULL);
}
convert_setup(&vimconv, NULL, NULL);
@@ -4435,7 +3883,7 @@ static void f_iconv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "indent()" function
-static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+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) {
@@ -4446,7 +3894,7 @@ static void f_indent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "index()" function
-static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
long idx = 0;
bool ic = false;
@@ -4521,23 +3969,20 @@ static bool inputsecret_flag = false;
/// "input()" function
/// Also handles inputsecret() when inputsecret is set.
-static void f_input(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_input(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- get_user_input(argvars, rettv, FALSE, inputsecret_flag);
+ get_user_input(argvars, rettv, false, inputsecret_flag);
}
/// "inputdialog()" function
-static void f_inputdialog(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputdialog(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- get_user_input(argvars, rettv, TRUE, inputsecret_flag);
+ get_user_input(argvars, rettv, true, inputsecret_flag);
}
/// "inputlist()" function
-static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int selected;
- int mouse_used;
-
if (argvars[0].v_type != VAR_LIST) {
semsg(_(e_listarg), "inputlist()");
return;
@@ -4555,7 +4000,8 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
});
// Ask for choice.
- selected = prompt_for_number(&mouse_used);
+ int mouse_used;
+ int selected = prompt_for_number(&mouse_used);
if (mouse_used) {
selected -= lines_left;
}
@@ -4566,7 +4012,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static garray_T ga_userinput = { 0, 0, sizeof(tasave_T), 4, NULL };
/// "inputrestore()" function
-static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputrestore(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (!GA_EMPTY(&ga_userinput)) {
ga_userinput.ga_len--;
@@ -4580,7 +4026,7 @@ static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "inputsave()" function
-static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputsave(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// Add an entry to the stack of typeahead storage.
tasave_T *p = GA_APPEND_VIA_PTR(tasave_T, &ga_userinput);
@@ -4588,17 +4034,17 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "inputsecret()" function
-static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_inputsecret(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
cmdline_star++;
inputsecret_flag = true;
- f_input(argvars, rettv, NULL);
+ f_input(argvars, rettv, fptr);
cmdline_star--;
inputsecret_flag = false;
}
/// "insert()" function
-static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_insert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
list_T *l;
bool error = false;
@@ -4671,28 +4117,27 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "interrupt()" function
static void f_interrupt(typval_T *argvars FUNC_ATTR_UNUSED, typval_T *rettv FUNC_ATTR_UNUSED,
- FunPtr fptr FUNC_ATTR_UNUSED)
+ EvalFuncData fptr FUNC_ATTR_UNUSED)
{
got_int = true;
}
/// "invert(expr)" function
-static void f_invert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_invert(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL);
}
/// "isdirectory()" function
-static void f_isdirectory(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_isdirectory(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- rettv->vval.v_number = os_isdir((const char_u *)tv_get_string(&argvars[0]));
+ rettv->vval.v_number = os_isdir(tv_get_string(&argvars[0]));
}
/// "islocked()" function
-static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_islocked(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
lval_T lv;
- dictitem_T *di;
rettv->vval.v_number = -1;
const char_u *const end = (char_u *)get_lval((char *)tv_get_string(&argvars[0]),
@@ -4705,7 +4150,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
semsg(_(e_trailing_arg), end);
} else {
if (lv.ll_tv == NULL) {
- di = find_var(lv.ll_name, lv.ll_name_len, NULL, true);
+ dictitem_T *di = find_var(lv.ll_name, lv.ll_name_len, NULL, true);
if (di != NULL) {
// Consider a variable locked when:
// 1. the variable itself is locked
@@ -4732,7 +4177,7 @@ static void f_islocked(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "isinf()" function
-static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_isinf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_FLOAT
&& xisinf(argvars[0].vval.v_float)) {
@@ -4741,14 +4186,14 @@ static void f_isinf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "isnan()" function
-static void f_isnan(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_isnan(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = argvars[0].v_type == VAR_FLOAT
&& xisnan(argvars[0].vval.v_float);
}
/// "id()" function
-static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_id(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars);
@@ -4758,7 +4203,7 @@ static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobpid(id)" function
-static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobpid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4782,7 +4227,7 @@ static void f_jobpid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobresize(job, width, height)" function
-static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4849,7 +4294,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
if (!clear_env) {
typval_T temp_env = TV_INITIAL_VALUE;
- f_environ(NULL, &temp_env, NULL);
+ f_environ(NULL, &temp_env, (EvalFuncData){ .nullptr = NULL });
tv_dict_extend(env, temp_env.vval.v_dict, "force");
tv_dict_free(temp_env.vval.v_dict);
@@ -4938,7 +4383,7 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en
}
/// "jobstart()" function
-static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -5013,7 +4458,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir_executable((const char *)cwd)) {
+ if (!os_isdir(cwd)) {
semsg(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -5058,7 +4503,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobstop()" function
-static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -5091,7 +4536,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "jobwait(ids[, timeout])" function
-static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -5190,7 +4635,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// json_decode() function
-static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_json_decode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char numbuf[NUMBUFLEN];
const char *s = NULL;
@@ -5224,14 +4669,14 @@ static void f_json_decode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// json_encode() function
-static void f_json_encode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_json_encode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = encode_tv2json(&argvars[0], NULL);
}
/// "last_buffer_nr()" function.
-static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n = 0;
@@ -5245,7 +4690,7 @@ static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "len()" function
-static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
switch (argvars[0].v_type) {
case VAR_STRING:
@@ -5316,19 +4761,19 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type)
}
/// "libcall()" function
-static void f_libcall(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_libcall(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
libcall_common(argvars, rettv, VAR_STRING);
}
/// "libcallnr()" function
-static void f_libcallnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_libcallnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
libcall_common(argvars, rettv, VAR_NUMBER);
}
/// "line(string, [winid])" function
-static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum = 0;
pos_T *fp = NULL;
@@ -5359,7 +4804,7 @@ static void f_line(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "line2byte(lnum)" function
-static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_line2byte(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 + 1) {
@@ -5373,7 +4818,7 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "lispindent(lnum)" function
-static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+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);
@@ -5387,13 +4832,13 @@ static void f_lispindent(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "localtime()" function
-static void f_localtime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_localtime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = (varnumber_T)time(NULL);
}
/// luaeval() function implementation
-static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
const char *const str = tv_get_string_chk(&argvars[0]);
@@ -5405,9 +4850,9 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "map()" function
-static void f_map(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_map(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- filter_map(argvars, rettv, TRUE);
+ filter_map(argvars, rettv, true);
}
static void find_some_match(typval_T *const argvars, typval_T *const rettv,
@@ -5417,19 +4862,17 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
long len = 0;
char_u *expr = NULL;
regmatch_T regmatch;
- char *save_cpo;
long start = 0;
long nth = 1;
colnr_T startcol = 0;
bool match = false;
list_T *l = NULL;
- listitem_T *li = NULL;
long idx = 0;
char_u *tofree = NULL;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
- save_cpo = p_cpo;
- p_cpo = "";
+ char *save_cpo = p_cpo;
+ p_cpo = empty_option;
rettv->vval.v_number = -1;
switch (type) {
@@ -5455,6 +4898,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
break;
}
+ listitem_T *li = NULL;
if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) == NULL) {
goto theend;
@@ -5563,10 +5007,8 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
const size_t rd = (size_t)(regmatch.endp[0] - regmatch.startp[0]);
TV_LIST_ITEM_TV(li1)->vval.v_string = xmemdupz((const char *)regmatch.startp[0], rd);
- TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(
- regmatch.startp[0] - expr);
- TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(
- regmatch.endp[0] - expr);
+ TV_LIST_ITEM_TV(li3)->vval.v_number = (varnumber_T)(regmatch.startp[0] - expr);
+ TV_LIST_ITEM_TV(li4)->vval.v_number = (varnumber_T)(regmatch.endp[0] - expr);
if (l != NULL) {
TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx;
}
@@ -5626,31 +5068,31 @@ theend:
}
/// "match()" function
-static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatch);
}
/// "matchend()" function
-static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchEnd);
}
/// "matchlist()" function
-static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchList);
}
/// "matchstr()" function
-static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchStr);
}
/// "matchstrpos()" function
-static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
find_some_match(argvars, rettv, kSomeMatchStrPos);
}
@@ -5705,19 +5147,19 @@ static void max_min(const typval_T *const tv, typval_T *const rettv, const bool
}
/// "max()" function
-static void f_max(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_max(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- max_min(argvars, rettv, TRUE);
+ max_min(argvars, rettv, true);
}
/// "min()" function
-static void f_min(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_min(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- max_min(argvars, rettv, FALSE);
+ max_min(argvars, rettv, false);
}
/// "mkdir()" function
-static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_mkdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int prot = 0755; // -V536
@@ -5762,7 +5204,7 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "mode()" function
-static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_mode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[MODE_MAX_LENGTH];
@@ -5779,7 +5221,7 @@ static void f_mode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "msgpackdump()" function
-static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_msgpackdump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
if (argvars[0].v_type != VAR_LIST) {
@@ -5918,7 +5360,7 @@ static void msgpackparse_unpack_blob(const blob_T *const blob, list_T *const ret
}
/// "msgpackparse" function
-static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_msgpackparse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
@@ -5934,7 +5376,7 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "nextnonblank()" function
-static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_nextnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum;
@@ -5951,7 +5393,7 @@ static void f_nextnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "nr2char()" function
-static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_nr2char(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[1].v_type != VAR_UNKNOWN) {
if (!tv_check_num(&argvars[1])) {
@@ -5982,14 +5424,14 @@ static void f_nr2char(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "or(expr, expr)" function
-static void f_or(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_or(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
| tv_get_number_chk(&argvars[1], NULL);
}
/// "pathshorten()" function
-static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pathshorten(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int trim_len = 1;
@@ -6011,7 +5453,7 @@ static void f_pathshorten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "pow()" function
-static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
float_T fx;
float_T fy;
@@ -6025,7 +5467,7 @@ static void f_pow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "prevnonblank()" function
-static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prevnonblank(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum = tv_get_lnum(argvars);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
@@ -6039,19 +5481,18 @@ static void f_prevnonblank(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "printf()" function
-static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
{
- int len;
int saved_did_emsg = did_emsg;
// Get the required length, allocate the buffer and do it for real.
did_emsg = false;
char buf[NUMBUFLEN];
const char *fmt = tv_get_string_buf(&argvars[0], buf);
- len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1);
+ int len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1);
if (!did_emsg) {
char *s = xmalloc((size_t)len + 1);
rettv->vval.v_string = s;
@@ -6062,15 +5503,14 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "prompt_setcallback({buffer}, {callback})" function
-static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- buf_T *buf;
Callback prompt_callback = { .type = kCallbackNone };
if (check_secure()) {
return;
}
- buf = tv_get_buf(&argvars[0], false);
+ buf_T *buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
return;
}
@@ -6086,15 +5526,14 @@ static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv, FunPtr fptr
}
/// "prompt_setinterrupt({buffer}, {callback})" function
-static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- buf_T *buf;
Callback interrupt_callback = { .type = kCallbackNone };
if (check_secure()) {
return;
}
- buf = tv_get_buf(&argvars[0], false);
+ buf_T *buf = tv_get_buf(&argvars[0], false);
if (buf == NULL) {
return;
}
@@ -6110,7 +5549,7 @@ static void f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv, FunPtr fpt
}
/// "prompt_getprompt({buffer})" function
-static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
// return an empty string by default, e.g. it's not a prompt buffer
@@ -6130,7 +5569,7 @@ static void f_prompt_getprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "prompt_setprompt({buffer}, {text})" function
-static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -6146,14 +5585,14 @@ static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "pum_getpos()" function
-static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pum_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
pum_set_event_info(rettv->vval.v_dict);
}
/// "pumvisible()" function
-static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_pumvisible(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (pum_visible()) {
rettv->vval.v_number = 1;
@@ -6161,7 +5600,7 @@ static void f_pumvisible(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "py3eval()" and "pyxeval()" functions (always python3)
-static void f_py3eval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_py3eval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
script_host_eval("python3", argvars, rettv);
}
@@ -6233,7 +5672,7 @@ static inline uint32_t shuffle_xoshiro128starstar(uint32_t *const x, uint32_t *c
}
/// "rand()" function
-static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
uint32_t result;
@@ -6244,7 +5683,7 @@ static void f_rand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// When no argument is given use the global seed list.
if (!initialized) {
// Initialize the global seed list.
- uint32_t x;
+ uint32_t x = 0;
init_srand(&x);
gx = splitmix32(&x);
@@ -6303,7 +5742,7 @@ theend:
}
/// "srand()" function
-static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_srand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
uint32_t x = 0;
@@ -6325,27 +5764,25 @@ static void f_srand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "perleval()" function
-static void f_perleval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_perleval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
script_host_eval("perl", argvars, rettv);
}
/// "rubyeval()" function
-static void f_rubyeval(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rubyeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
script_host_eval("ruby", argvars, rettv);
}
/// "range()" function
-static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_range(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- varnumber_T start;
varnumber_T end;
varnumber_T stride = 1;
- varnumber_T i;
bool error = false;
- start = tv_get_number_chk(&argvars[0], &error);
+ varnumber_T start = tv_get_number_chk(&argvars[0], &error);
if (argvars[1].v_type == VAR_UNKNOWN) {
end = start - 1;
start = 0;
@@ -6365,7 +5802,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
emsg(_("E727: Start past end"));
} else {
tv_list_alloc_ret(rettv, (end - start) / stride);
- for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {
+ for (varnumber_T i = start; stride > 0 ? i <= end : i >= end; i += stride) {
tv_list_append_number(rettv->vval.v_list, i);
}
}
@@ -6376,8 +5813,6 @@ static varnumber_T readdir_checkitem(void *context, const char *name)
FUNC_ATTR_NONNULL_ALL
{
typval_T *expr = (typval_T *)context;
- typval_T save_val;
- typval_T rettv;
typval_T argv[2];
varnumber_T retval = 0;
bool error = false;
@@ -6386,11 +5821,13 @@ static varnumber_T readdir_checkitem(void *context, const char *name)
return 1;
}
+ typval_T save_val;
prepare_vimvar(VV_VAL, &save_val);
set_vim_var_string(VV_VAL, name, -1);
argv[0].v_type = VAR_STRING;
argv[0].vval.v_string = (char *)name;
+ typval_T rettv;
if (eval_expr_typval(expr, argv, 1, &rettv) == FAIL) {
goto theend;
}
@@ -6409,7 +5846,7 @@ theend:
}
/// "readdir()" function
-static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_readdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenUnknown);
@@ -6427,17 +5864,16 @@ static void f_readdir(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "readfile()" function
-static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_readfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool binary = false;
bool blob = false;
FILE *fd;
- char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
+ char_u buf[(IOSIZE/256) * 256]; // rounded to avoid odd + 1
int io_size = sizeof(buf);
- int readlen; // size of last fread()
- char_u *prev = NULL; // previously read bytes, if any
- long prevlen = 0; // length of data in prev
- long prevsize = 0; // size of prev buffer
+ char_u *prev = NULL; // previously read bytes, if any
+ long prevlen = 0; // length of data in prev
+ long prevsize = 0; // size of prev buffer
long maxline = MAXLNUM;
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -6455,7 +5891,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// their own about CR-LF conversion.
const char *const fname = tv_get_string(&argvars[0]);
- if (os_isdir((const char_u *)fname)) {
+ if (os_isdir(fname)) {
semsg(_(e_isadir2), fname);
return;
}
@@ -6479,7 +5915,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);
while (maxline < 0 || tv_list_len(l) < maxline) {
- readlen = (int)fread(buf, 1, (size_t)io_size, fd);
+ int readlen = (int)fread(buf, 1, (size_t)io_size, fd);
// This for loop processes what was read, but is also entered at end
// of file so that either:
@@ -6511,9 +5947,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
assert(len < INT_MAX);
s = vim_strnsave(start, len);
} else {
- /* Change "prev" buffer to be the right size. This way
- * the bytes are only copied once, and very long lines are
- * allocated only once. */
+ // Change "prev" buffer to be the right size. This way
+ // the bytes are only copied once, and very long lines are
+ // allocated only once.
s = xrealloc(prev, (size_t)prevlen + len + 1);
memcpy(s + prevlen, start, len);
s[(size_t)prevlen + len] = NUL;
@@ -6587,10 +6023,10 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (start < p) {
// There's part of a line in buf, store it in "prev".
if (p - start + prevlen >= prevsize) {
- /* A common use case is ordinary text files and "prev" gets a
- * fragment of a line, so the first allocation is made
- * small, to avoid repeatedly 'allocing' large and
- * 'reallocing' small. */
+ // A common use case is ordinary text files and "prev" gets a
+ // fragment of a line, so the first allocation is made
+ // small, to avoid repeatedly 'allocing' large and
+ // 'reallocing' small.
if (prevsize == 0) {
prevsize = (long)(p - start);
} else {
@@ -6611,7 +6047,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getreginfo()" function
-static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int regname = getreg_get_regname(argvars);
if (regname == 0) {
@@ -6661,18 +6097,18 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reg_executing()" function
-static void f_reg_executing(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reg_executing(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
return_register(reg_executing, rettv);
}
/// "reg_recording()" function
-static void f_reg_recording(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reg_recording(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
return_register(reg_recording, rettv);
}
-static void f_reg_recorded(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reg_recorded(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
return_register(reg_recorded, rettv);
}
@@ -6714,7 +6150,7 @@ static int list2proftime(typval_T *arg, proftime_T *tm) FUNC_ATTR_NONNULL_ALL
/// one argument it returns the time passed since the argument.
/// With two arguments it returns the time passed between
/// the two arguments.
-static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reltime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
proftime_T res;
proftime_T start;
@@ -6756,7 +6192,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reltimestr()" function
-static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reltimestr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
proftime_T tm;
@@ -6769,7 +6205,7 @@ static void f_reltimestr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "remove()" function
-static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_remove(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const arg_errmsg = N_("remove() argument");
@@ -6785,7 +6221,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "rename({from}, {to})" function
-static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rename(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
rettv->vval.v_number = -1;
@@ -6797,7 +6233,7 @@ static void f_rename(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "repeat()" function
-static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_repeat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
varnumber_T n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST) {
@@ -6834,7 +6270,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "resolve()" function
-static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_resolve(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
const char *fname = tv_get_string(&argvars[0]);
@@ -6845,7 +6281,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
v = os_realpath(fname, v);
}
}
- rettv->vval.v_string = (char_u *)(v == NULL ? xstrdup(fname) : v);
+ rettv->vval.v_string = (v == NULL ? xstrdup(fname) : v);
#else
# ifdef HAVE_READLINK
{
@@ -6907,7 +6343,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (*q != NUL) {
cpy = remain;
remain = (remain
- ? (char *)concat_str((char_u *)q - 1, (char_u *)remain)
+ ? concat_str(q - 1, remain)
: xstrdup(q - 1));
xfree(cpy);
q[-1] = NUL;
@@ -6966,7 +6402,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& (p[2] == NUL
|| vim_ispathsep(p[2])))))) {
// Prepend "./".
- cpy = (char *)concat_str((const char_u *)"./", (const char_u *)p);
+ cpy = concat_str("./", p);
xfree(p);
p = cpy;
} else if (!is_relative_to_current) {
@@ -7003,7 +6439,7 @@ static void f_resolve(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reverse({list})" function
-static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reverse(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type == VAR_BLOB) {
blob_T *const b = argvars[0].vval.v_blob;
@@ -7028,7 +6464,7 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reduce(list, { accumulator, element -> value } [, initial])" function
-static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reduce(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_LIST && argvars[0].v_type != VAR_BLOB) {
emsg(_(e_listblobreq));
@@ -7139,7 +6575,6 @@ static void f_reduce(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static int get_search_arg(typval_T *varp, int *flagsp)
{
int dir = FORWARD;
- int mask;
if (varp->v_type != VAR_UNKNOWN) {
char nbuf[NUMBUFLEN];
@@ -7147,6 +6582,7 @@ static int get_search_arg(typval_T *varp, int *flagsp)
if (flags == NULL) {
return 0; // Type error; errmsg already given.
}
+ int mask;
while (*flags != NUL) {
switch (*flags) {
case 'b':
@@ -7196,26 +6632,19 @@ static int get_search_arg(typval_T *varp, int *flagsp)
/// Shared by search() and searchpos() functions.
static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
- int flags;
- pos_T pos;
- pos_T save_cursor;
bool save_p_ws = p_ws;
- int dir;
int retval = 0; // default: FAIL
long lnum_stop = 0;
- proftime_T tm;
long time_limit = 0;
int options = SEARCH_KEEP;
- int subpatnum;
- searchit_arg_T sia;
bool use_skip = false;
const char *const pat = tv_get_string(&argvars[0]);
- dir = get_search_arg(&argvars[1], flagsp); // May set p_ws.
+ int dir = get_search_arg(&argvars[1], flagsp); // May set p_ws.
if (dir == 0) {
goto theend;
}
- flags = *flagsp;
+ int flags = *flagsp;
if (flags & SP_START) {
options |= SEARCH_START;
}
@@ -7242,25 +6671,27 @@ static int search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
}
// Set the time limit, if there is one.
- tm = profile_setlimit(time_limit);
-
- /*
- * This function does not accept SP_REPEAT and SP_RETCOUNT flags.
- * Check to make sure only those flags are set.
- * Also, Only the SP_NOMOVE or the SP_SETPCMARK flag can be set. Both
- * flags cannot be set. Check for that condition also.
- */
+ proftime_T tm = profile_setlimit(time_limit);
+
+ // This function does not accept SP_REPEAT and SP_RETCOUNT flags.
+ // Check to make sure only those flags are set.
+ // Also, Only the SP_NOMOVE or the SP_SETPCMARK flag can be set. Both
+ // flags cannot be set. Check for that condition also.
if (((flags & (SP_REPEAT | SP_RETCOUNT)) != 0)
|| ((flags & SP_NOMOVE) && (flags & SP_SETPCMARK))) {
semsg(_(e_invarg2), tv_get_string(&argvars[1]));
goto theend;
}
- pos = save_cursor = curwin->w_cursor;
+ pos_T save_cursor;
+ pos_T pos = save_cursor = curwin->w_cursor;
pos_T firstpos = { 0 };
- memset(&sia, 0, sizeof(sia));
- sia.sa_stop_lnum = (linenr_T)lnum_stop;
- sia.sa_tm = &tm;
+ searchit_arg_T sia = {
+ .sa_stop_lnum = (linenr_T)lnum_stop,
+ .sa_tm = &tm,
+ };
+
+ int subpatnum;
// Repeat until {skip} returns false.
for (;;) {
@@ -7333,7 +6764,7 @@ theend:
}
/// "rpcnotify()" function
-static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -7368,7 +6799,7 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "rpcrequest()" function
-static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -7395,8 +6826,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
sctx_T save_current_sctx;
- char *save_sourcing_name, *save_autocmd_fname, *save_autocmd_match;
- linenr_T save_sourcing_lnum;
+ char *save_autocmd_fname, *save_autocmd_match;
int save_autocmd_bufnr;
funccal_entry_T funccal_entry;
@@ -7404,16 +6834,14 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// If this is called from a provider function, restore the scope
// information of the caller.
save_current_sctx = current_sctx;
- save_sourcing_name = sourcing_name;
- save_sourcing_lnum = sourcing_lnum;
save_autocmd_fname = autocmd_fname;
save_autocmd_match = autocmd_match;
save_autocmd_bufnr = autocmd_bufnr;
save_funccal(&funccal_entry);
current_sctx = provider_caller_scope.script_ctx;
- sourcing_name = provider_caller_scope.sourcing_name;
- sourcing_lnum = provider_caller_scope.sourcing_lnum;
+ ga_grow(&exestack, 1);
+ ((estack_T *)exestack.ga_data)[exestack.ga_len++] = provider_caller_scope.es_entry;
autocmd_fname = provider_caller_scope.autocmd_fname;
autocmd_match = provider_caller_scope.autocmd_match;
autocmd_bufnr = provider_caller_scope.autocmd_bufnr;
@@ -7430,8 +6858,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (l_provider_call_nesting) {
current_sctx = save_current_sctx;
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ exestack.ga_len--;
autocmd_fname = save_autocmd_fname;
autocmd_match = save_autocmd_match;
autocmd_bufnr = save_autocmd_bufnr;
@@ -7461,12 +6888,12 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
end:
- arena_mem_free(res_mem, NULL);
+ arena_mem_free(res_mem);
api_clear_error(&err);
}
/// "rpcstart()" function (DEPRECATED)
-static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -7533,7 +6960,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "rpcstop()" function
-static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -7551,7 +6978,7 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// if called with a job, stop it, else closes the channel
uint64_t id = (uint64_t)argvars[0].vval.v_number;
if (find_job(id, false)) {
- f_jobstop(argvars, rettv, NULL);
+ f_jobstop(argvars, rettv, fptr);
} else {
const char *error;
rettv->vval.v_number =
@@ -7563,16 +6990,15 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenattr()" function
-static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int c;
-
- ScreenGrid *grid;
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
+ ScreenGrid *grid;
screenchar_adjust(&grid, &row, &col);
+ int c;
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
@@ -7582,16 +7008,15 @@ static void f_screenattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenchar()" function
-static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int c;
-
- ScreenGrid *grid;
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
+ ScreenGrid *grid;
screenchar_adjust(&grid, &row, &col);
+ int c;
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
c = -1;
} else {
@@ -7601,12 +7026,12 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenchars()" function
-static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- ScreenGrid *grid;
int row = (int)tv_get_number_chk(&argvars[0], NULL) - 1;
int col = (int)tv_get_number_chk(&argvars[1], NULL) - 1;
+ ScreenGrid *grid;
screenchar_adjust(&grid, &row, &col);
if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) {
@@ -7614,7 +7039,7 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
int pcc[MAX_MCO];
- int c = utfc_ptr2char(grid->chars[grid->line_offset[row] + (size_t)col], pcc);
+ int c = utfc_ptr2char((char *)grid->chars[grid->line_offset[row] + (size_t)col], pcc);
int composing_len = 0;
while (pcc[composing_len] != 0) {
composing_len++;
@@ -7629,18 +7054,14 @@ static void f_screenchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "screencol()" function
///
/// First column is 1 to be consistent with virtcol().
-static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screencol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ui_current_col() + 1;
}
/// "screenpos({winid}, {lnum}, {col})" function
-static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- pos_T pos;
- int row = 0;
- int scol = 0, ccol = 0, ecol = 0;
-
tv_dict_alloc_ret(rettv);
dict_T *dict = rettv->vval.v_dict;
@@ -7649,9 +7070,13 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
- pos.lnum = (linenr_T)tv_get_number(&argvars[1]);
- pos.col = (colnr_T)tv_get_number(&argvars[2]) - 1;
- pos.coladd = 0;
+ pos_T pos = {
+ .lnum = (linenr_T)tv_get_number(&argvars[1]),
+ .col = (colnr_T)tv_get_number(&argvars[2]) - 1,
+ .coladd = 0
+ };
+ int row = 0;
+ int scol = 0, ccol = 0, ecol = 0;
textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false);
tv_dict_add_nr(dict, S_LEN("row"), row);
@@ -7661,13 +7086,13 @@ static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "screenrow()" function
-static void f_screenrow(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenrow(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ui_current_row() + 1;
}
/// "screenstring()" function
-static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_screenstring(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
@@ -7686,7 +7111,7 @@ static void f_screenstring(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "search()" function
-static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_search(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int flags = 0;
@@ -7694,7 +7119,7 @@ static void f_search(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "searchdecl()" function
-static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchdecl(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int locally = 1;
int thisblock = 0;
@@ -7719,7 +7144,6 @@ static void f_searchdecl(typval_T *argvars, typval_T *rettv, FunPtr fptr)
static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
{
bool save_p_ws = p_ws;
- int dir;
int flags = 0;
int retval = 0; // default: FAIL
long lnum_stop = 0;
@@ -7737,7 +7161,7 @@ static int searchpair_cmn(typval_T *argvars, pos_T *match_pos)
}
// Handle the optional fourth argument: flags.
- dir = get_search_arg(&argvars[3], &flags); // may set p_ws.
+ int dir = get_search_arg(&argvars[3], &flags); // may set p_ws.
if (dir == 0) {
goto theend;
}
@@ -7790,13 +7214,13 @@ theend:
}
/// "searchpair()" function
-static void f_searchpair(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchpair(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = searchpair_cmn(argvars, NULL);
}
/// "searchpairpos()" function
-static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchpairpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T match_pos;
int lnum = 0;
@@ -7831,33 +7255,24 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
long time_limit)
FUNC_ATTR_NONNULL_ARG(1, 2, 3)
{
- char *save_cpo;
- char_u *pat, *pat2 = NULL, *pat3 = NULL;
long retval = 0;
- pos_T pos;
- pos_T firstpos;
- pos_T foundpos;
- pos_T save_cursor;
- pos_T save_pos;
- int n;
int nest = 1;
bool use_skip = false;
int options = SEARCH_KEEP;
- proftime_T tm;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
- save_cpo = p_cpo;
- p_cpo = (char *)empty_option;
+ char *save_cpo = p_cpo;
+ p_cpo = empty_option;
// Set the time limit, if there is one.
- tm = profile_setlimit(time_limit);
+ proftime_T tm = profile_setlimit(time_limit);
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
const size_t pat2_len = strlen(spat) + strlen(epat) + 17;
- pat2 = xmalloc(pat2_len);
+ char_u *pat2 = xmalloc(pat2_len);
const size_t pat3_len = strlen(spat) + strlen(mpat) + strlen(epat) + 25;
- pat3 = xmalloc(pat3_len);
+ char_u *pat3 = xmalloc(pat3_len);
snprintf((char *)pat2, pat2_len, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
if (*mpat == NUL) {
STRCPY(pat3, pat2);
@@ -7873,19 +7288,21 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
use_skip = eval_expr_valid_arg(skip);
}
- save_cursor = curwin->w_cursor;
- pos = curwin->w_cursor;
+ pos_T save_cursor = curwin->w_cursor;
+ pos_T pos = curwin->w_cursor;
+ pos_T firstpos;
clearpos(&firstpos);
+ pos_T foundpos;
clearpos(&foundpos);
- pat = pat3;
+ char_u *pat = pat3;
for (;;) {
- searchit_arg_T sia;
- memset(&sia, 0, sizeof(sia));
- sia.sa_stop_lnum = lnum_stop;
- sia.sa_tm = &tm;
+ searchit_arg_T sia = {
+ .sa_stop_lnum = lnum_stop,
+ .sa_tm = &tm,
+ };
- n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
- options, RE_SEARCH, &sia);
+ int n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
+ options, RE_SEARCH, &sia);
if (n == FAIL || (firstpos.lnum != 0 && equalpos(pos, firstpos))) {
// didn't find it or found the first match again: FAIL
break;
@@ -7911,7 +7328,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
// If the skip pattern matches, ignore this match.
if (use_skip) {
- save_pos = curwin->w_cursor;
+ pos_T save_pos = curwin->w_cursor;
curwin->w_cursor = pos;
bool err = false;
const bool r = eval_expr_to_bool(skip, &err);
@@ -7971,18 +7388,23 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir
xfree(pat2);
xfree(pat3);
- if ((char_u *)p_cpo == empty_option) {
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
// Darn, evaluating the {skip} expression changed the value.
- free_string_option((char_u *)save_cpo);
+ // If it's still empty it was changed and restored, need to restore in
+ // the complicated way.
+ if (*p_cpo == NUL) {
+ set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ }
+ free_string_option(save_cpo);
}
return retval;
}
/// "searchpos()" function
-static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_searchpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T match_pos;
int flags = 0;
@@ -8002,7 +7424,7 @@ static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "serverlist()" function
-static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_serverlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
size_t n;
char **addrs = server_address_list(&n);
@@ -8016,7 +7438,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "serverstart()" function
-static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_serverstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; // Address of the new server
@@ -8061,7 +7483,7 @@ static void f_serverstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "serverstop()" function
-static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_serverstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -8081,7 +7503,7 @@ static void f_serverstop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "setbufline()" function
-static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum;
buf_T *buf;
@@ -8096,17 +7518,17 @@ static void f_setbufline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// Set the cursor or mark position.
-/// If 'charpos' is TRUE, then use the column number as a character offset.
+/// If 'charpos' is true, then use the column number as a character offset.
/// Otherwise use the column number as a byte offset.
static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
{
- pos_T pos;
- int fnum;
colnr_T curswant = -1;
rettv->vval.v_number = -1;
const char *const name = tv_get_string_chk(argvars);
if (name != NULL) {
+ pos_T pos;
+ int fnum;
if (list2fpos(&argvars[1], &pos, &fnum, &curswant, charpos) == OK) {
if (pos.col != MAXCOL && --pos.col < 0) {
pos.col = 0;
@@ -8133,30 +7555,28 @@ static void set_position(typval_T *argvars, typval_T *rettv, bool charpos)
}
/// "setcharpos()" function
-static void f_setcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_position(argvars, rettv, true);
}
-static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- dict_T *d;
- dictitem_T *di;
-
if (argvars[0].v_type != VAR_DICT) {
emsg(_(e_dictreq));
return;
}
- if ((d = argvars[0].vval.v_dict) != NULL) {
+ dict_T *d = argvars[0].vval.v_dict;
+ if (d != NULL) {
char_u *const csearch = (char_u *)tv_dict_get_string(d, "char", false);
if (csearch != NULL) {
int pcc[MAX_MCO];
- const int c = utfc_ptr2char(csearch, pcc);
+ const int c = utfc_ptr2char((char *)csearch, pcc);
set_last_csearch(c, csearch, utfc_ptr2len((char *)csearch));
}
- di = tv_dict_find(d, S_LEN("forward"));
+ dictitem_T *di = tv_dict_find(d, S_LEN("forward"));
if (di != NULL) {
set_csearch_direction(tv_get_number(&di->di_tv) ? FORWARD : BACKWARD);
}
@@ -8168,24 +7588,14 @@ static void f_setcharsearch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
-/// "setcmdpos()" function
-static void f_setcmdpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- const int pos = (int)tv_get_number(&argvars[0]) - 1;
-
- if (pos >= 0) {
- rettv->vval.v_number = set_cmdline_pos(pos);
- }
-}
-
/// "setcursorcharpos" function
-static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setcursorcharpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_cursorpos(argvars, rettv, true);
}
/// "setenv()" function
-static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setenv(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char namebuf[NUMBUFLEN];
char valbuf[NUMBUFLEN];
@@ -8193,14 +7603,14 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type == VAR_SPECIAL
&& argvars[1].vval.v_special == kSpecialVarNull) {
- os_unsetenv(name);
+ vim_unsetenv_ext(name);
} else {
- os_setenv(name, tv_get_string_buf(&argvars[1], valbuf), 1);
+ vim_setenv_ext(name, tv_get_string_buf(&argvars[1], valbuf));
}
}
/// "setfperm({fname}, {mode})" function
-static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 0;
@@ -8231,123 +7641,23 @@ static void f_setfperm(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "setline()" function
-static void f_setline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
linenr_T lnum = tv_get_lnum(&argvars[0]);
set_buffer_lines(curbuf, lnum, false, &argvars[1], rettv);
}
-/// Create quickfix/location list from VimL values
-///
-/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
-/// args argument in which case errors out, including VAR_UNKNOWN parameters.
-///
-/// @param[in,out] wp Window to create location list for. May be NULL in
-/// which case quickfix list will be created.
-/// @param[in] args [list, action, what]
-/// @param[in] args[0] Quickfix list contents.
-/// @param[in] args[1] Optional. Action to perform:
-/// append to an existing list, replace its content,
-/// or create a new one.
-/// @param[in] args[2] Optional. Quickfix list properties or title.
-/// Defaults to caller function name.
-/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
-static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
- FUNC_ATTR_NONNULL_ARG(2, 3)
-{
- static char *e_invact = N_("E927: Invalid action: '%s'");
- const char *title = NULL;
- char action = ' ';
- static int recursive = 0;
- rettv->vval.v_number = -1;
- dict_T *what = NULL;
-
- typval_T *list_arg = &args[0];
- if (list_arg->v_type != VAR_LIST) {
- emsg(_(e_listreq));
- return;
- } else if (recursive != 0) {
- emsg(_(e_au_recursive));
- return;
- }
-
- typval_T *action_arg = &args[1];
- if (action_arg->v_type == VAR_UNKNOWN) {
- // Option argument was not given.
- goto skip_args;
- } else if (action_arg->v_type != VAR_STRING) {
- emsg(_(e_stringreq));
- return;
- }
- const char *const act = tv_get_string_chk(action_arg);
- if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
- && act[1] == NUL) {
- action = *act;
- } else {
- semsg(_(e_invact), act);
- return;
- }
-
- typval_T *const what_arg = &args[2];
- if (what_arg->v_type == VAR_UNKNOWN) {
- // Option argument was not given.
- goto skip_args;
- } else if (what_arg->v_type == VAR_STRING) {
- title = tv_get_string_chk(what_arg);
- if (!title) {
- // Type error. Error already printed by tv_get_string_chk().
- return;
- }
- } else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
- what = what_arg->vval.v_dict;
- } else {
- emsg(_(e_dictreq));
- return;
- }
-
-skip_args:
- if (!title) {
- title = (wp ? ":setloclist()" : ":setqflist()");
- }
-
- recursive++;
- list_T *const l = list_arg->vval.v_list;
- if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
- rettv->vval.v_number = 0;
- }
- recursive--;
-}
-
-/// "setloclist()" function
-static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- win_T *win;
-
- rettv->vval.v_number = -1;
-
- win = find_win_by_nr_or_id(&argvars[0]);
- if (win != NULL) {
- set_qf_ll_list(win, &argvars[1], rettv);
- }
-}
-
/// "setpos()" function
-static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
set_position(argvars, rettv, false);
}
-/// "setqflist()" function
-static void f_setqflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
-{
- set_qf_ll_list(NULL, argvars, rettv);
-}
-
/// Translate a register type string to the yank type and block length
-static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *const block_len)
+static int get_yank_type(char **const pp, MotionType *const yank_type, long *const block_len)
FUNC_ATTR_NONNULL_ALL
{
- char *stropt = (char *)(*pp);
+ char *stropt = *pp;
switch (*stropt) {
case 'v':
case 'c': // character-wise selection
@@ -8369,19 +7679,17 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c
default:
return FAIL;
}
- *pp = (char_u *)stropt;
+ *pp = stropt;
return OK;
}
/// "setreg()" function
-static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_setreg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool append = false;
- MotionType yank_type;
- long block_len;
- block_len = -1;
- yank_type = kMTUnknown;
+ long block_len = -1;
+ MotionType yank_type = kMTUnknown;
rettv->vval.v_number = 1; // FAIL is default.
@@ -8401,7 +7709,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (tv_dict_len(d) == 0) {
// Empty dict, clear the register (like setreg(0, []))
- char_u *lstval[2] = { NULL, NULL };
+ char *lstval[2] = { NULL, NULL };
write_reg_contents_lst(regname, lstval, false, kMTUnknown, -1);
return;
}
@@ -8413,7 +7721,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *stropt = tv_dict_get_string(d, "regtype", false);
if (stropt != NULL) {
- const int ret = get_yank_type((char_u **)&stropt, &yank_type, &block_len);
+ const int ret = get_yank_type((char **)&stropt, &yank_type, &block_len);
if (ret == FAIL || *(++stropt) != NUL) {
semsg(_(e_invargval), "value");
@@ -8456,7 +7764,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
set_unnamed = true;
break;
default:
- get_yank_type((char_u **)&stropt, &yank_type, &block_len);
+ get_yank_type((char **)&stropt, &yank_type, &block_len);
}
}
}
@@ -8490,7 +7798,7 @@ static void f_setreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
});
*curval++ = NULL;
- write_reg_contents_lst(regname, (char_u **)lstval, append, yank_type, (colnr_T)block_len);
+ write_reg_contents_lst(regname, lstval, append, yank_type, (colnr_T)block_len);
free_lstval:
while (curallocval > allocval) {
@@ -8517,17 +7825,15 @@ free_lstval:
}
/// "settagstack()" function
-static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_settagstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
static char *e_invact2 = N_("E962: Invalid action: '%s'");
- win_T *wp;
- dict_T *d;
char action = 'r';
rettv->vval.v_number = -1;
// first argument: window number or id
- wp = find_win_by_nr_or_id(&argvars[0]);
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL) {
return;
}
@@ -8537,7 +7843,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
emsg(_(e_dictreq));
return;
}
- d = argvars[1].vval.v_dict;
+ dict_T *d = argvars[1].vval.v_dict;
if (d == NULL) {
return;
}
@@ -8570,7 +7876,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// f_sha256 - sha256({string}) function
-static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_sha256(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *p = tv_get_string(&argvars[0]);
const char *hash = sha256_bytes((const uint8_t *)p, strlen(p), NULL, 0);
@@ -8581,7 +7887,7 @@ static void f_sha256(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "shellescape({string})" function
-static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_shellescape(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const bool do_special = non_zero_arg(&argvars[1]);
@@ -8592,14 +7898,12 @@ static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// shiftwidth() function
-static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 0;
if (argvars[0].v_type != VAR_UNKNOWN) {
- long col;
-
- col = (long)tv_get_number_chk(argvars, NULL);
+ long col = (long)tv_get_number_chk(argvars, NULL);
if (col < 0) {
return; // type error; errmsg already given
}
@@ -8610,7 +7914,7 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "simplify()" function
-static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_simplify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const p = tv_get_string(&argvars[0]);
rettv->vval.v_string = xstrdup(p);
@@ -8619,7 +7923,7 @@ static void f_simplify(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sockconnect()" function
-static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_sockconnect(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) {
emsg(_(e_invarg));
@@ -8671,17 +7975,16 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "stdioopen()" function
-static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_stdioopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
emsg(_(e_invarg));
return;
}
- bool rpc = false;
CallbackReader on_stdin = CALLBACK_READER_INIT;
dict_T *opts = argvars[0].vval.v_dict;
- rpc = tv_dict_get_number(opts, "rpc") != 0;
+ bool rpc = tv_dict_get_number(opts, "rpc") != 0;
if (!tv_dict_get_callback(opts, S_LEN("on_stdin"), &on_stdin.cb)) {
return;
@@ -8706,7 +8009,7 @@ static void f_stdioopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "reltimefloat()" function
-static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_reltimefloat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
FUNC_ATTR_NONNULL_ALL
{
proftime_T tm;
@@ -8719,7 +8022,7 @@ static void f_reltimefloat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "soundfold({word})" function
-static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_soundfold(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
const char *const s = tv_get_string(&argvars[0]);
@@ -8727,11 +8030,8 @@ static void f_soundfold(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "spellbadword()" function
-static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_spellbadword(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- const char *word = "";
- hlf_T attr = HLF_COUNT;
- size_t len = 0;
const int wo_spell_save = curwin->w_p_spell;
if (!curwin->w_p_spell) {
@@ -8745,6 +8045,9 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ const char *word = "";
+ hlf_T attr = HLF_COUNT;
+ size_t len = 0;
if (argvars[0].v_type == VAR_UNKNOWN) {
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, true, true, &attr);
@@ -8776,22 +8079,16 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_list_alloc_ret(rettv, 2);
tv_list_append_string(rettv->vval.v_list, word, (ssize_t)len);
tv_list_append_string(rettv->vval.v_list,
- (attr == HLF_SPB ? "bad"
- : attr == HLF_SPR ? "rare"
- : attr == HLF_SPL ? "local"
- : attr ==
- HLF_SPC ? "caps"
- :
- NULL), -1);
+ (attr == HLF_SPB ? "bad" :
+ attr == HLF_SPR ? "rare" :
+ attr == HLF_SPL ? "local" :
+ attr == HLF_SPC ? "caps" : NULL), -1);
}
/// "spellsuggest()" function
-static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_spellsuggest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- bool typeerr = false;
- int maxcount;
garray_T ga = GA_EMPTY_INIT_VALUE;
- bool need_capital = false;
const int wo_spell_save = curwin->w_p_spell;
if (!curwin->w_p_spell) {
@@ -8805,8 +8102,11 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ int maxcount;
+ bool need_capital = false;
const char *const str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN) {
+ bool typeerr = false;
maxcount = (int)tv_get_number_chk(&argvars[1], &typeerr);
if (maxcount <= 0) {
goto f_spellsuggest_return;
@@ -8833,17 +8133,15 @@ f_spellsuggest_return:
curwin->w_p_spell = wo_spell_save;
}
-static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_split(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *save_cpo;
- int match;
colnr_T col = 0;
bool keepempty = false;
bool typeerr = false;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
- save_cpo = p_cpo;
- p_cpo = "";
+ char *save_cpo = p_cpo;
+ p_cpo = empty_option;
const char *str = tv_get_string(&argvars[0]);
const char *pat = NULL;
@@ -8875,6 +8173,7 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
};
if (regmatch.regprog != NULL) {
while (*str != NUL || keepempty) {
+ bool match;
if (*str == NUL) {
match = false; // Empty item at the end.
} else {
@@ -8913,7 +8212,7 @@ theend:
}
/// "stdpath(type)" function
-static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_stdpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
@@ -8945,7 +8244,7 @@ static void f_stdpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "str2float()" function
-static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char *p = skipwhite(tv_get_string(&argvars[0]));
bool isneg = (*p == '-');
@@ -8961,7 +8260,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "str2list()" function
-static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_str2list(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenUnknown);
const char_u *p = (const char_u *)tv_get_string(&argvars[0]);
@@ -8972,10 +8271,9 @@ static void f_str2list(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "str2nr()" function
-static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_str2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int base = 10;
- varnumber_T n;
int what = 0;
if (argvars[1].v_type != VAR_UNKNOWN) {
@@ -9005,6 +8303,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
what |= STR2NR_HEX | STR2NR_FORCE;
break;
}
+ varnumber_T n;
vim_str2nr(p, NULL, NULL, what, &n, NULL, 0, false);
// Text after the number is silently ignored.
if (isneg) {
@@ -9015,7 +8314,7 @@ static void f_str2nr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strftime({format}[, {time}])" function
-static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
time_t seconds;
@@ -9035,13 +8334,12 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = xstrdup(_("(Invalid)"));
} else {
vimconv_T conv;
- char_u *enc;
conv.vc_type = CONV_NONE;
- enc = enc_locale();
+ char *enc = (char *)enc_locale();
convert_setup(&conv, p_enc, enc);
if (conv.vc_type != CONV_NONE) {
- p = (char *)string_convert(&conv, (char_u *)p, NULL);
+ p = string_convert(&conv, p, NULL);
}
char result_buf[256];
if (p != NULL) {
@@ -9055,7 +8353,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
convert_setup(&conv, enc, p_enc);
if (conv.vc_type != CONV_NONE) {
- rettv->vval.v_string = (char *)string_convert(&conv, (char_u *)result_buf, NULL);
+ rettv->vval.v_string = string_convert(&conv, result_buf, NULL);
} else {
rettv->vval.v_string = xstrdup(result_buf);
}
@@ -9067,7 +8365,7 @@ static void f_strftime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strgetchar()" function
-static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strgetchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -9095,7 +8393,7 @@ static void f_strgetchar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "stridx()" function
-static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_stridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -9127,20 +8425,20 @@ static void f_stridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "string()" function
-static void f_string(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_string(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = encode_tv2string(&argvars[0], NULL);
}
/// "strlen()" function
-static void f_strlen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strlen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = (varnumber_T)strlen(tv_get_string(&argvars[0]));
}
/// "strchars()" function
-static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strchars(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *s = tv_get_string(&argvars[0]);
int skipcc = 0;
@@ -9163,7 +8461,7 @@ static void f_strchars(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strdisplaywidth()" function
-static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const s = tv_get_string(&argvars[0]);
int col = 0;
@@ -9172,11 +8470,11 @@ static void f_strdisplaywidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
col = (int)tv_get_number(&argvars[1]);
}
- rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char_u *)s) - col);
+ rettv->vval.v_number = (varnumber_T)(linetabsize_col(col, (char *)s) - col);
}
/// "strwidth()" function
-static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const s = tv_get_string(&argvars[0]);
@@ -9184,7 +8482,7 @@ static void f_strwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strcharpart()" function
-static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strcharpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const p = tv_get_string(&argvars[0]);
const size_t slen = STRLEN(p);
@@ -9238,7 +8536,7 @@ static void f_strcharpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strpart()" function
-static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strpart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool error = false;
@@ -9284,7 +8582,7 @@ static void f_strpart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strptime({format}, {timestring})" function
-static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strptime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char fmt_buf[NUMBUFLEN];
char str_buf[NUMBUFLEN];
@@ -9298,10 +8596,10 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
vimconv_T conv = {
.vc_type = CONV_NONE,
};
- char_u *enc = enc_locale();
+ char *enc = (char *)enc_locale();
convert_setup(&conv, p_enc, enc);
if (conv.vc_type != CONV_NONE) {
- fmt = (char *)string_convert(&conv, (char_u *)fmt, NULL);
+ fmt = string_convert(&conv, fmt, NULL);
}
if (fmt == NULL
|| os_strptime(str, fmt, &tmval) == NULL
@@ -9316,7 +8614,7 @@ static void f_strptime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strridx()" function
-static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strridx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
const char *const needle = tv_get_string_chk(&argvars[1]);
@@ -9359,14 +8657,14 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "strtrans()" function
-static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = transstr(tv_get_string(&argvars[0]), true);
}
/// "submatch()" function
-static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_submatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
bool error = false;
int no = (int)tv_get_number_chk(&argvars[0], &error);
@@ -9397,7 +8695,7 @@ static void f_submatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "substitute()" function
-static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_substitute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char patbuf[NUMBUFLEN];
char subbuf[NUMBUFLEN];
@@ -9426,14 +8724,14 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "swapinfo(swap_filename)" function
-static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_swapinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict);
}
/// "swapname(expr)" function
-static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_swapname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
buf_T *buf = tv_get_buf(&argvars[0], false);
@@ -9447,7 +8745,7 @@ static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synID(lnum, col, trans)" function
-static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synID(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// -1 on type error (both)
const linenr_T lnum = tv_get_lnum(argvars);
@@ -9466,7 +8764,7 @@ static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synIDattr(id, what [, mode])" function
-static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synIDattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const int id = (int)tv_get_number(&argvars[0]);
const char *const what = tv_get_string(&argvars[1]);
@@ -9553,7 +8851,7 @@ static void f_synIDattr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synIDtrans(id)" function
-static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int id = (int)tv_get_number(&argvars[0]);
@@ -9567,7 +8865,7 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synconcealed(lnum, col)" function
-static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int syntax_flags = 0;
int cchar;
@@ -9580,7 +8878,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const linenr_T lnum = tv_get_lnum(argvars);
const colnr_T col = (colnr_T)tv_get_number(&argvars[1]) - 1;
- memset(str, NUL, sizeof(str));
+ CLEAR_FIELD(str);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
@@ -9609,7 +8907,7 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "synstack(lnum, col)" function
-static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_set_ret(rettv, NULL);
@@ -9633,18 +8931,18 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// f_system - the VimL system() function
-static void f_system(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_system(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_system_output_as_rettv(argvars, rettv, false);
}
-static void f_systemlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_systemlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_system_output_as_rettv(argvars, rettv, true);
}
/// "tabpagebuflist()" function
-static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = NULL;
@@ -9666,7 +8964,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "tabpagenr()" function
-static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tabpagenr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int nr = 1;
@@ -9691,11 +8989,9 @@ static void f_tabpagenr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// Common code for tabpagewinnr() and winnr().
static int get_winnr(tabpage_T *tp, typval_T *argvar)
{
- win_T *twin;
int nr = 1;
- win_T *wp;
- twin = (tp == curtab) ? curwin : tp->tp_curwin;
+ win_T *twin = (tp == curtab) ? curwin : tp->tp_curwin;
if (argvar->v_type != VAR_UNKNOWN) {
bool invalid_arg = false;
const char *const arg = tv_get_string_chk(argvar);
@@ -9710,20 +9006,20 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
}
} else {
// Extract the window count (if specified). e.g. winnr('3j')
- char_u *endp;
- long count = strtol((char *)arg, (char **)&endp, 10);
+ char *endp;
+ long count = strtol((char *)arg, &endp, 10);
if (count <= 0) {
// if count is not specified, default to 1
count = 1;
}
if (endp != NULL && *endp != '\0') {
- if (strequal((char *)endp, "j")) {
+ if (strequal(endp, "j")) {
twin = win_vert_neighbor(tp, twin, false, count);
- } else if (strequal((char *)endp, "k")) {
+ } else if (strequal(endp, "k")) {
twin = win_vert_neighbor(tp, twin, true, count);
- } else if (strequal((char *)endp, "h")) {
+ } else if (strequal(endp, "h")) {
twin = win_horz_neighbor(tp, twin, true, count);
- } else if (strequal((char *)endp, "l")) {
+ } else if (strequal(endp, "l")) {
twin = win_horz_neighbor(tp, twin, false, count);
} else {
invalid_arg = true;
@@ -9740,21 +9036,21 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar)
}
if (nr > 0) {
- for (wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
+ for (win_T *wp = (tp == curtab) ? firstwin : tp->tp_firstwin;
wp != twin; wp = wp->w_next) {
if (wp == NULL) {
// didn't find it in this tabpage
nr = 0;
break;
}
- ++nr;
+ nr++;
}
}
return nr;
}
/// "tabpagewinnr()" function
-static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int nr = 1;
tabpage_T *const tp = find_tabpage((int)tv_get_number(&argvars[0]));
@@ -9767,16 +9063,14 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "tagfiles()" function
-static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tagfiles(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *fname;
- tagname_T tn;
-
tv_list_alloc_ret(rettv, kListLenUnknown);
- fname = xmalloc(MAXPATHL);
+ char *fname = xmalloc(MAXPATHL);
bool first = true;
- while (get_tagfname(&tn, first, (char_u *)fname) == OK) {
+ tagname_T tn;
+ while (get_tagfname(&tn, first, fname) == OK) {
tv_list_append_string(rettv->vval.v_list, fname, -1);
first = false;
}
@@ -9786,7 +9080,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "taglist()" function
-static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const tag_pattern = tv_get_string(&argvars[0]);
@@ -9804,14 +9098,14 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "tempname()" function
-static void f_tempname(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tempname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = (char *)vim_tempname();
}
/// "termopen(cmd[, cwd])" function
-static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()) {
return;
@@ -9854,7 +9148,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (new_cwd && *new_cwd != NUL) {
cwd = new_cwd;
// The new cwd must be a directory.
- if (!os_isdir_executable(cwd)) {
+ if (!os_isdir(cwd)) {
semsg(_(e_invarg2), "expected valid directory");
shell_free_argv(argv);
return;
@@ -9934,7 +9228,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "timer_info([timer])" function
-static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_NUMBER) {
@@ -9952,7 +9246,7 @@ static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "timer_pause(timer, paused)" function
-static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
+static void f_timer_pause(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_NUMBER) {
emsg(_(e_number_exp));
@@ -9972,10 +9266,9 @@ static void f_timer_pause(typval_T *argvars, typval_T *unused, FunPtr fptr)
}
/// "timer_start(timeout, callback, opts)" function
-static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_timer_start(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int repeat = 1;
- dict_T *dict;
rettv->vval.v_number = -1;
if (check_secure()) {
@@ -9983,8 +9276,8 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
if (argvars[2].v_type != VAR_UNKNOWN) {
- if (argvars[2].v_type != VAR_DICT
- || (dict = argvars[2].vval.v_dict) == NULL) {
+ dict_T *dict = argvars[2].vval.v_dict;
+ if (argvars[2].v_type != VAR_DICT || dict == NULL) {
semsg(_(e_invarg2), tv_get_string(&argvars[2]));
return;
}
@@ -10005,7 +9298,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "timer_stop(timerid)" function
-static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_timer_stop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_NUMBER) {
emsg(_(e_number_exp));
@@ -10020,27 +9313,27 @@ static void f_timer_stop(typval_T *argvars, typval_T *rettv, FunPtr fptr)
timer_stop(timer);
}
-static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr)
+static void f_timer_stopall(typval_T *argvars, typval_T *unused, EvalFuncData fptr)
{
timer_stop_all();
}
/// "tolower(string)" function
-static void f_tolower(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tolower(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), false);
}
/// "toupper(string)" function
-static void f_toupper(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_toupper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = strcase_save(tv_get_string(&argvars[0]), true);
}
/// "tr(string, fromstr, tostr)" function
-static void f_tr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_tr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
char buf2[NUMBUFLEN];
@@ -10119,16 +9412,14 @@ error:
}
/// "trim({expr})" function
-static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_trim(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf1[NUMBUFLEN];
char buf2[NUMBUFLEN];
const char_u *head = (const char_u *)tv_get_string_buf_chk(&argvars[0], buf1);
const char_u *mask = NULL;
- const char_u *tail;
const char_u *prev;
const char_u *p;
- int c1;
int dir = 0;
rettv->v_type = VAR_STRING;
@@ -10137,6 +9428,11 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return;
}
+ if (argvars[1].v_type != VAR_UNKNOWN && argvars[1].v_type != VAR_STRING) {
+ semsg(_(e_invarg2), tv_get_string(&argvars[1]));
+ return;
+ }
+
if (argvars[1].v_type == VAR_STRING) {
mask = (const char_u *)tv_get_string_buf_chk(&argvars[1], buf2);
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -10153,6 +9449,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
+ int c1;
if (dir == 0 || dir == 1) {
// Trim leading characters
while (*head != NUL) {
@@ -10175,7 +9472,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
- tail = head + STRLEN(head);
+ const char_u *tail = head + STRLEN(head);
if (dir == 0 || dir == 2) {
// Trim trailing characters
for (; tail > head; tail = prev) {
@@ -10202,7 +9499,7 @@ static void f_trim(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "type(expr)" function
-static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_type(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int n = -1;
@@ -10234,7 +9531,7 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "undofile(name)" function
-static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
const char *const fname = tv_get_string(&argvars[0]);
@@ -10253,7 +9550,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "undotree()" function
-static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -10271,13 +9568,12 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "virtcol(string)" function
-static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_virtcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
colnr_T vcol = 0;
- pos_T *fp;
int fnum = curbuf->b_fnum;
- fp = var2fpos(&argvars[0], false, &fnum, false);
+ pos_T *fp = var2fpos(&argvars[0], false, &fnum, false);
if (fp != NULL && fp->lnum <= curbuf->b_ml.ml_line_count
&& fnum == curbuf->b_fnum) {
// Limit the column to a valid value, getvvcol() doesn't check.
@@ -10290,14 +9586,14 @@ static void f_virtcol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
getvvcol(curwin, fp, NULL, NULL, &vcol);
- ++vcol;
+ vcol++;
}
rettv->vval.v_number = vcol;
}
/// "visualmode()" function
-static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_visualmode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char_u str[2];
@@ -10313,28 +9609,28 @@ static void f_visualmode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "wildmenumode()" function
-static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wildmenumode(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (wild_menu_showing || ((State & MODE_CMDLINE) && pum_visible())) {
+ if (wild_menu_showing || ((State & MODE_CMDLINE) && cmdline_pum_active())) {
rettv->vval.v_number = 1;
}
}
/// "win_findbuf()" function
-static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_findbuf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_list_alloc_ret(rettv, kListLenMayKnow);
win_findbuf(argvars, rettv->vval.v_list);
}
/// "win_getid()" function
-static void f_win_getid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_getid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = win_getid(argvars);
}
/// "win_gettype(nr)" function
-static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_gettype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = curwin;
@@ -10361,43 +9657,52 @@ static void f_win_gettype(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "win_gotoid()" function
-static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_gotoid(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- rettv->vval.v_number = win_gotoid(argvars);
+ int id = (int)tv_get_number(&argvars[0]);
+
+ if (cmdwin_type != 0) {
+ emsg(_(e_cmdwin));
+ return;
+ }
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (wp->handle == id) {
+ goto_tabpage_win(tp, wp);
+ rettv->vval.v_number = 1;
+ return;
+ }
+ }
}
/// "win_id2tabwin()" function
-static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_id2tabwin(argvars, rettv);
}
/// "win_id2win()" function
-static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_id2win(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = win_id2win(argvars);
}
/// "win_move_separator()" function
-static void f_win_move_separator(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_move_separator(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- win_T *wp;
- int offset;
-
rettv->vval.v_number = false;
- wp = find_win_by_nr_or_id(&argvars[0]);
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL || wp->w_floating) {
return;
}
- offset = (int)tv_get_number(&argvars[1]);
+ int offset = (int)tv_get_number(&argvars[1]);
win_drag_vsep_line(wp, offset);
rettv->vval.v_number = true;
}
/// "win_move_statusline()" function
-static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp;
int offset;
@@ -10415,7 +9720,7 @@ static void f_win_move_statusline(typval_T *argvars, typval_T *rettv, FunPtr fpt
}
/// "winbufnr(nr)" function
-static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winbufnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL) {
@@ -10426,25 +9731,25 @@ static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "wincol()" function
-static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wincol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
validate_cursor();
rettv->vval.v_number = curwin->w_wcol + 1;
}
/// "winheight(nr)" function
-static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winheight(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL) {
rettv->vval.v_number = -1;
} else {
- rettv->vval.v_number = wp->w_height;
+ rettv->vval.v_number = wp->w_height_inner;
}
}
/// "winlayout()" function
-static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winlayout(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tabpage_T *tp;
@@ -10463,27 +9768,24 @@ static void f_winlayout(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "winline()" function
-static void f_winline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
validate_cursor();
rettv->vval.v_number = curwin->w_wrow + 1;
}
/// "winnr()" function
-static void f_winnr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winnr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int nr = 1;
-
- nr = get_winnr(curtab, &argvars[0]);
- rettv->vval.v_number = nr;
+ rettv->vval.v_number = get_winnr(curtab, &argvars[0]);
}
/// "winrestcmd()" function
-static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winrestcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- garray_T ga;
char_u buf[50];
+ garray_T ga;
ga_init(&ga, (int)sizeof(char), 70);
// Do this twice to handle some window layouts properly.
@@ -10506,12 +9808,11 @@ static void f_winrestcmd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "winrestview()" function
-static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winrestview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- dict_T *dict;
+ dict_T *dict = argvars[0].vval.v_dict;
- if (argvars[0].v_type != VAR_DICT
- || (dict = argvars[0].vval.v_dict) == NULL) {
+ if (argvars[0].v_type != VAR_DICT || dict == NULL) {
emsg(_(e_invarg));
} else {
dictitem_T *di;
@@ -10557,12 +9858,10 @@ static void f_winrestview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "winsaveview()" function
-static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winsaveview(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- dict_T *dict;
-
tv_dict_alloc_ret(rettv);
- dict = rettv->vval.v_dict;
+ dict_T *dict = rettv->vval.v_dict;
tv_dict_add_nr(dict, S_LEN("lnum"), (varnumber_T)curwin->w_cursor.lnum);
tv_dict_add_nr(dict, S_LEN("col"), (varnumber_T)curwin->w_cursor.col);
@@ -10577,32 +9876,32 @@ static void f_winsaveview(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "winwidth(nr)" function
-static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_winwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *wp = find_win_by_nr_or_id(&argvars[0]);
if (wp == NULL) {
rettv->vval.v_number = -1;
} else {
- rettv->vval.v_number = wp->w_width;
+ rettv->vval.v_number = wp->w_width_inner;
}
}
/// "windowsversion()" function
-static void f_windowsversion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_windowsversion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_STRING;
rettv->vval.v_string = xstrdup(windowsVersion);
}
/// "wordcount()" function
-static void f_wordcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_wordcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
cursor_pos_info(rettv->vval.v_dict);
}
/// "writefile()" function
-static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -10680,7 +9979,7 @@ static void f_writefile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "xor(expr, expr)" function
-static void f_xor(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+static void f_xor(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = tv_get_number_chk(&argvars[0], NULL)
^ tv_get_number_chk(&argvars[1], NULL);
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index 583ee0e75e..adff0b2441 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -1,11 +1,12 @@
#ifndef NVIM_EVAL_FUNCS_H
#define NVIM_EVAL_FUNCS_H
+#include "nvim/api/private/dispatch.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
/// Prototype of C function that implements VimL function
-typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
+typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, EvalFuncData data);
/// Special flags for base_arg @see EvalFuncDef
#define BASE_NONE 0 ///< Not a method (no base argument).
@@ -13,13 +14,13 @@ typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
/// Structure holding VimL function definition
typedef struct {
- char *name; ///< Name of the function.
- uint8_t min_argc; ///< Minimal number of arguments.
- uint8_t max_argc; ///< Maximal number of arguments.
- uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST.
- bool fast; ///< Can be run in |api-fast| events
- VimLFunc func; ///< Function implementation.
- FunPtr data; ///< Userdata for function implementation.
+ char *name; ///< Name of the function.
+ uint8_t min_argc; ///< Minimal number of arguments.
+ uint8_t max_argc; ///< Maximal number of arguments.
+ uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST.
+ bool fast; ///< Can be run in |api-fast| events
+ VimLFunc func; ///< Function implementation.
+ EvalFuncData data; ///< Userdata for function implementation.
} EvalFuncDef;
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index ff1808ed91..d5b2b1f2ae 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -233,7 +233,7 @@ void tv_list_init_static10(staticList10_T *const sl)
#define SL_SIZE ARRAY_SIZE(sl->sl_items)
list_T *const l = &sl->sl_list;
- memset(sl, 0, sizeof(staticList10_T));
+ CLEAR_POINTER(sl);
l->lv_first = &sl->sl_items[0];
l->lv_last = &sl->sl_items[SL_SIZE - 1];
l->lv_refcount = DO_NOT_FREE_CNT;
@@ -261,7 +261,7 @@ void tv_list_init_static10(staticList10_T *const sl)
void tv_list_init_static(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
- memset(l, 0, sizeof(*l));
+ CLEAR_POINTER(l);
l->lv_refcount = DO_NOT_FREE_CNT;
list_log(l, NULL, NULL, "sinit");
}
@@ -831,7 +831,7 @@ int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep)
}
/// "join()" function
-void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_join(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_LIST) {
emsg(_(e_listreq));
@@ -855,7 +855,7 @@ void f_join(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "list2str()" function
-void f_list2str(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_list2str(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga;
@@ -1247,15 +1247,15 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
; li != NULL;) {
listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
if (item_compare_func_ptr(&prev_li, &li) == 0) {
- if (info.item_compare_func_err) { // -V547
- emsg(_("E882: Uniq compare function failed"));
- break;
- }
li = tv_list_item_remove(l, li);
} else {
idx++;
li = TV_LIST_ITEM_NEXT(l, li);
}
+ if (info.item_compare_func_err) { // -V547
+ emsg(_("E882: Uniq compare function failed"));
+ break;
+ }
}
}
@@ -1267,13 +1267,13 @@ theend:
}
/// "sort"({list})" function
-void f_sort(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sort(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, true);
}
/// "uniq({list})" function
-void f_uniq(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_uniq(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_sort_uniq(argvars, rettv, false);
}
@@ -2533,7 +2533,7 @@ dict_T *tv_dict_copy(const vimconv_T *const conv, dict_T *const orig, const bool
new_di = tv_dict_item_alloc((const char *)di->di_key);
} else {
size_t len = STRLEN(di->di_key);
- char *const key = (char *)string_convert(conv, di->di_key, &len);
+ char *const key = (char *)string_convert(conv, (char *)di->di_key, &len);
if (key == NULL) {
new_di = tv_dict_item_alloc_len((const char *)di->di_key, len);
} else {
@@ -2806,25 +2806,25 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi
}
/// "items(dict)" function
-void f_items(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_items(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_list(argvars, rettv, 2);
}
/// "keys()" function
-void f_keys(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_keys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_list(argvars, rettv, 0);
}
/// "values(dict)" function
-void f_values(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_values(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_list(argvars, rettv, 1);
}
/// "has_key()" function
-void f_has_key(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_has_key(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (argvars[0].v_type != VAR_DICT) {
emsg(_(e_dictreq));
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index c02351947b..8177d01f90 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -13,10 +13,9 @@
#include "nvim/hashtab.h"
#include "nvim/lib/queue.h"
#include "nvim/macros.h"
-#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/message.h"
#include "nvim/pos.h" // for linenr_T
-#include "nvim/profile.h" // for proftime_T
#include "nvim/types.h"
#ifdef LOG_LIST_ACTIONS
# include "nvim/memory.h"
@@ -26,9 +25,6 @@
typedef int64_t varnumber_T;
typedef uint64_t uvarnumber_T;
-/// Type used for VimL VAR_FLOAT values
-typedef double float_T;
-
/// Refcount for dict or list that should not be freed
enum { DO_NOT_FREE_CNT = (INT_MAX / 2), };
@@ -356,6 +352,8 @@ struct ufunc {
///< used for s: variables
int uf_refcount; ///< reference count, see func_name_refcount()
funccall_T *uf_scoped; ///< l: local variables for closure
+ char_u *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
+ ///< "<SNR>" as a string, otherwise NULL
char_u uf_name[]; ///< Name of function (actual size equals name);
///< can start with <SNR>123_
///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 2f4799db57..4be922a055 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -12,8 +12,8 @@
#include "nvim/eval/funcs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
@@ -21,7 +21,9 @@
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -44,7 +46,7 @@
# include "eval/userfunc.c.generated.h"
#endif
-hashtab_T func_hashtab;
+static hashtab_T func_hashtab;
// Used by get_func_tv()
static garray_T funcargs = GA_EMPTY_INIT_VALUE;
@@ -66,13 +68,19 @@ void func_init(void)
hash_init(&func_hashtab);
}
+/// Return the function hash table
+hashtab_T *func_tbl_get(void)
+{
+ return &func_hashtab;
+}
+
/// Get function arguments.
-static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, int *varargs,
+static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int *varargs,
garray_T *default_args, bool skip)
{
bool mustend = false;
- char_u *arg = *argp;
- char_u *p = arg;
+ char *arg = *argp;
+ char *p = arg;
char_u c;
int i;
@@ -89,7 +97,7 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, i
// Isolate the arguments: "arg1, arg2, ...)"
bool any_default = false;
- while (*p != endchar) {
+ while (*p != (char)endchar) {
if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
if (varargs != NULL) {
*varargs = true;
@@ -111,44 +119,43 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, i
}
if (newargs != NULL) {
ga_grow(newargs, 1);
- c = *p;
+ c = (char_u)(*p);
*p = NUL;
- arg = vim_strsave(arg);
+ arg = xstrdup(arg);
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; i++) {
- if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0) {
+ if (STRCMP(((char **)(newargs->ga_data))[i], arg) == 0) {
semsg(_("E853: Duplicate argument name: %s"), arg);
xfree(arg);
goto err_ret;
}
}
- ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg;
+ ((char **)(newargs->ga_data))[newargs->ga_len] = arg;
newargs->ga_len++;
- *p = c;
+ *p = (char)c;
}
- if (*skipwhite((char *)p) == '=' && default_args != NULL) {
+ if (*skipwhite(p) == '=' && default_args != NULL) {
typval_T rettv;
any_default = true;
- p = (char_u *)skipwhite((char *)p) + 1;
- p = (char_u *)skipwhite((char *)p);
- char_u *expr = p;
- if (eval1((char **)&p, &rettv, false) != FAIL) {
+ p = skipwhite(p) + 1;
+ p = skipwhite(p);
+ char_u *expr = (char_u *)p;
+ if (eval1(&p, &rettv, false) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
- while (p > expr && ascii_iswhite(p[-1])) {
+ while (p > (char *)expr && ascii_iswhite(p[-1])) {
p--;
}
- c = *p;
+ c = (char_u)(*p);
*p = NUL;
expr = vim_strsave(expr);
- ((char_u **)(default_args->ga_data))
- [default_args->ga_len] = expr;
+ ((char **)(default_args->ga_data))[default_args->ga_len] = (char *)expr;
default_args->ga_len++;
- *p = c;
+ *p = (char)c;
} else {
mustend = true;
}
@@ -162,15 +169,15 @@ static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, i
mustend = true;
}
}
- p = (char_u *)skipwhite((char *)p);
- if (mustend && *p != endchar) {
+ p = skipwhite(p);
+ if (mustend && *p != (char)endchar) {
if (!skip) {
semsg(_(e_invarg2), *argp);
}
break;
}
}
- if (*p != endchar) {
+ if (*p != (char)endchar) {
goto err_ret;
}
p++; // skip "endchar"
@@ -213,10 +220,21 @@ char_u *get_lambda_name(void)
return name;
}
+static void set_ufunc_name(ufunc_T *fp, char_u *name)
+{
+ STRCPY(fp->uf_name, name);
+
+ if (name[0] == K_SPECIAL) {
+ fp->uf_name_exp = xmalloc(STRLEN(name) + 3);
+ STRCPY(fp->uf_name_exp, "<SNR>");
+ STRCAT(fp->uf_name_exp, fp->uf_name + 3);
+ }
+}
+
/// Parse a lambda expression and get a Funcref from "*arg".
///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
-int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
+int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
{
garray_T newargs = GA_EMPTY_INIT_VALUE;
garray_T *pnewargs;
@@ -224,13 +242,13 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
partial_T *pt = NULL;
int varargs;
int ret;
- char_u *start = (char_u *)skipwhite((char *)(*arg) + 1);
+ char_u *start = (char_u *)skipwhite(*arg + 1);
char_u *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
+ ret = get_function_args((char **)&start, '-', NULL, NULL, NULL, true);
if (ret == FAIL || *start != '>') {
return NOTDONE;
}
@@ -241,7 +259,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
} else {
pnewargs = NULL;
}
- *arg = (char_u *)skipwhite((char *)(*arg) + 1);
+ *arg = skipwhite(*arg + 1);
ret = get_function_args(arg, '-', pnewargs, &varargs, NULL, false);
if (ret == FAIL || **arg != '>') {
goto errret;
@@ -253,14 +271,14 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
}
// Get the start and the end of the expression.
- *arg = (char_u *)skipwhite((char *)(*arg) + 1);
- s = *arg;
- ret = skip_expr((char **)arg);
+ *arg = skipwhite((*arg) + 1);
+ s = (char_u *)(*arg);
+ ret = skip_expr(arg);
if (ret == FAIL) {
goto errret;
}
- e = *arg;
- *arg = (char_u *)skipwhite((char *)(*arg));
+ e = (char_u *)(*arg);
+ *arg = skipwhite(*arg);
if (**arg != '}') {
goto errret;
}
@@ -282,7 +300,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
// Add "return " before the expression.
size_t len = (size_t)(7 + e - s + 1);
p = (char_u *)xmalloc(len);
- ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
STRCPY(p, "return ");
STRLCPY(p + 7, s, e - s + 1);
if (strstr((char *)p + 7, "a:") == NULL) {
@@ -291,7 +309,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
}
fp->uf_refcount = 1;
- STRCPY(fp->uf_name, name);
+ set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1);
@@ -313,7 +331,7 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ctx = current_sctx;
- fp->uf_script_ctx.sc_lnum += sourcing_lnum - newlines.ga_len;
+ fp->uf_script_ctx.sc_lnum += SOURCING_LNUM - newlines.ga_len;
pt->pt_func = fp;
pt->pt_refcount = 1;
@@ -391,7 +409,7 @@ void emsg_funcname(char *ermsg, const char_u *name)
char_u *p;
if (*name == K_SPECIAL) {
- p = concat_str((char_u *)"<SNR>", name + 3);
+ p = (char_u *)concat_str("<SNR>", (char *)name + 3);
} else {
p = (char_u *)name;
}
@@ -411,34 +429,32 @@ void emsg_funcname(char *ermsg, const char_u *name)
/// @param funcexe various values
///
/// @return OK or FAIL.
-int get_func_tv(const char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe)
+int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
{
- char_u *argp;
+ char *argp;
int ret = OK;
typval_T argvars[MAX_FUNC_ARGS + 1]; // vars for arguments
int argcount = 0; // number of arguments found
- /*
- * Get the arguments.
- */
+ // Get the arguments.
argp = *arg;
while (argcount < MAX_FUNC_ARGS
- (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
- argp = (char_u *)skipwhite((char *)argp + 1); // skip the '(' or ','
+ argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1((char **)&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
ret = FAIL;
break;
}
- ++argcount;
+ argcount++;
if (*argp != ',') {
break;
}
}
if (*argp == ')') {
- ++argp;
+ argp++;
} else {
ret = FAIL;
}
@@ -472,7 +488,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char_u **arg, func
tv_clear(&argvars[argcount]);
}
- *arg = (char_u *)skipwhite((char *)argp);
+ *arg = skipwhite(argp);
return ret;
}
@@ -740,6 +756,7 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_def_args));
ga_clear_strings(&(fp->uf_lines));
+ XFREE_CLEAR(fp->uf_name_exp);
if (fp->uf_cb_free != NULL) {
fp->uf_cb_free(fp->uf_cb_state);
@@ -803,8 +820,6 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
linenr_T firstline, linenr_T lastline, dict_T *selfdict)
FUNC_ATTR_NONNULL_ARG(1, 3, 4)
{
- char_u *save_sourcing_name;
- linenr_T save_sourcing_lnum;
bool using_sandbox = false;
funccall_T *fc;
int save_did_emsg;
@@ -814,7 +829,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
int ai;
bool islambda = false;
char_u numbuf[NUMBUFLEN];
- char_u *name;
+ char *name;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
proftime_T wait_start;
@@ -830,14 +845,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
rettv->vval.v_number = -1;
return;
}
- ++depth;
+ depth++;
// Save search patterns and redo buffer.
save_search_patterns();
if (!ins_compl_active()) {
saveRedobuff(&save_redo);
did_save_redo = true;
}
- ++fp->uf_calls;
+ fp->uf_calls++;
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
@@ -848,7 +863,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
fc->rettv = rettv;
fc->level = ex_nesting_level;
// Check if this function has a breakpoint.
- fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0);
+ fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
// Set up fields for closure.
@@ -870,7 +885,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// some compiler that checks the destination size.
v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
#ifndef __clang_analyzer__
- name = v->di_key;
+ name = (char *)v->di_key;
STRCPY(name, "self");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
@@ -878,7 +893,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
v->di_tv.v_type = VAR_DICT;
v->di_tv.v_lock = VAR_UNLOCKED;
v->di_tv.vval.v_dict = selfdict;
- ++selfdict->dv_refcount;
+ selfdict->dv_refcount++;
}
// Init a: variables, unless none found (in lambda).
@@ -896,7 +911,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// destination size.
v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
#ifndef __clang_analyzer__
- name = v->di_key;
+ name = (char *)v->di_key;
STRCPY(name, "000");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
@@ -935,13 +950,13 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// evaluate named argument default expression
isdefault = ai + fp->uf_def_args.ga_len >= 0 && i >= argcount;
if (isdefault) {
- char_u *default_expr = NULL;
+ char *default_expr = NULL;
def_rettv.v_type = VAR_NUMBER;
def_rettv.vval.v_number = -1;
- default_expr = ((char_u **)(fp->uf_def_args.ga_data))
+ default_expr = ((char **)(fp->uf_def_args.ga_data))
[ai + fp->uf_def_args.ga_len];
- if (eval1((char **)&default_expr, &def_rettv, true) == FAIL) {
+ if (eval1(&default_expr, &def_rettv, true) == FAIL) {
default_arg_err = true;
break;
}
@@ -953,7 +968,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
}
// "..." argument a:1, a:2, etc.
snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
- name = numbuf;
+ name = (char *)numbuf;
}
if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
@@ -994,80 +1009,56 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Don't redraw while executing the function.
RedrawingDisabled++;
- save_sourcing_name = (char_u *)sourcing_name;
- save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 1;
if (fp->uf_flags & FC_SANDBOX) {
using_sandbox = true;
sandbox++;
}
- // need space for new sourcing_name:
- // * save_sourcing_name
- // * "["number"].." or "function "
- // * "<SNR>" + fp->uf_name - 3
- // * terminating NUL
- size_t len = (save_sourcing_name == NULL ? 0 : STRLEN(save_sourcing_name))
- + STRLEN(fp->uf_name) + 27;
- sourcing_name = xmalloc(len);
- {
- if (save_sourcing_name != NULL
- && STRNCMP(save_sourcing_name, "function ", 9) == 0) {
- vim_snprintf(sourcing_name,
- len,
- "%s[%" PRId64 "]..",
- save_sourcing_name,
- (int64_t)save_sourcing_lnum);
- } else {
- STRCPY(sourcing_name, "function ");
- }
- cat_func_name((char_u *)sourcing_name + STRLEN(sourcing_name), fp);
-
- if (p_verbose >= 12) {
- ++no_wait_return;
- verbose_enter_scroll();
+ estack_push_ufunc(fp, 1);
+ if (p_verbose >= 12) {
+ no_wait_return++;
+ verbose_enter_scroll();
- smsg(_("calling %s"), sourcing_name);
- if (p_verbose >= 14) {
- msg_puts("(");
- for (int i = 0; i < argcount; i++) {
- if (i > 0) {
- msg_puts(", ");
- }
- if (argvars[i].v_type == VAR_NUMBER) {
- msg_outnum((long)argvars[i].vval.v_number);
- } else {
- // Do not want errors such as E724 here.
- emsg_off++;
- char *tofree = encode_tv2string(&argvars[i], NULL);
- emsg_off--;
- if (tofree != NULL) {
- char *s = tofree;
- char buf[MSG_BUF_LEN];
- if (vim_strsize(s) > MSG_BUF_CLEN) {
- trunc_string(s, buf, MSG_BUF_CLEN, sizeof(buf));
- s = buf;
- }
- msg_puts(s);
- xfree(tofree);
+ smsg(_("calling %s"), SOURCING_NAME);
+ if (p_verbose >= 14) {
+ msg_puts("(");
+ for (int i = 0; i < argcount; i++) {
+ if (i > 0) {
+ msg_puts(", ");
+ }
+ if (argvars[i].v_type == VAR_NUMBER) {
+ msg_outnum((long)argvars[i].vval.v_number);
+ } else {
+ // Do not want errors such as E724 here.
+ emsg_off++;
+ char *tofree = encode_tv2string(&argvars[i], NULL);
+ emsg_off--;
+ if (tofree != NULL) {
+ char *s = tofree;
+ char buf[MSG_BUF_LEN];
+ if (vim_strsize(s) > MSG_BUF_CLEN) {
+ trunc_string(s, buf, MSG_BUF_CLEN, sizeof(buf));
+ s = buf;
}
+ msg_puts(s);
+ xfree(tofree);
}
}
- msg_puts(")");
}
- msg_puts("\n"); // don't overwrite this either
-
- verbose_leave_scroll();
- --no_wait_return;
+ msg_puts(")");
}
+ msg_puts("\n"); // don't overwrite this either
+
+ verbose_leave_scroll();
+ no_wait_return--;
}
const bool do_profiling_yes = do_profiling == PROF_YES;
bool func_not_yet_profiling_but_should =
do_profiling_yes
- && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);
+ && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL);
if (func_not_yet_profiling_but_should) {
started_profiling = true;
@@ -1080,7 +1071,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
|| (fc->caller != NULL && fc->caller->func->uf_profiling));
if (func_or_func_caller_profiling) {
- ++fp->uf_tm_count;
+ fp->uf_tm_count++;
call_start = profile_start();
fp->uf_tm_children = profile_zero();
}
@@ -1092,17 +1083,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
const sctx_T save_current_sctx = current_sctx;
current_sctx = fp->uf_script_ctx;
save_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
if (default_arg_err && (fp->uf_flags & FC_ABORT)) {
did_emsg = true;
} else if (islambda) {
- char_u *p = *(char_u **)fp->uf_lines.ga_data + 7;
+ char *p = *(char **)fp->uf_lines.ga_data + 7;
// A Lambda always has the command "return {expr}". It is much faster
// to evaluate {expr} directly.
ex_nesting_level++;
- (void)eval1((char **)&p, rettv, true);
+ (void)eval1(&p, rettv, true);
ex_nesting_level--;
} else {
// call do_cmdline() to execute the lines
@@ -1110,7 +1101,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT);
}
- --RedrawingDisabled;
+ RedrawingDisabled--;
// when the function was aborted because of an error, return -1
if ((did_emsg
@@ -1140,14 +1131,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// when being verbose, mention the return value
if (p_verbose >= 12) {
- ++no_wait_return;
+ no_wait_return++;
verbose_enter_scroll();
if (aborting()) {
- smsg(_("%s aborted"), sourcing_name);
+ smsg(_("%s aborted"), SOURCING_NAME);
} else if (fc->rettv->v_type == VAR_NUMBER) {
smsg(_("%s returning #%" PRId64 ""),
- sourcing_name, (int64_t)fc->rettv->vval.v_number);
+ SOURCING_NAME, (int64_t)fc->rettv->vval.v_number);
} else {
char buf[MSG_BUF_LEN];
@@ -1163,19 +1154,17 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
trunc_string(s, buf, MSG_BUF_CLEN, MSG_BUF_LEN);
s = buf;
}
- smsg(_("%s returning %s"), sourcing_name, s);
+ smsg(_("%s returning %s"), SOURCING_NAME, s);
xfree(tofree);
}
}
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
- --no_wait_return;
+ no_wait_return--;
}
- xfree(sourcing_name);
- sourcing_name = (char *)save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ estack_pop();
current_sctx = save_current_sctx;
if (do_profiling_yes) {
script_prof_restore(&wait_start);
@@ -1184,15 +1173,15 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
sandbox--;
}
- if (p_verbose >= 12 && sourcing_name != NULL) {
- ++no_wait_return;
+ if (p_verbose >= 12 && SOURCING_NAME != NULL) {
+ no_wait_return++;
verbose_enter_scroll();
- smsg(_("continuing in %s"), sourcing_name);
+ smsg(_("continuing in %s"), SOURCING_NAME);
msg_puts("\n"); // don't overwrite this either
verbose_leave_scroll();
- --no_wait_return;
+ no_wait_return--;
}
did_emsg |= save_did_emsg;
@@ -1633,9 +1622,8 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
msg_puts(" ");
}
msg_puts(force ? "function! " : "function ");
- if (fp->uf_name[0] == K_SPECIAL) {
- msg_puts_attr("<SNR>", HL_ATTR(HLF_8));
- msg_puts((const char *)fp->uf_name + 3);
+ if (fp->uf_name_exp != NULL) {
+ msg_puts((const char *)fp->uf_name_exp);
} else {
msg_puts((const char *)fp->uf_name);
}
@@ -1691,7 +1679,7 @@ static void list_func_head(ufunc_T *fp, int indent, bool force)
/// @param partial return: partial of a FuncRef
///
/// @return the function name in allocated memory, or NULL for failure.
-char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial)
+char_u *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, partial_T **partial)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *name = NULL;
@@ -1702,13 +1690,13 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
lval_T lv;
if (fdp != NULL) {
- memset(fdp, 0, sizeof(funcdict_T));
+ CLEAR_POINTER(fdp);
}
- start = *pp;
+ start = (char_u *)(*pp);
// Check for hard coded <SNR>: already translated function ID (from a user
// command).
- if ((*pp)[0] == K_SPECIAL && (*pp)[1] == KS_EXTRA
+ if ((unsigned char)(*pp)[0] == K_SPECIAL && (unsigned char)(*pp)[1] == KS_EXTRA
&& (*pp)[2] == KE_SNR) {
*pp += 3;
len = get_id_len((const char **)pp) + 3;
@@ -1742,7 +1730,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
semsg(_(e_invarg2), start);
}
} else {
- *pp = (char_u *)find_name_end((char *)start, NULL, NULL, FNE_INCL_BR);
+ *pp = (char *)find_name_end((char *)start, NULL, NULL, FNE_INCL_BR);
}
goto theend;
}
@@ -1756,7 +1744,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
}
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
name = vim_strsave((char_u *)lv.ll_tv->vval.v_string);
- *pp = (char_u *)end;
+ *pp = (char *)end;
} else if (lv.ll_tv->v_type == VAR_PARTIAL
&& lv.ll_tv->vval.v_partial != NULL) {
if (is_luafunc(lv.ll_tv->vval.v_partial) && *end == '.') {
@@ -1767,10 +1755,10 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
}
name = xmallocz((size_t)len);
memcpy(name, end + 1, (size_t)len);
- *pp = (char_u *)end + 1 + len;
+ *pp = (char *)end + 1 + len;
} else {
name = vim_strsave((char_u *)partial_name(lv.ll_tv->vval.v_partial));
- *pp = (char_u *)end;
+ *pp = (char *)end;
}
if (partial != NULL) {
*partial = lv.ll_tv->vval.v_partial;
@@ -1781,7 +1769,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
|| fdp->fd_newkey == NULL)) {
emsg(_(e_funcref));
} else {
- *pp = (char_u *)end;
+ *pp = (char *)end;
}
name = NULL;
}
@@ -1790,7 +1778,7 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
if (lv.ll_name == NULL) {
// Error found, but continue after the function name.
- *pp = (char_u *)end;
+ *pp = (char *)end;
goto theend;
}
@@ -1803,16 +1791,16 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
name = NULL;
}
} else if (!(flags & TFN_NO_DEREF)) {
- len = (int)(end - *pp);
+ len = (int)(end - (char_u *)(*pp));
name = deref_func_name((const char *)(*pp), &len, partial,
flags & TFN_NO_AUTOLOAD);
- if (name == *pp) {
+ if (name == (char_u *)(*pp)) {
name = NULL;
}
}
if (name != NULL) {
name = vim_strsave(name);
- *pp = (char_u *)end;
+ *pp = (char *)end;
if (STRNCMP(name, "<SNR>", 5) == 0) {
// Change "<SNR>" to the byte sequence.
name[0] = K_SPECIAL;
@@ -1890,13 +1878,15 @@ char_u *trans_function_name(char_u **pp, bool skip, int flags, funcdict_T *fdp,
}
memmove(name + lead, lv.ll_name, (size_t)len);
name[lead + len] = NUL;
- *pp = (char_u *)end;
+ *pp = (char *)end;
theend:
clear_lval(&lv);
return name;
}
+#define MAX_FUNC_NESTING 50
+
/// ":function"
void ex_function(exarg_T *eap)
{
@@ -1939,11 +1929,11 @@ void ex_function(exarg_T *eap)
if (ends_excmd(*eap->arg)) {
if (!eap->skip) {
todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
+ for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
fp = HI2UF(hi);
- if (message_filtered(fp->uf_name)) {
+ if (message_filtered((char *)fp->uf_name)) {
continue;
}
if (!func_name_refcount(fp->uf_name)) {
@@ -1960,7 +1950,7 @@ void ex_function(exarg_T *eap)
* ":function /pat": list functions matching pattern.
*/
if (*eap->arg == '/') {
- p = skip_regexp((char_u *)eap->arg + 1, '/', true, NULL);
+ p = (char_u *)skip_regexp(eap->arg + 1, '/', true, NULL);
if (!eap->skip) {
regmatch_T regmatch;
@@ -1972,9 +1962,9 @@ void ex_function(exarg_T *eap)
regmatch.rm_ic = p_ic;
todo = (int)func_hashtab.ht_used;
- for (hi = func_hashtab.ht_array; todo > 0 && !got_int; ++hi) {
+ for (hi = func_hashtab.ht_array; todo > 0 && !got_int; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
fp = HI2UF(hi);
if (!isdigit(*fp->uf_name)
&& vim_regexec(&regmatch, (char *)fp->uf_name, 0)) {
@@ -1986,7 +1976,7 @@ void ex_function(exarg_T *eap)
}
}
if (*p == '/') {
- ++p;
+ p++;
}
eap->nextcmd = (char *)check_nextcmd(p);
return;
@@ -2007,7 +1997,7 @@ void ex_function(exarg_T *eap)
// s:func script-local function name
// g:func global function name, same as "func"
p = (char_u *)eap->arg;
- name = trans_function_name(&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
+ name = trans_function_name((char **)&p, eap->skip, TFN_NO_AUTOLOAD, &fudi, NULL);
paren = (vim_strchr((char *)p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip) {
/*
@@ -2022,14 +2012,14 @@ void ex_function(exarg_T *eap)
xfree(fudi.fd_newkey);
return;
} else {
- eap->skip = TRUE;
+ eap->skip = true;
}
}
// An error in a function call during evaluation of an expression in magic
// braces should not cause the function not to be defined.
saved_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
//
// ":function func" with only function name: list function.
@@ -2064,7 +2054,7 @@ void ex_function(exarg_T *eap)
msg_putchar(' ');
}
}
- msg_prt_line(FUNCLINE(fp, j), false);
+ msg_prt_line((char_u *)FUNCLINE(fp, j), false);
ui_flush(); // show a line at a time
os_breakcheck();
}
@@ -2108,9 +2098,8 @@ void ex_function(exarg_T *eap)
}
if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
int j = (*arg == K_SPECIAL) ? 3 : 0;
- while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j])
- : eval_isnamec(arg[j]))) {
- ++j;
+ while (arg[j] != NUL && (j == 0 ? eval_isnamec1(arg[j]) : eval_isnamec(arg[j]))) {
+ j++;
}
if (arg[j] != NUL) {
emsg_funcname((char *)e_invarg2, arg);
@@ -2122,7 +2111,7 @@ void ex_function(exarg_T *eap)
}
}
- if (get_function_args(&p, ')', &newargs, &varargs,
+ if (get_function_args((char **)&p, ')', &newargs, &varargs,
&default_args, eap->skip) == FAIL) {
goto errret_2;
}
@@ -2192,7 +2181,7 @@ void ex_function(exarg_T *eap)
}
// Save the starting line number.
- sourcing_lnum_top = sourcing_lnum;
+ sourcing_lnum_top = SOURCING_LNUM;
indent = 2;
nesting = 0;
@@ -2234,10 +2223,10 @@ void ex_function(exarg_T *eap)
ui_ext_cmdline_block_append((size_t)indent, (const char *)theline);
}
- // Detect line continuation: sourcing_lnum increased more than one.
+ // Detect line continuation: SOURCING_LNUM increased more than one.
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
- if (sourcing_lnum < sourcing_lnum_off) {
- sourcing_lnum_off -= sourcing_lnum;
+ if (SOURCING_LNUM < sourcing_lnum_off) {
+ sourcing_lnum_off -= SOURCING_LNUM;
} else {
sourcing_lnum_off = 0;
}
@@ -2315,10 +2304,14 @@ void ex_function(exarg_T *eap)
p = (char_u *)skipwhite((char *)p + 1);
}
p += eval_fname_script((const char *)p);
- xfree(trans_function_name(&p, true, 0, NULL, NULL));
+ xfree(trans_function_name((char **)&p, true, 0, NULL, NULL));
if (*skipwhite((char *)p) == '(') {
- nesting++;
- indent += 2;
+ if (nesting == MAX_FUNC_NESTING - 1) {
+ emsg(_("E1058: function nesting too deep"));
+ } else {
+ nesting++;
+ indent += 2;
+ }
}
}
@@ -2339,7 +2332,7 @@ void ex_function(exarg_T *eap)
}
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
- arg = (char_u *)skipwhite((char *)skiptowhite(p));
+ arg = (char_u *)skipwhite(skiptowhite((char *)p));
if (arg[0] == '<' && arg[1] == '<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
@@ -2366,12 +2359,12 @@ void ex_function(exarg_T *eap)
// Check for ":let v =<< [trim] EOF"
// and ":let [a, b] =<< [trim] EOF"
- arg = (char_u *)skipwhite((char *)skiptowhite(p));
+ arg = (char_u *)skipwhite(skiptowhite((char *)p));
if (*arg == '[') {
arg = (char_u *)vim_strchr((char *)arg, ']');
}
if (arg != NULL) {
- arg = (char_u *)skipwhite((char *)skiptowhite(arg));
+ arg = (char_u *)skipwhite(skiptowhite((char *)arg));
if (arg[0] == '='
&& arg[1] == '<'
&& arg[2] == '<'
@@ -2386,7 +2379,7 @@ void ex_function(exarg_T *eap)
heredoc_trimmed =
vim_strnsave(theline, (size_t)((char_u *)skipwhite((char *)theline) - theline));
}
- skip_until = vim_strnsave(p, (size_t)(skiptowhite(p) - p));
+ skip_until = vim_strnsave(p, (size_t)((char_u *)skiptowhite((char *)p) - p));
do_concat = false;
is_heredoc = true;
}
@@ -2400,12 +2393,12 @@ void ex_function(exarg_T *eap)
// allocates 250 bytes per line, this saves 80% on average. The cost
// is an extra alloc/free.
p = vim_strsave(theline);
- ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
// Add NULL lines for continuation lines, so that the line count is
// equal to the index in the growarray.
while (sourcing_lnum_off-- > 0) {
- ((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = NULL;
}
// Check for end of eap->arg.
@@ -2454,9 +2447,12 @@ void ex_function(exarg_T *eap)
fp = NULL;
overwrite = true;
} else {
- // redefine existing function
+ char_u *exp_name = fp->uf_name_exp;
+ // redefine existing function, keep the expanded name
XFREE_CLEAR(name);
+ fp->uf_name_exp = NULL;
func_clear_items(fp);
+ fp->uf_name_exp = exp_name;
fp->uf_profiling = false;
fp->uf_prof_initialized = false;
}
@@ -2495,13 +2491,12 @@ void ex_function(exarg_T *eap)
// Check that the autoload name matches the script name.
int j = FAIL;
- if (sourcing_name != NULL) {
+ if (SOURCING_NAME != NULL) {
scriptname = (char_u *)autoload_name((const char *)name, STRLEN(name));
p = (char_u *)vim_strchr((char *)scriptname, '/');
plen = (int)STRLEN(p);
- slen = (int)STRLEN(sourcing_name);
- if (slen > plen && FNAMECMP(p,
- sourcing_name + slen - plen) == 0) {
+ slen = (int)STRLEN(SOURCING_NAME);
+ if (slen > plen && FNAMECMP(p, SOURCING_NAME + slen - plen) == 0) {
j = OK;
}
xfree(scriptname);
@@ -2536,7 +2531,7 @@ void ex_function(exarg_T *eap)
}
// insert the new function in the function list
- STRCPY(fp->uf_name, name);
+ set_ufunc_name(fp, name);
if (overwrite) {
hi = hash_find(&func_hashtab, (char *)name);
hi->hi_key = UF2HIKEY(fp);
@@ -2628,8 +2623,7 @@ bool function_exists(const char *const name, bool no_deref)
if (no_deref) {
flag |= TFN_NO_DEREF;
}
- char *const p = (char *)trans_function_name((char_u **)&nm, false, flag, NULL,
- NULL);
+ char *const p = (char *)trans_function_name((char **)&nm, false, flag, NULL, NULL);
nm = (char_u *)skipwhite((char *)nm);
// Only accept "funcname", "funcname ", "funcname (..." and
@@ -2656,10 +2650,10 @@ char *get_user_func_name(expand_T *xp, int idx)
assert(hi);
if (done < func_hashtab.ht_used) {
if (done++ > 0) {
- ++hi;
+ hi++;
}
while (HASHITEM_EMPTY(hi)) {
- ++hi;
+ hi++;
}
fp = HI2UF(hi);
@@ -2693,7 +2687,7 @@ void ex_delfunction(exarg_T *eap)
funcdict_T fudi;
p = (char_u *)eap->arg;
- name = trans_function_name(&p, eap->skip, 0, &fudi, NULL);
+ name = trans_function_name((char **)&p, eap->skip, 0, &fudi, NULL);
xfree(fudi.fd_newkey);
if (name == NULL) {
if (fudi.fd_dict != NULL && !eap->skip) {
@@ -2859,7 +2853,7 @@ void ex_return(exarg_T *eap)
{
char_u *arg = (char_u *)eap->arg;
typval_T rettv;
- int returning = FALSE;
+ int returning = false;
if (current_funccal == NULL) {
emsg(_("E133: :return not inside a function"));
@@ -2867,7 +2861,7 @@ void ex_return(exarg_T *eap)
}
if (eap->skip) {
- ++emsg_skip;
+ emsg_skip++;
}
eap->nextcmd = NULL;
@@ -2899,7 +2893,7 @@ void ex_return(exarg_T *eap)
}
if (eap->skip) {
- --emsg_skip;
+ emsg_skip--;
}
}
@@ -2932,7 +2926,7 @@ void ex_call(exarg_T *eap)
return;
}
- tofree = trans_function_name(&arg, false, TFN_INT, &fudi, &partial);
+ tofree = trans_function_name((char **)&arg, false, TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL) {
// Still need to give an error message for missing key.
semsg(_(e_dictkey), fudi.fd_newkey);
@@ -2987,7 +2981,7 @@ void ex_call(exarg_T *eap)
funcexe.evaluate = true;
funcexe.partial = partial;
funcexe.selfdict = fudi.fd_dict;
- if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) {
+ if (get_func_tv(name, -1, &rettv, (char **)&arg, &funcexe) == FAIL) {
failed = true;
break;
}
@@ -3016,7 +3010,7 @@ void ex_call(exarg_T *eap)
// When inside :try we need to check for following "| catch" or "| endtry".
// Not when there was an error, but do check if an exception was thrown.
- if ((!aborting() || current_exception != NULL) && (!failed || eap->cstack->cs_trylevel > 0)) {
+ if ((!aborting() || did_throw) && (!failed || eap->cstack->cs_trylevel > 0)) {
// Check for trailing illegal characters and a following command.
if (!ends_excmd(*arg)) {
if (!failed && !aborting()) {
@@ -3041,8 +3035,8 @@ end:
/// @param is_cmd set when called due to a ":return" command.
/// @param rettv may point to a typval_T with the return rettv.
///
-/// @return TRUE when the return can be carried out,
-/// FALSE when the return gets pending.
+/// @return true when the return can be carried out,
+/// false when the return gets pending.
int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
{
int idx;
@@ -3092,7 +3086,7 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
}
report_make_pending(CSTP_RETURN, rettv);
} else {
- current_funccal->returned = TRUE;
+ current_funccal->returned = true;
// If the return is carried out now, store the return value. For
// a return immediately after reanimation, the value is already
@@ -3111,16 +3105,16 @@ int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv)
/// Generate a return command for producing the value of "rettv". The result
/// is an allocated string. Used by report_pending() for verbose messages.
-char_u *get_return_cmd(void *rettv)
+char *get_return_cmd(void *rettv)
{
- char_u *s = NULL;
- char_u *tofree = NULL;
+ char *s = NULL;
+ char *tofree = NULL;
if (rettv != NULL) {
- tofree = s = (char_u *)encode_tv2echo((typval_T *)rettv, NULL);
+ tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
}
if (s == NULL) {
- s = (char_u *)"";
+ s = "";
}
STRCPY(IObuff, ":return ");
@@ -3129,7 +3123,7 @@ char_u *get_return_cmd(void *rettv)
STRCPY(IObuff + IOSIZE - 4, "...");
}
xfree(tofree);
- return vim_strsave(IObuff);
+ return (char *)vim_strsave(IObuff);
}
/// Get next function line.
@@ -3140,13 +3134,12 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
{
funccall_T *fcp = (funccall_T *)cookie;
ufunc_T *fp = fcp->func;
- char_u *retval;
+ char *retval;
garray_T *gap; // growarray with function lines
// If breakpoints have been added/deleted need to check for it.
if (fcp->dbg_tick != debug_tick) {
- fcp->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name,
- sourcing_lnum);
+ fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -3160,14 +3153,14 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
} else {
// Skip NULL lines (continuation lines).
while (fcp->linenr < gap->ga_len
- && ((char_u **)(gap->ga_data))[fcp->linenr] == NULL) {
+ && ((char **)(gap->ga_data))[fcp->linenr] == NULL) {
fcp->linenr++;
}
if (fcp->linenr >= gap->ga_len) {
retval = NULL;
} else {
- retval = vim_strsave(((char_u **)(gap->ga_data))[fcp->linenr++]);
- sourcing_lnum = fcp->linenr;
+ retval = xstrdup(((char **)(gap->ga_data))[fcp->linenr++]);
+ SOURCING_LNUM = fcp->linenr;
if (do_profiling == PROF_YES) {
func_line_start(cookie);
}
@@ -3175,18 +3168,17 @@ char *get_func_line(int c, void *cookie, int indent, bool do_concat)
}
// Did we encounter a breakpoint?
- if (fcp->breakpoint != 0 && fcp->breakpoint <= sourcing_lnum) {
- dbg_breakpoint(fp->uf_name, sourcing_lnum);
+ if (fcp->breakpoint != 0 && fcp->breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint((char *)fp->uf_name, SOURCING_LNUM);
// Find next breakpoint.
- fcp->breakpoint = dbg_find_breakpoint(false, fp->uf_name,
- sourcing_lnum);
+ fcp->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, SOURCING_LNUM);
fcp->dbg_tick = debug_tick;
}
- return (char *)retval;
+ return retval;
}
-/// @return TRUE if the currently active function should be ended, because a
+/// @return true if the currently active function should be ended, because a
/// return was encountered or an error occurred. Used inside a ":while".
int func_has_ended(void *cookie)
{
@@ -3198,7 +3190,7 @@ int func_has_ended(void *cookie)
|| fcp->returned;
}
-/// @return TRUE if cookie indicates a function which "abort"s on errors.
+/// @return true if cookie indicates a function which "abort"s on errors.
int func_has_abort(void *cookie)
{
return ((funccall_T *)cookie)->func->uf_flags & FC_ABORT;
@@ -3289,7 +3281,7 @@ int func_level(void *cookie)
return ((funccall_T *)cookie)->level;
}
-/// @return TRUE when a function was ended by a ":return" command.
+/// @return true when a function was ended by a ":return" command.
int current_func_returned(void)
{
return current_funccal->returned;
diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h
index ed86aaad4a..4b7007aae9 100644
--- a/src/nvim/eval/userfunc.h
+++ b/src/nvim/eval/userfunc.h
@@ -4,6 +4,11 @@
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds_defs.h"
+// From user function to hashitem and back.
+#define UF2HIKEY(fp) ((fp)->uf_name)
+#define HIKEY2UF(p) ((ufunc_T *)(p - offsetof(ufunc_T, uf_name)))
+#define HI2UF(hi) HIKEY2UF((hi)->hi_key)
+
///< Structure used by trans_function_name()
typedef struct {
dict_T *fd_dict; ///< Dictionary used.
@@ -59,8 +64,8 @@ typedef struct {
.basetv = NULL, \
}
-#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
-#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
+#define FUNCARG(fp, j) ((char **)(fp->uf_args.ga_data))[j]
+#define FUNCLINE(fp, j) ((char **)(fp->uf_lines.ga_data))[j]
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/userfunc.h.generated.h"
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index cf2755f639..75410d40d8 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -7,6 +7,7 @@
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
@@ -15,8 +16,10 @@
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/window.h"
@@ -78,7 +81,7 @@ static list_T *heredoc_get(exarg_T *eap, char *cmd)
// The marker is the next word.
if (*cmd != NUL && *cmd != '"') {
marker = skipwhite(cmd);
- p = (char *)skiptowhite((char_u *)marker);
+ p = skiptowhite(marker);
if (*skipwhite(p) != NUL && *skipwhite(p) != '"') {
semsg(_(e_trailing_arg), p);
return NULL;
@@ -410,7 +413,7 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs
// apply :filter /pat/ to variable name
xstrlcpy(buf, prefix, IOSIZE);
xstrlcat(buf, (char *)di->di_key, IOSIZE);
- if (message_filtered((char_u *)buf)) {
+ if (message_filtered(buf)) {
continue;
}
@@ -584,21 +587,13 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
char *s = vim_getenv(name);
if (s != NULL) {
- tofree = (char *)concat_str((const char_u *)s, (const char_u *)p);
+ tofree = concat_str(s, p);
p = (const char *)tofree;
xfree(s);
}
}
if (p != NULL) {
- os_setenv(name, p, 1);
- if (STRICMP(name, "HOME") == 0) {
- init_homedir();
- } else if (didset_vim && STRICMP(name, "VIM") == 0) {
- didset_vim = false;
- } else if (didset_vimruntime
- && STRICMP(name, "VIMRUNTIME") == 0) {
- didset_vimruntime = false;
- }
+ vim_setenv_ext(name, p);
arg_end = arg;
}
name[len] = c1;
@@ -668,8 +663,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
} else if (opt_type == gov_string && stringval != NULL && s != NULL) {
// string
char *const oldstringval = stringval;
- stringval = (char *)concat_str((const char_u *)stringval,
- (const char_u *)s);
+ stringval = concat_str(stringval, s);
xfree(oldstringval);
s = stringval;
}
@@ -710,7 +704,7 @@ static char *ex_let_one(char *arg, typval_T *const tv, const bool copy, const bo
if (p != NULL && op != NULL && *op == '.') {
s = get_reg_contents(*arg == '@' ? '"' : *arg, kGRegExprSrc);
if (s != NULL) {
- ptofree = (char *)concat_str((char_u *)s, (const char_u *)p);
+ ptofree = concat_str(s, p);
p = (const char *)ptofree;
xfree(s);
}
@@ -796,6 +790,7 @@ static void ex_unletlock(exarg_T *eap, char *argstart, int deep, ex_unletlock_ca
semsg(_(e_invarg2), arg - 1);
return;
}
+ assert(*lv.ll_name == '$'); // suppress clang "Uninitialized argument value"
if (!error && !eap->skip && callback(&lv, arg, eap, deep) == FAIL) {
error = true;
}
@@ -856,7 +851,7 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_
// Environment variable, normal name or expanded name.
if (*lp->ll_name == '$') {
- os_unsetenv(lp->ll_name + 1);
+ vim_unsetenv_ext(lp->ll_name + 1);
} else if (do_unlet(lp->ll_name, lp->ll_name_len, forceit) == FAIL) {
ret = FAIL;
}
@@ -1123,7 +1118,7 @@ void vars_clear(hashtab_T *ht)
vars_clear_ext(ht, true);
}
-/// Like vars_clear(), but only free the value if "free_val" is TRUE.
+/// Like vars_clear(), but only free the value if "free_val" is true.
void vars_clear_ext(hashtab_T *ht, int free_val)
{
int todo;
@@ -1305,7 +1300,7 @@ void set_var_const(const char *name, const size_t name_len, typval_T *const tv,
set_search_direction(v->di_tv.vval.v_number ? '/' : '?');
} else if (strcmp(varname, "hlsearch") == 0) {
no_hlsearch = !v->di_tv.vval.v_number;
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
return;
} else if (v->di_tv.v_type != tv->v_type) {
@@ -1632,7 +1627,7 @@ static void set_option_from_tv(const char *varname, typval_T *varp)
strval = tv_get_string_buf_chk(varp, nbuf);
}
if (!error && strval != NULL) {
- set_option_value(varname, numval, strval, OPT_LOCAL);
+ set_option_value_give_err(varname, numval, strval, OPT_LOCAL);
}
}
@@ -1707,7 +1702,7 @@ bool var_exists(const char *var)
}
/// "gettabvar()" function
-void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_gettabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const varname = tv_get_string_chk(&argvars[1]);
tabpage_T *const tp = find_tabpage((int)tv_get_number_chk(&argvars[0], NULL));
@@ -1721,19 +1716,19 @@ void f_gettabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "gettabwinvar()" function
-void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_gettabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getwinvar(argvars, rettv, 1);
}
/// "getwinvar()" function
-void f_getwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
getwinvar(argvars, rettv, 0);
}
/// "getbufvar()" function
-void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const varname = tv_get_string_chk(&argvars[1]);
buf_T *const buf = tv_get_buf_from_arg(&argvars[0]);
@@ -1742,7 +1737,7 @@ void f_getbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "settabvar()" function
-void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_settabvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = 0;
@@ -1773,19 +1768,19 @@ void f_settabvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "settabwinvar()" function
-void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_settabwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
setwinvar(argvars, rettv, 1);
}
/// "setwinvar()" function
-void f_setwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setwinvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
setwinvar(argvars, rettv, 0);
}
/// "setbufvar()" function
-void f_setbufvar(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setbufvar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if (check_secure()
|| !tv_check_str_or_nr(&argvars[0])) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 9d1ac185d4..e672b80d69 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -15,15 +15,18 @@
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
@@ -36,6 +39,7 @@
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/help.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -52,6 +56,7 @@
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/shell.h"
@@ -59,9 +64,9 @@
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/plines.h"
+#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
@@ -115,7 +120,7 @@ void do_ascii(const exarg_T *const eap)
{
char *dig;
int cc[MAX_MCO];
- int c = utfc_ptr2char(get_cursor_pos_ptr(), cc);
+ int c = utfc_ptr2char((char *)get_cursor_pos_ptr(), cc);
if (c == NUL) {
msg("NUL");
return;
@@ -269,7 +274,7 @@ void ex_align(exarg_T *eap)
if (eap->cmdidx == CMD_left) { // left align
new_indent = indent;
} else {
- has_tab = FALSE; // avoid uninit warnings
+ has_tab = false; // avoid uninit warnings
len = linelen(eap->cmdidx == CMD_right ? &has_tab
: NULL) - get_indent();
@@ -300,7 +305,7 @@ void ex_align(exarg_T *eap)
new_indent--;
break;
}
- --new_indent;
+ new_indent--;
}
}
}
@@ -399,7 +404,7 @@ static int sort_compare(const void *s1, const void *s2)
}
fast_breakcheck();
if (got_int) {
- sort_abort = TRUE;
+ sort_abort = true;
}
// When sorting numbers "start_col_nr" is the number, not the column
@@ -508,7 +513,7 @@ void ex_sort(exarg_T *eap)
eap->nextcmd = (char *)check_nextcmd((char_u *)p);
break;
} else if (!ASCII_ISALPHA(*p) && regmatch.regprog == NULL) {
- s = (char *)skip_regexp((char_u *)p + 1, *p, true, NULL);
+ s = skip_regexp(p + 1, *p, true, NULL);
if (*s != *p) {
emsg(_(e_invalpat));
goto sortend;
@@ -581,11 +586,11 @@ void ex_sort(exarg_T *eap)
p = s + start_col;
if (sort_nr) {
if (sort_what & STR2NR_HEX) {
- s = (char *)skiptohex((char_u *)p);
+ s = skiptohex(p);
} else if (sort_what & STR2NR_BIN) {
s = (char *)skiptobin(p);
} else {
- s = (char *)skiptodigit((char_u *)p);
+ s = skiptodigit(p);
}
if (s > p && s[-1] == '-') {
s--; // include preceding negative sign
@@ -673,7 +678,7 @@ void ex_sort(exarg_T *eap)
// delete the original lines if appending worked
if (i == count) {
- for (i = 0; i < count; ++i) {
+ for (i = 0; i < count; i++) {
ml_delete(eap->line1, false);
}
} else {
@@ -738,7 +743,7 @@ void ex_retab(exarg_T *eap)
curwin->w_p_list = 0; // don't want list mode here
new_ts_str = eap->arg;
- if (!tabstop_set((char_u *)eap->arg, &new_vts_array)) {
+ if (!tabstop_set(eap->arg, &new_vts_array)) {
return;
}
while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
@@ -836,7 +841,7 @@ void ex_retab(exarg_T *eap)
if (ptr[col] == NUL) {
break;
}
- vcol += win_chartabsize(curwin, (char_u *)ptr + col, (colnr_T)vcol);
+ vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
if (vcol >= MAXCOL) {
emsg(_(e_resulting_text_too_long));
break;
@@ -862,7 +867,7 @@ void ex_retab(exarg_T *eap)
&& tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
// not changed
} else {
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
}
if (first_line != 0) {
changed_lines(first_line, 0, last_line + 1, 0L, true);
@@ -1091,14 +1096,14 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n)
if (line1 == n) {
line1 = curwin->w_cursor.lnum;
}
- ++line1;
+ line1++;
if (curwin->w_cursor.lnum < line1) {
- ++line1;
+ line1++;
}
if (curwin->w_cursor.lnum < line2) {
- ++line2;
+ line2++;
}
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
appended_lines_mark(n, count);
@@ -1145,7 +1150,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
}
if (addr_count == 0) { // :!
- msg_scroll = FALSE; // don't scroll here
+ msg_scroll = false; // don't scroll here
autowrite_all();
msg_scroll = scroll_save;
}
@@ -1198,7 +1203,7 @@ void do_bang(int addr_count, exarg_T *eap, bool forceit, bool do_in, bool do_out
break;
}
}
- ++p;
+ p++;
}
} while (trailarg != NULL);
@@ -1342,7 +1347,8 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
msg_putchar('\n'); // Keep message from buf_write().
no_wait_return--;
if (!aborting()) {
- semsg(_("E482: Can't create file %s"), itmp); // Will call wait_return.
+ // will call wait_return()
+ semsg(_("E482: Can't create file %s"), itmp);
}
goto filterend;
}
@@ -1363,7 +1369,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
xfree(cmd_buf);
goto error;
}
- redraw_curbuf_later(VALID);
+ redraw_curbuf_later(UPD_VALID);
}
read_linecount = curbuf->b_ml.ml_line_count;
@@ -1371,14 +1377,14 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
call_shell((char_u *)cmd_buf, (ShellOpts)(kShellOptFilter | shell_flags), NULL);
xfree(cmd_buf);
- did_check_timestamps = FALSE;
- need_check_timestamps = TRUE;
+ did_check_timestamps = false;
+ need_check_timestamps = true;
// When interrupting the shell command, it may still have produced some
// useful output. Reset got_int here, so that readfile() won't cancel
// reading.
os_breakcheck();
- got_int = FALSE;
+ got_int = false;
if (do_out) {
if (otmp != NULL) {
@@ -1442,7 +1448,7 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
}
beginline(BL_WHITE | BL_FIX); // cursor on first non-blank
- --no_wait_return;
+ no_wait_return--;
if (linecount > p_report) {
if (do_in) {
@@ -1460,8 +1466,8 @@ static void do_filter(linenr_T line1, linenr_T line2, exarg_T *eap, char *cmd, b
error:
// put cursor back in same position for ":w !cmd"
curwin->w_cursor = cursor_save;
- --no_wait_return;
- wait_return(FALSE);
+ no_wait_return--;
+ wait_return(false);
}
filterend:
@@ -1651,7 +1657,7 @@ char *make_filter_cmd(char *cmd, char *itmp, char *otmp)
}
#endif
if (otmp != NULL) {
- append_redir(buf, len, (char *)p_srr, otmp);
+ append_redir(buf, len, p_srr, otmp);
}
return buf;
}
@@ -1704,12 +1710,12 @@ void print_line(linenr_T lnum, int use_number, int list)
int save_silent = silent_mode;
// apply :filter /pat/
- if (message_filtered(ml_get(lnum))) {
+ if (message_filtered((char *)ml_get(lnum))) {
return;
}
msg_start();
- silent_mode = FALSE;
+ silent_mode = false;
info_message = true; // use mch_msg(), not mch_errmsg()
print_line_no_prefix(lnum, use_number, list);
if (save_silent) {
@@ -1817,7 +1823,7 @@ void ex_write(exarg_T *eap)
}
/// write current buffer to file 'eap->arg'
-/// if 'eap->append' is TRUE, append to the file
+/// if 'eap->append' is true, append to the file
///
/// if *eap->arg == NUL write to current file
///
@@ -1842,7 +1848,7 @@ int do_write(exarg_T *eap)
emsg(_(e_argreq));
goto theend;
}
- other = FALSE;
+ other = false;
} else {
fname = ffname;
free_fname = fix_fname(ffname);
@@ -1875,8 +1881,7 @@ int do_write(exarg_T *eap)
// Writing to the current file is not allowed in readonly mode
// and a file name is required.
// "nofile" and "nowrite" buffers cannot be written implicitly either.
- if (!other && (bt_dontwrite_msg(curbuf)
- || check_fname() == FAIL
+ if (!other && (bt_dontwrite_msg(curbuf) || check_fname() == FAIL
|| check_readonly(&eap->forceit, curbuf))) {
goto theend;
}
@@ -1895,7 +1900,7 @@ int do_write(exarg_T *eap)
(char_u *)_("Write partial file?"), 2) != VIM_YES) {
goto theend;
}
- eap->forceit = TRUE;
+ eap->forceit = true;
} else {
emsg(_("E140: Use ! to write partial buffer"));
goto theend;
@@ -1932,8 +1937,8 @@ int do_write(exarg_T *eap)
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, alt_buf);
if (!alt_buf->b_p_bl) {
- alt_buf->b_p_bl = TRUE;
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, alt_buf);
+ alt_buf->b_p_bl = true;
+ apply_autocmds(EVENT_BUFADD, NULL, NULL, false, alt_buf);
}
if (curbuf != was_curbuf || aborting()) {
// buffer changed, don't write the file
@@ -1961,7 +1966,7 @@ int do_write(exarg_T *eap)
// After ":saveas fname" reset 'readonly'.
if (eap->cmdidx == CMD_saveas) {
if (retval == OK) {
- curbuf->b_p_ro = FALSE;
+ curbuf->b_p_ro = false;
redraw_tabline = true;
}
}
@@ -1991,17 +1996,22 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
{
// Write to another file or b_flags set or not writing the whole file:
// overwriting only allowed with '!'
+ // If "other" is false and bt_nofilename(buf) is true, this must be
+ // writing an "acwrite" buffer to the same file as its b_ffname, and
+ // buf_write() will only allow writing with BufWriteCmd autocommands,
+ // so there is no need for an overwrite check.
if ((other
- || (buf->b_flags & BF_NOTEDITED)
- || ((buf->b_flags & BF_NEW)
- && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
- || (buf->b_flags & BF_READERR))
+ || (!bt_nofilename(buf)
+ && ((buf->b_flags & BF_NOTEDITED)
+ || ((buf->b_flags & BF_NEW)
+ && vim_strchr(p_cpo, CPO_OVERNEW) == NULL)
+ || (buf->b_flags & BF_READERR))))
&& !p_wa
&& os_path_exists((char_u *)ffname)) {
if (!eap->forceit && !eap->append) {
#ifdef UNIX
// It is possible to open a directory on Unix.
- if (os_isdir((char_u *)ffname)) {
+ if (os_isdir(ffname)) {
semsg(_(e_isadir2), ffname);
return FAIL;
}
@@ -2013,7 +2023,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2) != VIM_YES) {
return FAIL;
}
- eap->forceit = TRUE;
+ eap->forceit = true;
} else {
emsg(_(e_exists));
return FAIL;
@@ -2036,7 +2046,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
STRCPY(dir, ".");
} else {
dir = xmalloc(MAXPATHL);
- p = (char *)p_dir;
+ p = p_dir;
copy_option_part(&p, dir, MAXPATHL, ",");
}
swapname = (char *)makeswapname((char_u *)fname, (char_u *)ffname, curbuf, (char_u *)dir);
@@ -2053,7 +2063,7 @@ int check_overwrite(exarg_T *eap, buf_T *buf, char *fname, char *ffname, int oth
xfree(swapname);
return FAIL;
}
- eap->forceit = TRUE;
+ eap->forceit = true;
} else {
semsg(_("E768: Swap file exists: %s (:silent! overrides)"),
swapname);
@@ -2111,7 +2121,7 @@ void do_wqall(exarg_T *eap)
* 4. if overwriting is allowed (even after a dialog)
*/
if (not_writing()) {
- ++error;
+ error++;
break;
}
if (buf->b_ffname == NULL) {
@@ -2154,7 +2164,7 @@ bool not_writing(void)
}
/// Check if a buffer is read-only (either 'readonly' option is set or file is
-/// read-only). Ask for overruling in a dialog. Return TRUE and give an error
+/// read-only). Ask for overruling in a dialog. Return true and give an error
/// message when the buffer is readonly.
static int check_readonly(int *forceit, buf_T *buf)
{
@@ -2179,10 +2189,10 @@ static int check_readonly(int *forceit, buf_T *buf)
if (vim_dialog_yesno(VIM_QUESTION, NULL, (char_u *)buff, 2) == VIM_YES) {
// Set forceit, to force the writing of a readonly file
- *forceit = TRUE;
- return FALSE;
+ *forceit = true;
+ return false;
} else {
- return TRUE;
+ return true;
}
} else if (buf->b_p_ro) {
emsg(_(e_readonly));
@@ -2190,10 +2200,10 @@ static int check_readonly(int *forceit, buf_T *buf)
semsg(_("E505: \"%s\" is read-only (add ! to override)"),
buf->b_fname);
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Try to abandon the current file and edit a new or existing file.
@@ -2246,7 +2256,7 @@ int getfile(int fnum, char *ffname_arg, char *sfname_arg, int setpm, linenr_T ln
}
}
if (other) {
- --no_wait_return;
+ no_wait_return--;
}
if (setpm) {
setpcmark();
@@ -2287,7 +2297,7 @@ theend:
/// ECMD_LASTL: use last position in loaded file
/// ECMD_LAST: use last position in all files
/// ECMD_ONE: use first line
-/// @param flags ECMD_HIDE: if TRUE don't free the current buffer
+/// @param flags ECMD_HIDE: if true don't free the current buffer
/// ECMD_SET_HELP: set b_help flag of (new) buffer before
/// opening file
/// ECMD_OLDBUF: use existing buffer if it exists
@@ -2305,7 +2315,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
win_T *oldwin)
{
bool other_file; // true if editing another file
- int oldbuf; // TRUE if using existing buffer
+ int oldbuf; // true if using existing buffer
bool auto_buf = false; // true if autocommands brought us
// into the buffer unexpectedly
char *new_name = NULL;
@@ -2381,7 +2391,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// If the file was changed we may not be allowed to abandon it:
// - if we are going to re-edit the same file
- // - or if we are the only window on this file and if ECMD_HIDE is FALSE
+ // - or if we are the only window on this file and if ECMD_HIDE is false
if (((!other_file && !(flags & ECMD_OLDBUF))
|| (curbuf->b_nwindows == 1
&& !(flags & (ECMD_HIDE | ECMD_ADDBUF | ECMD_ALTBUF))))
@@ -2499,7 +2509,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
/*
* Make the (new) buffer the one used by the current window.
- * If the old buffer becomes unused, free it if ECMD_HIDE is FALSE.
+ * If the old buffer becomes unused, free it if ECMD_HIDE is false.
* If the current buffer was empty and has no file name, curbuf
* is returned by buflist_new(), nothing to do here.
*/
@@ -2596,11 +2606,11 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
curwin->w_buffer = buf;
curbuf = buf;
- ++curbuf->b_nwindows;
+ curbuf->b_nwindows++;
// Set 'fileformat', 'binary' and 'fenc' when forced.
if (!oldbuf && eap != NULL) {
- set_file_options(TRUE, eap);
+ set_file_options(true, eap);
set_forced_fenc(eap);
}
}
@@ -2636,7 +2646,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
} else if (!curbuf->b_help) {
// Don't make a buffer listed if it's a help buffer. Useful when using
// CTRL-O to go back to a help file.
- set_buflisted(TRUE);
+ set_buflisted(true);
}
// If autocommands change buffers under our fingers, forget about
@@ -2655,10 +2665,10 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
/*
* other_file oldbuf
- * FALSE FALSE re-edit same file, buffer is re-used
- * FALSE TRUE re-edit same file, nothing changes
- * TRUE FALSE start editing new file, new buffer
- * TRUE TRUE start editing in existing buffer (nothing to do)
+ * false false re-edit same file, buffer is re-used
+ * false true re-edit same file, nothing changes
+ * true false start editing new file, new buffer
+ * true true start editing in existing buffer (nothing to do)
*/
if (!other_file && !oldbuf) { // re-use the buffer
set_last_cursor(curwin); // may set b_last_cursor
@@ -2785,7 +2795,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// changed by the user.
do_modelines(OPT_WINONLY);
- apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf,
+ apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, false, curbuf,
&retval);
if ((flags & ECMD_NOWINENTER) == 0) {
apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, false, curbuf,
@@ -2843,7 +2853,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
curwin->w_cursor.col = solcol;
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
} else {
beginline(BL_SOL | BL_FIX);
}
@@ -2869,7 +2879,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Obey the 'O' flag in 'cpoptions': overwrite any previous file
// message.
if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) {
- msg_scroll = FALSE;
+ msg_scroll = false;
}
if (!msg_scroll) { // wait a bit when overwriting an error msg
check_for_delay(false);
@@ -2905,7 +2915,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
update_topline(curwin);
curwin->w_scbind_pos = curwin->w_topline;
*so_ptr = n;
- redraw_curbuf_later(NOT_VALID); // redraw this buffer later
+ redraw_curbuf_later(UPD_NOT_VALID); // redraw this buffer later
}
// Change directories when the 'acd' option is set.
@@ -2959,7 +2969,7 @@ void ex_append(exarg_T *eap)
}
if (eap->cmdidx != CMD_append) {
- --lnum;
+ lnum--;
}
// when the buffer is empty need to delete the dummy line
@@ -3013,9 +3023,9 @@ void ex_append(exarg_T *eap)
// Look for the "." after automatic indent.
vcol = 0;
- for (p = theline; indent > vcol; ++p) {
+ for (p = theline; indent > vcol; p++) {
if (*p == ' ') {
- ++vcol;
+ vcol++;
} else if (*p == TAB) {
vcol += 8 - vcol % 8;
} else {
@@ -3044,7 +3054,7 @@ void ex_append(exarg_T *eap)
}
xfree(theline);
- ++lnum;
+ lnum++;
if (empty) {
ml_delete(2L, false);
@@ -3093,7 +3103,7 @@ void ex_change(exarg_T *eap)
append_indent = get_indent_lnum(eap->line1);
}
- for (lnum = eap->line2; lnum >= eap->line1; --lnum) {
+ for (lnum = eap->line2; lnum >= eap->line1; lnum--) {
if (curbuf->b_ml.ml_flags & ML_EMPTY) { // nothing to delete
break;
}
@@ -3136,10 +3146,10 @@ void ex_z(exarg_T *eap)
kind = x;
if (*kind == '-' || *kind == '+' || *kind == '='
|| *kind == '^' || *kind == '.') {
- ++x;
+ x++;
}
while (*x == '-' || *x == '+') {
- ++x;
+ x++;
}
if (*x != 0) {
@@ -3196,7 +3206,7 @@ void ex_z(exarg_T *eap)
if (*kind == '+') {
start += (linenr_T)bigness * (linenr_T)(x - kind - 1) + 1;
} else if (eap->addr_count == 0) {
- ++start;
+ start++;
}
end = start + (linenr_T)bigness - 1;
curs = end;
@@ -3343,6 +3353,7 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, char *sub, char *cmd, boo
if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) {
save_re_pat(RE_SUBST, (char_u *)pat, p_magic);
}
+ // put pattern in history
add_to_history(HIST_SEARCH, (char_u *)pat, true, NUL);
}
@@ -3541,7 +3552,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
which_pat = RE_LAST; // use last used regexp
delimiter = (char_u)(*cmd++); // remember delimiter character
pat = cmd; // remember start of search pat
- cmd = (char *)skip_regexp((char_u *)cmd, delimiter, p_magic, (char_u **)&eap->arg);
+ cmd = skip_regexp(cmd, delimiter, p_magic, &eap->arg);
if (cmd[0] == delimiter) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
has_second_delim = true;
@@ -3798,7 +3809,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// Save the line number of the last change for the final
// cursor position (just like Vi).
curwin->w_cursor.lnum = lnum;
- do_again = FALSE;
+ do_again = false;
/*
* 1. Match empty string does not count, except for first
@@ -3934,8 +3945,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
// what matches. Temporarily replace the line
// and change it back afterwards.
orig_line = (char *)vim_strsave(ml_get(lnum));
- char *new_line = (char *)concat_str((char_u *)new_start,
- (char_u *)sub_firstline + copycol);
+ char *new_line = concat_str(new_start, sub_firstline + copycol);
// Position the cursor relative to the end of the line, the
// previous substitute may have inserted or deleted characters
@@ -3949,13 +3959,13 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
- regmatch.startpos[0].lnum;
search_match_endcol = regmatch.endpos[0].col
+ len_change;
- highlight_match = TRUE;
+ highlight_match = true;
update_topline(curwin);
validate_cursor();
- update_screen(SOME_VALID);
+ update_screen(UPD_SOME_VALID);
highlight_match = false;
- redraw_later(curwin, SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
curwin->w_p_fen = save_p_fen;
if (msg_row == Rows - 1) {
@@ -3971,7 +3981,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T
_("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
msg_no_more = false;
msg_scroll = (int)i;
- showruler(true);
+ show_cursor_info(true);
ui_cursor_goto(msg_row, msg_col);
RedrawingDisabled = temp;
@@ -4296,7 +4306,7 @@ skip:
* has been appended to new_start, we don't need
* it in the buffer.
*/
- ++lnum;
+ lnum++;
if (u_savedel(lnum, nmatch_tl) != OK) {
break;
}
@@ -4626,7 +4636,7 @@ void ex_global(exarg_T *eap)
delim = *cmd; // get the delimiter
cmd++; // skip delimiter if there is one
pat = cmd; // remember start of pattern
- cmd = (char *)skip_regexp((char_u *)cmd, delim, p_magic, (char_u **)&eap->arg);
+ cmd = skip_regexp(cmd, delim, p_magic, &eap->arg);
if (cmd[0] == delim) { // end delimiter found
*cmd++ = NUL; // replace it with a NUL
}
@@ -4762,10 +4772,9 @@ bool prepare_tagpreview(bool undo_sync)
== FAIL) {
return false;
}
- curwin->w_p_pvw = TRUE;
- curwin->w_p_wfh = TRUE;
- RESET_BINDING(curwin); /* don't take over 'scrollbind'
- and 'cursorbind' */
+ curwin->w_p_pvw = true;
+ curwin->w_p_wfh = true;
+ RESET_BINDING(curwin); // don't take over 'scrollbind' and 'cursorbind'
curwin->w_p_diff = false; // no 'diff'
set_string_option_direct("fdc", -1, // no 'foldcolumn'
"0", OPT_FREE, SID_NONE);
@@ -4775,1124 +4784,6 @@ bool prepare_tagpreview(bool undo_sync)
return false;
}
-/// ":help": open a read-only window on a help file
-void ex_help(exarg_T *eap)
-{
- char *arg;
- char *tag;
- FILE *helpfd; // file descriptor of help file
- int n;
- int i;
- win_T *wp;
- int num_matches;
- char **matches;
- char *p;
- int empty_fnum = 0;
- int alt_fnum = 0;
- buf_T *buf;
- int len;
- char *lang;
- const bool old_KeyTyped = KeyTyped;
-
- if (eap != NULL) {
- /*
- * A ":help" command ends at the first LF, or at a '|' that is
- * followed by some text. Set nextcmd to the following command.
- */
- for (arg = eap->arg; *arg; arg++) {
- if (*arg == '\n' || *arg == '\r'
- || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) {
- *arg++ = NUL;
- eap->nextcmd = arg;
- break;
- }
- }
- arg = eap->arg;
-
- if (eap->forceit && *arg == NUL && !curbuf->b_help) {
- emsg(_("E478: Don't panic!"));
- return;
- }
-
- if (eap->skip) { // not executing commands
- return;
- }
- } else {
- arg = "";
- }
-
- // remove trailing blanks
- p = arg + STRLEN(arg) - 1;
- while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') {
- *p-- = NUL;
- }
-
- // Check for a specified language
- lang = check_help_lang(arg);
-
- // When no argument given go to the index.
- if (*arg == NUL) {
- arg = "help.txt";
- }
-
- /*
- * Check if there is a match for the argument.
- */
- n = find_help_tags(arg, &num_matches, &matches, eap != NULL && eap->forceit);
-
- i = 0;
- if (n != FAIL && lang != NULL) {
- // Find first item with the requested language.
- for (i = 0; i < num_matches; ++i) {
- len = (int)STRLEN(matches[i]);
- if (len > 3 && matches[i][len - 3] == '@'
- && STRICMP(matches[i] + len - 2, lang) == 0) {
- break;
- }
- }
- }
- if (i >= num_matches || n == FAIL) {
- if (lang != NULL) {
- semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg);
- } else {
- semsg(_("E149: Sorry, no help for %s"), arg);
- }
- if (n != FAIL) {
- FreeWild(num_matches, matches);
- }
- return;
- }
-
- // The first match (in the requested language) is the best match.
- tag = xstrdup(matches[i]);
- FreeWild(num_matches, matches);
-
- /*
- * Re-use an existing help window or open a new one.
- * Always open a new one for ":tab help".
- */
- if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {
- if (cmdmod.cmod_tab != 0) {
- wp = NULL;
- } else {
- wp = NULL;
- FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
- if (bt_help(wp2->w_buffer)) {
- wp = wp2;
- break;
- }
- }
- }
- if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
- win_enter(wp, true);
- } else {
- // There is no help window yet.
- // Try to open the file specified by the "helpfile" option.
- if ((helpfd = os_fopen((char *)p_hf, READBIN)) == NULL) {
- smsg(_("Sorry, help file \"%s\" not found"), p_hf);
- goto erret;
- }
- fclose(helpfd);
-
- // Split off help window; put it at far top if no position
- // specified, the current window is vertically split and
- // narrow.
- n = WSP_HELP;
- if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
- && curwin->w_width < 80) {
- n |= WSP_TOP;
- }
- if (win_split(0, n) == FAIL) {
- goto erret;
- }
-
- if (curwin->w_height < p_hh) {
- win_setheight((int)p_hh);
- }
-
- /*
- * Open help file (do_ecmd() will set b_help flag, readfile() will
- * set b_p_ro flag).
- * Set the alternate file to the previously edited file.
- */
- alt_fnum = curbuf->b_fnum;
- (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
- ECMD_HIDE + ECMD_SET_HELP,
- NULL); // buffer is still open, don't store info
-
- if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
- curwin->w_alt_fnum = alt_fnum;
- }
- empty_fnum = curbuf->b_fnum;
- }
- }
-
- restart_edit = 0; // don't want insert mode in help file
-
- // Restore KeyTyped, setting 'filetype=help' may reset it.
- // It is needed for do_tag top open folds under the cursor.
- KeyTyped = old_KeyTyped;
-
- do_tag((char_u *)tag, DT_HELP, 1, false, true);
-
- // Delete the empty buffer if we're not using it. Careful: autocommands
- // may have jumped to another window, check that the buffer is not in a
- // window.
- if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
- buf = buflist_findnr(empty_fnum);
- if (buf != NULL && buf->b_nwindows == 0) {
- wipe_buffer(buf, true);
- }
- }
-
- // keep the previous alternate file
- if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum
- && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
- curwin->w_alt_fnum = alt_fnum;
- }
-
-erret:
- xfree(tag);
-}
-
-/// In an argument search for a language specifiers in the form "@xx".
-/// Changes the "@" to NUL if found, and returns a pointer to "xx".
-///
-/// @return NULL if not found.
-char *check_help_lang(char *arg)
-{
- int len = (int)STRLEN(arg);
-
- if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
- && ASCII_ISALPHA(arg[len - 1])) {
- arg[len - 3] = NUL; // remove the '@'
- return arg + len - 2;
- }
- return NULL;
-}
-
-/// Return a heuristic indicating how well the given string matches. The
-/// smaller the number, the better the match. This is the order of priorities,
-/// from best match to worst match:
-/// - Match with least alphanumeric characters is better.
-/// - Match with least total characters is better.
-/// - Match towards the start is better.
-/// - Match starting with "+" is worse (feature instead of command)
-/// Assumption is made that the matched_string passed has already been found to
-/// match some string for which help is requested. webb.
-///
-/// @param offset offset for match
-/// @param wrong_case no matching case
-///
-/// @return a heuristic indicating how well the given string matches.
-int help_heuristic(char *matched_string, int offset, int wrong_case)
- FUNC_ATTR_PURE
-{
- int num_letters;
- char *p;
-
- num_letters = 0;
- for (p = matched_string; *p; p++) {
- if (ASCII_ISALNUM(*p)) {
- num_letters++;
- }
- }
-
- /*
- * Multiply the number of letters by 100 to give it a much bigger
- * weighting than the number of characters.
- * If there only is a match while ignoring case, add 5000.
- * If the match starts in the middle of a word, add 10000 to put it
- * somewhere in the last half.
- * If the match is more than 2 chars from the start, multiply by 200 to
- * put it after matches at the start.
- */
- if (offset > 0
- && ASCII_ISALNUM(matched_string[offset])
- && ASCII_ISALNUM(matched_string[offset - 1])) {
- offset += 10000;
- } else if (offset > 2) {
- offset *= 200;
- }
- if (wrong_case) {
- offset += 5000;
- }
- // Features are less interesting than the subjects themselves, but "+"
- // alone is not a feature.
- if (matched_string[0] == '+' && matched_string[1] != NUL) {
- offset += 100;
- }
- return 100 * num_letters + (int)STRLEN(matched_string) + offset;
-}
-
-/// Compare functions for qsort() below, that checks the help heuristics number
-/// that has been put after the tagname by find_tags().
-static int help_compare(const void *s1, const void *s2)
-{
- char *p1;
- char *p2;
-
- p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
- p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
-
- // Compare by help heuristic number first.
- int cmp = strcmp(p1, p2);
- if (cmp != 0) {
- return cmp;
- }
-
- // Compare by strings as tie-breaker when same heuristic number.
- return strcmp(*(char **)s1, *(char **)s2);
-}
-
-/// Find all help tags matching "arg", sort them and return in matches[], with
-/// the number of matches in num_matches.
-/// The matches will be sorted with a "best" match algorithm.
-/// When "keep_lang" is true try keeping the language of the current buffer.
-int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep_lang)
-{
- int i;
-
- // Specific tags that either have a specific replacement or won't go
- // through the generic rules.
- static char *(except_tbl[][2]) = {
- { "*", "star" },
- { "g*", "gstar" },
- { "[*", "[star" },
- { "]*", "]star" },
- { ":*", ":star" },
- { "/*", "/star" }, // NOLINT
- { "/\\*", "/\\\\star" },
- { "\"*", "quotestar" },
- { "**", "starstar" },
- { "cpo-*", "cpo-star" },
- { "/\\(\\)", "/\\\\(\\\\)" },
- { "/\\%(\\)", "/\\\\%(\\\\)" },
- { "?", "?" },
- { "??", "??" },
- { ":?", ":?" },
- { "?<CR>", "?<CR>" },
- { "g?", "g?" },
- { "g?g?", "g?g?" },
- { "g??", "g??" },
- { "-?", "-?" },
- { "q?", "q?" },
- { "v_g?", "v_g?" },
- { "/\\?", "/\\\\?" },
- { "/\\z(\\)", "/\\\\z(\\\\)" },
- { "\\=", "\\\\=" },
- { ":s\\=", ":s\\\\=" },
- { "[count]", "\\[count]" },
- { "[quotex]", "\\[quotex]" },
- { "[range]", "\\[range]" },
- { ":[range]", ":\\[range]" },
- { "[pattern]", "\\[pattern]" },
- { "\\|", "\\\\bar" },
- { "\\%$", "/\\\\%\\$" },
- { "s/\\~", "s/\\\\\\~" },
- { "s/\\U", "s/\\\\U" },
- { "s/\\L", "s/\\\\L" },
- { "s/\\1", "s/\\\\1" },
- { "s/\\2", "s/\\\\2" },
- { "s/\\3", "s/\\\\3" },
- { "s/\\9", "s/\\\\9" },
- { NULL, NULL }
- };
-
- static const char *(expr_table[]) = {
- "!=?", "!~?", "<=?", "<?", "==?", "=~?",
- ">=?", ">?", "is?", "isnot?"
- };
- char *d = (char *)IObuff; // assume IObuff is long enough!
- d[0] = NUL;
-
- if (STRNICMP(arg, "expr-", 5) == 0) {
- // When the string starting with "expr-" and containing '?' and matches
- // the table, it is taken literally (but ~ is escaped). Otherwise '?'
- // is recognized as a wildcard.
- for (i = (int)ARRAY_SIZE(expr_table); --i >= 0;) {
- if (STRCMP(arg + 5, expr_table[i]) == 0) {
- for (int si = 0, di = 0;; si++) {
- if (arg[si] == '~') {
- d[di++] = '\\';
- }
- d[di++] = arg[si];
- if (arg[si] == NUL) {
- break;
- }
- }
- break;
- }
- }
- } else {
- // Recognize a few exceptions to the rule. Some strings that contain
- // '*'are changed to "star", otherwise '*' is recognized as a wildcard.
- for (i = 0; except_tbl[i][0] != NULL; i++) {
- if (STRCMP(arg, except_tbl[i][0]) == 0) {
- STRCPY(d, except_tbl[i][1]);
- break;
- }
- }
- }
-
- if (d[0] == NUL) { // no match in table
- // Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
- // Also replace "\%^" and "\%(", they match every tag too.
- // Also "\zs", "\z1", etc.
- // Also "\@<", "\@=", "\@<=", etc.
- // And also "\_$" and "\_^".
- if (arg[0] == '\\'
- && ((arg[1] != NUL && arg[2] == NUL)
- || (vim_strchr("%_z@", arg[1]) != NULL
- && arg[2] != NUL))) {
- vim_snprintf(d, IOSIZE, "/\\\\%s", arg + 1);
- // Check for "/\\_$", should be "/\\_\$"
- if (d[3] == '_' && d[4] == '$') {
- STRCPY(d + 4, "\\$");
- }
- } else {
- // Replace:
- // "[:...:]" with "\[:...:]"
- // "[++...]" with "\[++...]"
- // "\{" with "\\{" -- matching "} \}"
- if ((arg[0] == '[' && (arg[1] == ':'
- || (arg[1] == '+' && arg[2] == '+')))
- || (arg[0] == '\\' && arg[1] == '{')) {
- *d++ = '\\';
- }
-
- // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
- if (*arg == '(' && arg[1] == '\'') {
- arg++;
- }
- for (const char *s = arg; *s; s++) {
- // Replace "|" with "bar" and '"' with "quote" to match the name of
- // the tags for these commands.
- // Replace "*" with ".*" and "?" with "." to match command line
- // completion.
- // Insert a backslash before '~', '$' and '.' to avoid their
- // special meaning.
- if ((char_u *)d - IObuff > IOSIZE - 10) { // getting too long!?
- break;
- }
- switch (*s) {
- case '|':
- STRCPY(d, "bar");
- d += 3;
- continue;
- case '"':
- STRCPY(d, "quote");
- d += 5;
- continue;
- case '*':
- *d++ = '.';
- break;
- case '?':
- *d++ = '.';
- continue;
- case '$':
- case '.':
- case '~':
- *d++ = '\\';
- break;
- }
-
- /*
- * Replace "^x" by "CTRL-X". Don't do this for "^_" to make
- * ":help i_^_CTRL-D" work.
- * Insert '-' before and after "CTRL-X" when applicable.
- */
- if (*s < ' '
- || (*s == '^' && s[1]
- && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
- if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
- *d++ = '_'; // prepend a '_' to make x_CTRL-x
- }
- STRCPY(d, "CTRL-");
- d += 5;
- if (*s < ' ') {
- *d++ = (char)(*s + '@');
- if (d[-1] == '\\') {
- *d++ = '\\'; // double a backslash
- }
- } else {
- *d++ = *++s;
- }
- if (s[1] != NUL && s[1] != '_') {
- *d++ = '_'; // append a '_'
- }
- continue;
- } else if (*s == '^') { // "^" or "CTRL-^" or "^_"
- *d++ = '\\';
- }
- /*
- * Insert a backslash before a backslash after a slash, for search
- * pattern tags: "/\|" --> "/\\|".
- */
- else if (s[0] == '\\' && s[1] != '\\'
- && *arg == '/' && s == arg + 1) {
- *d++ = '\\';
- }
-
- // "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
- // "CTRL-\_CTRL-N"
- if (STRNICMP(s, "CTRL-\\_", 7) == 0) {
- STRCPY(d, "CTRL-\\\\");
- d += 7;
- s += 6;
- }
-
- *d++ = *s;
-
- // If tag contains "({" or "([", tag terminates at the "(".
- // This is for help on functions, e.g.: abs({expr}).
- if (*s == '(' && (s[1] == '{' || s[1] == '[')) {
- break;
- }
-
- // If tag starts with ', toss everything after a second '. Fixes
- // CTRL-] on 'option'. (would include the trailing '.').
- if (*s == '\'' && s > arg && *arg == '\'') {
- break;
- }
- // Also '{' and '}'. Fixes CTRL-] on '{address}'.
- if (*s == '}' && s > arg && *arg == '{') {
- break;
- }
- }
- *d = NUL;
-
- if (*IObuff == '`') {
- if ((char_u *)d > IObuff + 2 && d[-1] == '`') {
- // remove the backticks from `command`
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
- d[-2] = NUL;
- } else if ((char_u *)d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
- // remove the backticks and comma from `command`,
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
- d[-3] = NUL;
- } else if ((char_u *)d > IObuff + 4 && d[-3] == '`'
- && d[-2] == '\\' && d[-1] == '.') {
- // remove the backticks and dot from `command`\.
- memmove(IObuff, IObuff + 1, STRLEN(IObuff));
- d[-4] = NUL;
- }
- }
- }
- }
-
- *matches = NULL;
- *num_matches = 0;
- int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC;
- if (keep_lang) {
- flags |= TAG_KEEP_LANG;
- }
- if (find_tags(IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
- && *num_matches > 0) {
- // Sort the matches found on the heuristic number that is after the
- // tag name.
- qsort((void *)(*matches), (size_t)(*num_matches),
- sizeof(char_u *), help_compare);
- // Delete more than TAG_MANY to reduce the size of the listing.
- while (*num_matches > TAG_MANY) {
- xfree((*matches)[--*num_matches]);
- }
- }
- return OK;
-}
-
-/// Called when starting to edit a buffer for a help file.
-static void prepare_help_buffer(void)
-{
- curbuf->b_help = true;
- set_string_option_direct("buftype", -1, "help", OPT_FREE|OPT_LOCAL, 0);
-
- // Always set these options after jumping to a help tag, because the
- // user may have an autocommand that gets in the way.
- // Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
- // latin1 word characters (for translated help files).
- // Only set it when needed, buf_init_chartab() is some work.
- char *p = "!-~,^*,^|,^\",192-255";
- if (STRCMP(curbuf->b_p_isk, p) != 0) {
- set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
- check_buf_options(curbuf);
- (void)buf_init_chartab(curbuf, FALSE);
- }
-
- // Don't use the global foldmethod.
- set_string_option_direct("fdm", -1, "manual", OPT_FREE|OPT_LOCAL, 0);
-
- curbuf->b_p_ts = 8; // 'tabstop' is 8.
- curwin->w_p_list = FALSE; // No list mode.
-
- curbuf->b_p_ma = FALSE; // Not modifiable.
- curbuf->b_p_bin = FALSE; // Reset 'bin' before reading file.
- curwin->w_p_nu = 0; // No line numbers.
- curwin->w_p_rnu = 0; // No relative line numbers.
- RESET_BINDING(curwin); // No scroll or cursor binding.
- curwin->w_p_arab = FALSE; // No arabic mode.
- curwin->w_p_rl = FALSE; // Help window is left-to-right.
- curwin->w_p_fen = FALSE; // No folding in the help window.
- curwin->w_p_diff = FALSE; // No 'diff'.
- curwin->w_p_spell = FALSE; // No spell checking.
-
- set_buflisted(FALSE);
-}
-
-/// After reading a help file: May cleanup a help buffer when syntax
-/// highlighting is not used.
-void fix_help_buffer(void)
-{
- linenr_T lnum;
- char *line;
- bool in_example = false;
-
- // Set filetype to "help".
- if (STRCMP(curbuf->b_p_ft, "help") != 0) {
- curbuf->b_ro_locked++;
- set_option_value("ft", 0L, "help", OPT_LOCAL);
- curbuf->b_ro_locked--;
- }
-
- if (!syntax_present(curwin)) {
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
- line = (char *)ml_get_buf(curbuf, lnum, false);
- const size_t len = STRLEN(line);
- if (in_example && len > 0 && !ascii_iswhite(line[0])) {
- // End of example: non-white or '<' in first column.
- if (line[0] == '<') {
- // blank-out a '<' in the first column
- line = (char *)ml_get_buf(curbuf, lnum, true);
- line[0] = ' ';
- }
- in_example = false;
- }
- if (!in_example && len > 0) {
- if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) {
- // blank-out a '>' in the last column (start of example)
- line = (char *)ml_get_buf(curbuf, lnum, true);
- line[len - 1] = ' ';
- in_example = true;
- } else if (line[len - 1] == '~') {
- // blank-out a '~' at the end of line (header marker)
- line = (char *)ml_get_buf(curbuf, lnum, true);
- line[len - 1] = ' ';
- }
- }
- }
- }
-
- /*
- * In the "help.txt" and "help.abx" file, add the locally added help
- * files. This uses the very first line in the help file.
- */
- char *const fname = path_tail(curbuf->b_fname);
- if (FNAMECMP(fname, "help.txt") == 0
- || (FNAMENCMP(fname, "help.", 5) == 0
- && ASCII_ISALPHA(fname[5])
- && ASCII_ISALPHA(fname[6])
- && TOLOWER_ASC(fname[7]) == 'x'
- && fname[8] == NUL)) {
- for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) {
- line = (char *)ml_get_buf(curbuf, lnum, false);
- if (strstr(line, "*local-additions*") == NULL) {
- continue;
- }
-
- // Go through all directories in 'runtimepath', skipping
- // $VIMRUNTIME.
- char *p = (char *)p_rtp;
- while (*p != NUL) {
- copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
- char *const rt = vim_getenv("VIMRUNTIME");
- if (rt != NULL
- && path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
- int fcount;
- char **fnames;
- char *s;
- vimconv_T vc;
- char *cp;
-
- // Find all "doc/ *.txt" files in this directory.
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, "doc/*.??[tx]",
- sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- continue;
- }
-
- // Note: We cannot just do `&NameBuff` because it is a statically sized array
- // so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
- if (gen_expand_wildcards(1, buff_list, &fcount,
- &fnames, EW_FILE|EW_SILENT) == OK
- && fcount > 0) {
- // If foo.abx is found use it instead of foo.txt in
- // the same directory.
- for (int i1 = 0; i1 < fcount; i1++) {
- for (int i2 = 0; i2 < fcount; i2++) {
- if (i1 == i2) {
- continue;
- }
- if (fnames[i1] == NULL || fnames[i2] == NULL) {
- continue;
- }
- const char *const f1 = fnames[i1];
- const char *const f2 = fnames[i2];
- const char *const t1 = path_tail(f1);
- const char *const t2 = path_tail(f2);
- const char *const e1 = (char *)STRRCHR(t1, '.');
- const char *const e2 = (char *)STRRCHR(t2, '.');
- if (e1 == NULL || e2 == NULL) {
- continue;
- }
- if (FNAMECMP(e1, ".txt") != 0
- && FNAMECMP(e1, fname + 4) != 0) {
- // Not .txt and not .abx, remove it.
- XFREE_CLEAR(fnames[i1]);
- continue;
- }
- if (e1 - f1 != e2 - f2
- || FNAMENCMP(f1, f2, e1 - f1) != 0) {
- continue;
- }
- if (FNAMECMP(e1, ".txt") == 0
- && FNAMECMP(e2, fname + 4) == 0) {
- // use .abx instead of .txt
- XFREE_CLEAR(fnames[i1]);
- }
- }
- }
- for (int fi = 0; fi < fcount; fi++) {
- if (fnames[fi] == NULL) {
- continue;
- }
-
- FILE *const fd = os_fopen(fnames[fi], "r");
- if (fd == NULL) {
- continue;
- }
- vim_fgets(IObuff, IOSIZE, fd);
- if (IObuff[0] == '*'
- && (s = vim_strchr((char *)IObuff + 1, '*'))
- != NULL) {
- TriState this_utf = kNone;
- // Change tag definition to a
- // reference and remove <CR>/<NL>.
- IObuff[0] = '|';
- *s = '|';
- while (*s != NUL) {
- if (*s == '\r' || *s == '\n') {
- *s = NUL;
- }
- // The text is utf-8 when a byte
- // above 127 is found and no
- // illegal byte sequence is found.
- if ((char_u)(*s) >= 0x80 && this_utf != kFalse) {
- this_utf = kTrue;
- const int l = utf_ptr2len(s);
- if (l == 1) {
- this_utf = kFalse;
- }
- s += l - 1;
- }
- ++s;
- }
- // The help file is latin1 or utf-8;
- // conversion to the current
- // 'encoding' may be required.
- vc.vc_type = CONV_NONE;
- convert_setup(&vc,
- (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"),
- p_enc);
- if (vc.vc_type == CONV_NONE) {
- // No conversion needed.
- cp = (char *)IObuff;
- } else {
- // Do the conversion. If it fails
- // use the unconverted text.
- cp = (char *)string_convert(&vc, IObuff, NULL);
- if (cp == NULL) {
- cp = (char *)IObuff;
- }
- }
- convert_setup(&vc, NULL, NULL);
-
- ml_append(lnum, cp, (colnr_T)0, false);
- if ((char_u *)cp != IObuff) {
- xfree(cp);
- }
- lnum++;
- }
- fclose(fd);
- }
- FreeWild(fcount, fnames);
- }
- }
- xfree(rt);
- }
- break;
- }
- }
-}
-
-/// ":exusage"
-void ex_exusage(exarg_T *eap)
-{
- do_cmdline_cmd("help ex-cmd-index");
-}
-
-/// ":viusage"
-void ex_viusage(exarg_T *eap)
-{
- do_cmdline_cmd("help normal-index");
-}
-
-/// Generate tags in one help directory
-///
-/// @param dir Path to the doc directory
-/// @param ext Suffix of the help files (".txt", ".itx", ".frx", etc.)
-/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for
-/// French)
-/// @param add_help_tags Whether to add the "help-tags" tag
-/// @param ignore_writeerr ignore write error
-static void helptags_one(char *dir, const char *ext, const char *tagfname, bool add_help_tags,
- bool ignore_writeerr)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T ga;
- int filecount;
- char **files;
- char *p1, *p2;
- char *s;
- TriState utf8 = kNone;
- bool mix = false; // detected mixed encodings
-
- // Find all *.txt files.
- size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
- if (dirlen >= MAXPATHL
- || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
- || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- return;
- }
-
- // Note: We cannot just do `&NameBuff` because it is a statically sized array
- // so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
- const int res = gen_expand_wildcards(1, buff_list, &filecount, &files,
- EW_FILE|EW_SILENT);
- if (res == FAIL || filecount == 0) {
- if (!got_int) {
- semsg(_("E151: No match: %s"), NameBuff);
- }
- if (res != FAIL) {
- FreeWild(filecount, files);
- }
- return;
- }
-
- //
- // Open the tags file for writing.
- // Do this before scanning through all the files.
- //
- memcpy(NameBuff, dir, dirlen + 1);
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- return;
- }
-
- FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
- if (fd_tags == NULL) {
- if (!ignore_writeerr) {
- semsg(_("E152: Cannot open %s for writing"), NameBuff);
- }
- FreeWild(filecount, files);
- return;
- }
-
- // If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
- // add the "help-tags" tag.
- ga_init(&ga, (int)sizeof(char_u *), 100);
- if (add_help_tags
- || path_full_compare("$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) {
- size_t s_len = 18 + STRLEN(tagfname);
- s = xmalloc(s_len);
- snprintf(s, s_len, "help-tags\t%s\t1\n", tagfname);
- GA_APPEND(char *, &ga, s);
- }
-
- // Go over all the files and extract the tags.
- for (int fi = 0; fi < filecount && !got_int; fi++) {
- FILE *const fd = os_fopen(files[fi], "r");
- if (fd == NULL) {
- semsg(_("E153: Unable to open %s for reading"), files[fi]);
- continue;
- }
- const char *const fname = files[fi] + dirlen + 1;
-
- bool firstline = true;
- while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
- if (firstline) {
- // Detect utf-8 file by a non-ASCII char in the first line.
- TriState this_utf8 = kNone;
- for (s = (char *)IObuff; *s != NUL; s++) {
- if ((char_u)(*s) >= 0x80) {
- this_utf8 = kTrue;
- const int l = utf_ptr2len(s);
- if (l == 1) {
- // Illegal UTF-8 byte sequence.
- this_utf8 = kFalse;
- break;
- }
- s += l - 1;
- }
- }
- if (this_utf8 == kNone) { // only ASCII characters found
- this_utf8 = kFalse;
- }
- if (utf8 == kNone) { // first file
- utf8 = this_utf8;
- } else if (utf8 != this_utf8) {
- semsg(_("E670: Mix of help file encodings within a language: %s"),
- files[fi]);
- mix = !got_int;
- got_int = TRUE;
- }
- firstline = false;
- }
- p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
- while (p1 != NULL) {
- p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
- if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
- for (s = p1 + 1; s < p2; s++) {
- if (*s == ' ' || *s == '\t' || *s == '|') {
- break;
- }
- }
-
- // Only accept a *tag* when it consists of valid
- // characters, there is white space before it and is
- // followed by a white character or end-of-line.
- if (s == p2
- && ((char_u *)p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
- && (vim_strchr(" \t\n\r", s[1]) != NULL
- || s[1] == '\0')) {
- *p2 = '\0';
- p1++;
- size_t s_len= (size_t)(p2 - p1) + STRLEN(fname) + 2;
- s = xmalloc(s_len);
- GA_APPEND(char *, &ga, s);
- snprintf(s, s_len, "%s\t%s", p1, fname);
-
- // find next '*'
- p2 = vim_strchr(p2 + 1, '*');
- }
- }
- p1 = p2;
- }
- line_breakcheck();
- }
-
- fclose(fd);
- }
-
- FreeWild(filecount, files);
-
- if (!got_int && ga.ga_data != NULL) {
- // Sort the tags.
- sort_strings(ga.ga_data, ga.ga_len);
-
- // Check for duplicates.
- for (int i = 1; i < ga.ga_len; i++) {
- p1 = ((char **)ga.ga_data)[i - 1];
- p2 = ((char **)ga.ga_data)[i];
- while (*p1 == *p2) {
- if (*p2 == '\t') {
- *p2 = NUL;
- vim_snprintf((char *)NameBuff, MAXPATHL,
- _("E154: Duplicate tag \"%s\" in file %s/%s"),
- ((char_u **)ga.ga_data)[i], dir, p2 + 1);
- emsg((char *)NameBuff);
- *p2 = '\t';
- break;
- }
- ++p1;
- ++p2;
- }
- }
-
- if (utf8 == kTrue) {
- fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
- }
-
- // Write the tags into the file.
- for (int i = 0; i < ga.ga_len; i++) {
- s = ((char **)ga.ga_data)[i];
- if (STRNCMP(s, "help-tags\t", 10) == 0) {
- // help-tags entry was added in formatted form
- fputs(s, fd_tags);
- } else {
- fprintf(fd_tags, "%s\t/" "*", s);
- for (p1 = s; *p1 != '\t'; p1++) {
- // insert backslash before '\\' and '/'
- if (*p1 == '\\' || *p1 == '/') {
- putc('\\', fd_tags);
- }
- putc(*p1, fd_tags);
- }
- fprintf(fd_tags, "*\n");
- }
- }
- }
- if (mix) {
- got_int = false; // continue with other languages
- }
-
- GA_DEEP_CLEAR_PTR(&ga);
- fclose(fd_tags); // there is no check for an error...
-}
-
-/// Generate tags in one help directory, taking care of translations.
-static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
- FUNC_ATTR_NONNULL_ALL
-{
- int len;
- garray_T ga;
- char lang[2];
- char ext[5];
- char fname[8];
- int filecount;
- char **files;
-
- // Get a list of all files in the help directory and in subdirectories.
- STRLCPY(NameBuff, dirname, sizeof(NameBuff));
- if (!add_pathsep((char *)NameBuff)
- || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
- emsg(_(e_fnametoolong));
- return;
- }
-
- // Note: We cannot just do `&NameBuff` because it is a statically sized array
- // so `NameBuff == &NameBuff` according to C semantics.
- char *buff_list[1] = { (char *)NameBuff };
- if (gen_expand_wildcards(1, buff_list, &filecount, &files,
- EW_FILE|EW_SILENT) == FAIL
- || filecount == 0) {
- semsg(_("E151: No match: %s"), NameBuff);
- return;
- }
-
- // Go over all files in the directory to find out what languages are
- // present.
- int j;
- ga_init(&ga, 1, 10);
- for (int i = 0; i < filecount; i++) {
- len = (int)STRLEN(files[i]);
- if (len <= 4) {
- continue;
- }
-
- if (STRICMP(files[i] + len - 4, ".txt") == 0) {
- // ".txt" -> language "en"
- lang[0] = 'e';
- lang[1] = 'n';
- } else if (files[i][len - 4] == '.'
- && ASCII_ISALPHA(files[i][len - 3])
- && ASCII_ISALPHA(files[i][len - 2])
- && TOLOWER_ASC(files[i][len - 1]) == 'x') {
- // ".abx" -> language "ab"
- lang[0] = (char)TOLOWER_ASC(files[i][len - 3]);
- lang[1] = (char)TOLOWER_ASC(files[i][len - 2]);
- } else {
- continue;
- }
-
- // Did we find this language already?
- for (j = 0; j < ga.ga_len; j += 2) {
- if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) {
- break;
- }
- }
- if (j == ga.ga_len) {
- // New language, add it.
- ga_grow(&ga, 2);
- ((char *)ga.ga_data)[ga.ga_len++] = lang[0];
- ((char *)ga.ga_data)[ga.ga_len++] = lang[1];
- }
- }
-
- /*
- * Loop over the found languages to generate a tags file for each one.
- */
- for (j = 0; j < ga.ga_len; j += 2) {
- STRCPY(fname, "tags-xx");
- fname[5] = ((char *)ga.ga_data)[j];
- fname[6] = ((char *)ga.ga_data)[j + 1];
- if (fname[5] == 'e' && fname[6] == 'n') {
- // English is an exception: use ".txt" and "tags".
- fname[4] = NUL;
- STRCPY(ext, ".txt");
- } else {
- // Language "ab" uses ".abx" and "tags-ab".
- STRCPY(ext, ".xxx");
- ext[1] = fname[5];
- ext[2] = fname[6];
- }
- helptags_one(dirname, (char *)ext, (char *)fname, add_help_tags, ignore_writeerr);
- }
-
- ga_clear(&ga);
- FreeWild(filecount, files);
-}
-
-static void helptags_cb(char *fname, void *cookie)
- FUNC_ATTR_NONNULL_ALL
-{
- do_helptags(fname, *(bool *)cookie, true);
-}
-
-/// ":helptags"
-void ex_helptags(exarg_T *eap)
-{
- expand_T xpc;
- char *dirname;
- bool add_help_tags = false;
-
- // Check for ":helptags ++t {dir}".
- if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
- add_help_tags = true;
- eap->arg = skipwhite(eap->arg + 3);
- }
-
- if (STRCMP(eap->arg, "ALL") == 0) {
- do_in_path(p_rtp, "doc", DIP_ALL + DIP_DIR, helptags_cb, &add_help_tags);
- } else {
- ExpandInit(&xpc);
- xpc.xp_context = EXPAND_DIRECTORIES;
- dirname = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL,
- WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
- if (dirname == NULL || !os_isdir((char_u *)dirname)) {
- semsg(_("E150: Not a directory: %s"), eap->arg);
- } else {
- do_helptags(dirname, add_help_tags, false);
- }
- xfree(dirname);
- }
-}
-
-/// ":helpclose": Close one help window
-void ex_helpclose(exarg_T *eap)
-{
- FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
- if (bt_help(win->w_buffer)) {
- win_close(win, false, eap->forceit);
- return;
- }
- }
-}
-
/// Shows the effects of the :substitute command being typed ('inccommand').
/// If inccommand=split, shows a preview window and later restores the layout.
///
@@ -5901,7 +4792,7 @@ static int show_sub(exarg_T *eap, pos_T old_cusr, PreviewLines *preview_lines, i
long cmdpreview_ns, handle_T cmdpreview_bufnr)
FUNC_ATTR_NONNULL_ALL
{
- char *save_shm_p = (char *)vim_strsave(p_shm);
+ char *save_shm_p = xstrdup(p_shm);
PreviewLines lines = *preview_lines;
buf_T *orig_buf = curbuf;
// We keep a special-purpose buffer around, but don't assume it exists.
@@ -6052,7 +4943,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
if (s != NULL) {
*s = p;
}
- p = (char *)skiptowhite((char_u *)p);
+ p = skiptowhite(p);
if (s != NULL && *p != NUL) {
*p++ = NUL;
}
@@ -6062,7 +4953,7 @@ char *skip_vimgrep_pat(char *p, char **s, int *flags)
*s = p + 1;
}
c = (char_u)(*p);
- p = (char *)skip_regexp((char_u *)p + 1, c, true, NULL);
+ p = skip_regexp(p + 1, c, true, NULL);
if (*p != c) {
return NULL;
}
@@ -6107,7 +4998,7 @@ void ex_oldfiles(exarg_T *eap)
}
nr++;
const char *fname = tv_get_string(TV_LIST_ITEM_TV(li));
- if (!message_filtered((char_u *)fname)) {
+ if (!message_filtered((char *)fname)) {
msg_outnum(nr);
msg_puts(": ");
msg_outtrans((char *)tv_get_string(TV_LIST_ITEM_TV(li)));
diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h
index a55e74a789..3aaba9ce42 100644
--- a/src/nvim/ex_cmds.h
+++ b/src/nvim/ex_cmds.h
@@ -20,9 +20,9 @@
#define ECMD_NOWINENTER 0x40 // do not trigger BufWinEnter
// for lnum argument in do_ecmd()
-#define ECMD_LASTL (linenr_T)0 // use last position in loaded file
-#define ECMD_LAST ((linenr_T) - 1) // use last position in all files
-#define ECMD_ONE (linenr_T)1 // use first line
+#define ECMD_LASTL (linenr_T)0 // use last position in loaded file
+#define ECMD_LAST ((linenr_T)(-1)) // use last position in all files
+#define ECMD_ONE (linenr_T)1 // use first line
/// Previous :substitute replacement string definition
typedef struct {
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index a5ba5e0b30..4bed1e94b9 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -107,6 +107,12 @@ module.cmds = {
func='ex_listdo',
},
{
+ command='argdedupe',
+ flags=TRLBAR,
+ addr_type='ADDR_NONE',
+ func='ex_argdedupe',
+ },
+ {
command='argedit',
flags=bit.bor(BANG, NEEDARG, RANGE, ZEROR, FILES, CMDARG, ARGOPT, TRLBAR),
addr_type='ADDR_ARGUMENTS',
@@ -3175,7 +3181,7 @@ module.cmds = {
},
{
command='wincmd',
- flags=bit.bor(NEEDARG, WORD1, RANGE, CMDWIN, LOCK_OK),
+ flags=bit.bor(NEEDARG, WORD1, RANGE, COUNT, CMDWIN, LOCK_OK),
addr_type='ADDR_OTHER',
func='ex_wincmd',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 730a2f1b69..11280eecbb 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -11,30 +11,23 @@
#include <stdbool.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
-#include "nvim/globals.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
-#include "nvim/api/private/defs.h"
-#include "nvim/api/private/helpers.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
-#include "nvim/debugger.h"
-#include "nvim/eval/userfunc.h"
+#include "nvim/eval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
-#include "nvim/garray.h"
-#include "nvim/lua/executor.h"
+#include "nvim/globals.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
-#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
@@ -42,101 +35,19 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/os/fs_defs.h"
-#include "nvim/os/input.h"
-#include "nvim/os/shell.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/profile.h"
#include "nvim/quickfix.h"
-#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/undo.h"
-#include "nvim/version.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
-/// Growarray to store info about already sourced scripts.
-static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
-#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
-
-// Struct used in sn_prl_ga for every line of a script.
-typedef struct sn_prl_S {
- int snp_count; ///< nr of times line was executed
- proftime_T sn_prl_total; ///< time spent in a line + children
- proftime_T sn_prl_self; ///< time spent in a line itself
-} sn_prl_T;
-
-/// Structure used to store info for each sourced file.
-/// It is shared between do_source() and getsourceline().
-/// This is required, because it needs to be handed to do_cmdline() and
-/// sourcing can be done recursively.
-struct source_cookie {
- FILE *fp; ///< opened file for sourcing
- char *nextline; ///< if not NULL: line that was read ahead
- linenr_T sourcing_lnum; ///< line number of the source file
- int finished; ///< ":finish" used
-#if defined(USE_CRNL)
- int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
- bool error; ///< true if LF found after CR-LF
-#endif
- linenr_T breakpoint; ///< next line with breakpoint or zero
- char *fname; ///< name of sourced file
- int dbg_tick; ///< debug_tick when breakpoint was set
- int level; ///< top nesting level of sourced file
- vimconv_T conv; ///< type of conversion
-};
-
-#define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds2.c.generated.h"
#endif
-static char *profile_fname = NULL;
-
-/// ":profile cmd args"
-void ex_profile(exarg_T *eap)
-{
- static proftime_T pause_time;
-
- char *e;
- int len;
-
- e = (char *)skiptowhite((char_u *)eap->arg);
- len = (int)(e - eap->arg);
- e = skipwhite(e);
-
- if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
- xfree(profile_fname);
- profile_fname = (char *)expand_env_save_opt((char_u *)e, true);
- do_profiling = PROF_YES;
- profile_set_wait(profile_zero());
- set_vim_var_nr(VV_PROFILING, 1L);
- } else if (do_profiling == PROF_NONE) {
- emsg(_("E750: First use \":profile start {fname}\""));
- } else if (STRCMP(eap->arg, "stop") == 0) {
- profile_dump();
- do_profiling = PROF_NONE;
- set_vim_var_nr(VV_PROFILING, 0L);
- profile_reset();
- } else if (STRCMP(eap->arg, "pause") == 0) {
- if (do_profiling == PROF_YES) {
- pause_time = profile_start();
- }
- do_profiling = PROF_PAUSED;
- } else if (STRCMP(eap->arg, "continue") == 0) {
- if (do_profiling == PROF_PAUSED) {
- pause_time = profile_end(pause_time);
- profile_set_wait(profile_add(profile_get_wait(), pause_time));
- }
- do_profiling = PROF_YES;
- } else if (STRCMP(eap->arg, "dump") == 0) {
- profile_dump();
- } else {
- // The rest is similar to ":breakadd".
- ex_breakadd(eap);
- }
-}
-
void ex_ruby(exarg_T *eap)
{
script_host_execute("ruby", eap);
@@ -182,273 +93,6 @@ void ex_perldo(exarg_T *eap)
script_host_do_range("perl", eap);
}
-// Command line expansion for :profile.
-static enum {
- PEXP_SUBCMD, ///< expand :profile sub-commands
- PEXP_FUNC, ///< expand :profile func {funcname}
-} pexpand_what;
-
-static char *pexpand_cmds[] = {
- "continue",
- "dump",
- "file",
- "func",
- "pause",
- "start",
- "stop",
- NULL
-};
-
-/// Function given to ExpandGeneric() to obtain the profile command
-/// specific expansion.
-char *get_profile_name(expand_T *xp, int idx)
- FUNC_ATTR_PURE
-{
- switch (pexpand_what) {
- case PEXP_SUBCMD:
- return pexpand_cmds[idx];
- // case PEXP_FUNC: TODO
- default:
- return NULL;
- }
-}
-
-/// Handle command line completion for :profile command.
-void set_context_in_profile_cmd(expand_T *xp, const char *arg)
-{
- // Default: expand subcommands.
- xp->xp_context = EXPAND_PROFILE;
- pexpand_what = PEXP_SUBCMD;
- xp->xp_pattern = (char *)arg;
-
- char_u *const end_subcmd = skiptowhite((const char_u *)arg);
- if (*end_subcmd == NUL) {
- return;
- }
-
- if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
- xp->xp_context = EXPAND_FILES;
- xp->xp_pattern = skipwhite((char *)end_subcmd);
- return;
- }
-
- // TODO(tarruda): expand function names after "func"
- xp->xp_context = EXPAND_NOTHING;
-}
-
-/// Dump the profiling info.
-void profile_dump(void)
-{
- FILE *fd;
-
- if (profile_fname != NULL) {
- fd = os_fopen(profile_fname, "w");
- if (fd == NULL) {
- semsg(_(e_notopen), profile_fname);
- } else {
- script_dump_profile(fd);
- func_dump_profile(fd);
- fclose(fd);
- }
- }
-}
-
-/// Reset all profiling information.
-static void profile_reset(void)
-{
- // Reset sourced files.
- for (int id = 1; id <= script_items.ga_len; id++) {
- scriptitem_T *si = &SCRIPT_ITEM(id);
- if (si->sn_prof_on) {
- si->sn_prof_on = false;
- si->sn_pr_force = false;
- si->sn_pr_child = profile_zero();
- si->sn_pr_nest = 0;
- si->sn_pr_count = 0;
- si->sn_pr_total = profile_zero();
- si->sn_pr_self = profile_zero();
- si->sn_pr_start = profile_zero();
- si->sn_pr_children = profile_zero();
- ga_clear(&si->sn_prl_ga);
- si->sn_prl_start = profile_zero();
- si->sn_prl_children = profile_zero();
- si->sn_prl_wait = profile_zero();
- si->sn_prl_idx = -1;
- si->sn_prl_execed = 0;
- }
- }
-
- // Reset functions.
- size_t n = func_hashtab.ht_used;
- hashitem_T *hi = func_hashtab.ht_array;
-
- for (; n > (size_t)0; hi++) {
- if (!HASHITEM_EMPTY(hi)) {
- n--;
- ufunc_T *uf = HI2UF(hi);
- if (uf->uf_prof_initialized) {
- uf->uf_profiling = 0;
- uf->uf_tm_count = 0;
- uf->uf_tm_total = profile_zero();
- uf->uf_tm_self = profile_zero();
- uf->uf_tm_children = profile_zero();
-
- for (int i = 0; i < uf->uf_lines.ga_len; i++) {
- uf->uf_tml_count[i] = 0;
- uf->uf_tml_total[i] = uf->uf_tml_self[i] = 0;
- }
-
- uf->uf_tml_start = profile_zero();
- uf->uf_tml_children = profile_zero();
- uf->uf_tml_wait = profile_zero();
- uf->uf_tml_idx = -1;
- uf->uf_tml_execed = 0;
- }
- }
- }
-
- XFREE_CLEAR(profile_fname);
-}
-
-/// Start profiling a script.
-static void profile_init(scriptitem_T *si)
-{
- si->sn_pr_count = 0;
- si->sn_pr_total = profile_zero();
- si->sn_pr_self = profile_zero();
-
- ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
- si->sn_prl_idx = -1;
- si->sn_prof_on = true;
- si->sn_pr_nest = 0;
-}
-
-/// Save time when starting to invoke another script or function.
-///
-/// @param tm place to store wait time
-void script_prof_save(proftime_T *tm)
-{
- scriptitem_T *si;
-
- if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && si->sn_pr_nest++ == 0) {
- si->sn_pr_child = profile_start();
- }
- }
- *tm = profile_get_wait();
-}
-
-/// Count time spent in children after invoking another script or function.
-void script_prof_restore(proftime_T *tm)
-{
- scriptitem_T *si;
-
- if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && --si->sn_pr_nest == 0) {
- si->sn_pr_child = profile_end(si->sn_pr_child);
- // don't count wait time
- si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
- si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
- si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
- }
- }
-}
-
-static proftime_T inchar_time;
-
-/// Called when starting to wait for the user to type a character.
-void prof_inchar_enter(void)
-{
- inchar_time = profile_start();
-}
-
-/// Called when finished waiting for the user to type a character.
-void prof_inchar_exit(void)
-{
- inchar_time = profile_end(inchar_time);
- profile_set_wait(profile_add(profile_get_wait(), inchar_time));
-}
-
-/// Dump the profiling results for all scripts in file "fd".
-static void script_dump_profile(FILE *fd)
-{
- scriptitem_T *si;
- FILE *sfd;
- sn_prl_T *pp;
-
- for (int id = 1; id <= script_items.ga_len; id++) {
- si = &SCRIPT_ITEM(id);
- if (si->sn_prof_on) {
- fprintf(fd, "SCRIPT %s\n", si->sn_name);
- if (si->sn_pr_count == 1) {
- fprintf(fd, "Sourced 1 time\n");
- } else {
- fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
- }
- fprintf(fd, "Total time: %s\n", profile_msg(si->sn_pr_total));
- fprintf(fd, " Self time: %s\n", profile_msg(si->sn_pr_self));
- fprintf(fd, "\n");
- fprintf(fd, "count total (s) self (s)\n");
-
- sfd = os_fopen((char *)si->sn_name, "r");
- if (sfd == NULL) {
- fprintf(fd, "Cannot open file!\n");
- } else {
- // Keep going till the end of file, so that trailing
- // continuation lines are listed.
- for (int i = 0;; i++) {
- if (vim_fgets(IObuff, IOSIZE, sfd)) {
- break;
- }
- // When a line has been truncated, append NL, taking care
- // of multi-byte characters .
- if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) {
- int n = IOSIZE - 2;
-
- // Move to the first byte of this char.
- // utf_head_off() doesn't work, because it checks
- // for a truncated character.
- while (n > 0 && (IObuff[n] & 0xc0) == 0x80) {
- n--;
- }
-
- IObuff[n] = NL;
- IObuff[n + 1] = NUL;
- }
- if (i < si->sn_prl_ga.ga_len
- && (pp = &PRL_ITEM(si, i))->snp_count > 0) {
- fprintf(fd, "%5d ", pp->snp_count);
- if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) {
- fprintf(fd, " ");
- } else {
- fprintf(fd, "%s ", profile_msg(pp->sn_prl_total));
- }
- fprintf(fd, "%s ", profile_msg(pp->sn_prl_self));
- } else {
- fprintf(fd, " ");
- }
- fprintf(fd, "%s", IObuff);
- }
- fclose(sfd);
- }
- fprintf(fd, "\n");
- }
- }
-}
-
-/// @return true when a function defined in the current script should be
-/// profiled.
-bool prof_def_func(void)
- FUNC_ATTR_PURE
-{
- if (current_sctx.sc_sid > 0) {
- return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
- }
- return false;
-}
-
/// If 'autowrite' option set, try to write the file.
/// Careful: autocommands may make "buf" invalid!
///
@@ -720,7 +364,7 @@ bool check_changed_any(bool hidden, bool unload)
exiting = false;
// When ":confirm" used, don't give an error message.
if (!(p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM))) {
- // There must be a wait_return for this message, do_buffer()
+ // There must be a wait_return() for this message, do_buffer()
// may cause a redraw. But wait_return() is a no-op when vgetc()
// is busy (Quit used from window menu), then make sure we don't
// cause a scroll up.
@@ -796,483 +440,6 @@ int buf_write_all(buf_T *buf, int forceit)
return retval;
}
-/// Code to handle the argument list.
-
-#define AL_SET 1
-#define AL_ADD 2
-#define AL_DEL 3
-
-/// Isolate one argument, taking backticks.
-/// Changes the argument in-place, puts a NUL after it. Backticks remain.
-///
-/// @return a pointer to the start of the next argument.
-static char *do_one_arg(char *str)
-{
- char *p;
- bool inbacktick;
-
- inbacktick = false;
- for (p = str; *str; str++) {
- // When the backslash is used for escaping the special meaning of a
- // character we need to keep it until wildcard expansion.
- if (rem_backslash((char_u *)str)) {
- *p++ = *str++;
- *p++ = *str;
- } else {
- // An item ends at a space not in backticks
- if (!inbacktick && ascii_isspace(*str)) {
- break;
- }
- if (*str == '`') {
- inbacktick ^= true;
- }
- *p++ = *str;
- }
- }
- str = skipwhite(str);
- *p = NUL;
-
- return str;
-}
-
-/// Separate the arguments in "str" and return a list of pointers in the
-/// growarray "gap".
-static void get_arglist(garray_T *gap, char *str, int escaped)
-{
- ga_init(gap, (int)sizeof(char_u *), 20);
- while (*str != NUL) {
- GA_APPEND(char *, gap, str);
-
- // If str is escaped, don't handle backslashes or spaces
- if (!escaped) {
- return;
- }
-
- // Isolate one argument, change it in-place, put a NUL after it.
- str = do_one_arg(str);
- }
-}
-
-/// Parse a list of arguments (file names), expand them and return in
-/// "fnames[fcountp]". When "wig" is true, removes files matching 'wildignore'.
-///
-/// @return FAIL or OK.
-int get_arglist_exp(char_u *str, int *fcountp, char ***fnamesp, bool wig)
-{
- garray_T ga;
- int i;
-
- get_arglist(&ga, (char *)str, true);
-
- if (wig) {
- i = expand_wildcards(ga.ga_len, ga.ga_data,
- fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
- } else {
- i = gen_expand_wildcards(ga.ga_len, ga.ga_data,
- fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
- }
-
- ga_clear(&ga);
- return i;
-}
-
-/// @param str
-/// @param what
-/// AL_SET: Redefine the argument list to 'str'.
-/// AL_ADD: add files in 'str' to the argument list after "after".
-/// AL_DEL: remove files in 'str' from the argument list.
-/// @param after
-/// 0 means before first one
-/// @param will_edit will edit added argument
-///
-/// @return FAIL for failure, OK otherwise.
-static int do_arglist(char *str, int what, int after, bool will_edit)
- FUNC_ATTR_NONNULL_ALL
-{
- garray_T new_ga;
- int exp_count;
- char **exp_files;
- char *p;
- int match;
- int arg_escaped = true;
-
- // Set default argument for ":argadd" command.
- if (what == AL_ADD && *str == NUL) {
- if (curbuf->b_ffname == NULL) {
- return FAIL;
- }
- str = curbuf->b_fname;
- arg_escaped = false;
- }
-
- // Collect all file name arguments in "new_ga".
- get_arglist(&new_ga, str, arg_escaped);
-
- if (what == AL_DEL) {
- regmatch_T regmatch;
- bool didone;
-
- // Delete the items: use each item as a regexp and find a match in the
- // argument list.
- regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
- for (int i = 0; i < new_ga.ga_len && !got_int; i++) {
- p = ((char **)new_ga.ga_data)[i];
- p = file_pat_to_reg_pat(p, NULL, NULL, false);
- if (p == NULL) {
- break;
- }
- regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0);
- if (regmatch.regprog == NULL) {
- xfree(p);
- break;
- }
-
- didone = false;
- for (match = 0; match < ARGCOUNT; match++) {
- if (vim_regexec(&regmatch, alist_name(&ARGLIST[match]), (colnr_T)0)) {
- didone = true;
- xfree(ARGLIST[match].ae_fname);
- memmove(ARGLIST + match, ARGLIST + match + 1,
- (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
- ALIST(curwin)->al_ga.ga_len--;
- if (curwin->w_arg_idx > match) {
- curwin->w_arg_idx--;
- }
- match--;
- }
- }
-
- vim_regfree(regmatch.regprog);
- xfree(p);
- if (!didone) {
- semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]);
- }
- }
- ga_clear(&new_ga);
- } else {
- int i = expand_wildcards(new_ga.ga_len, new_ga.ga_data,
- &exp_count, &exp_files,
- EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
- ga_clear(&new_ga);
- if (i == FAIL || exp_count == 0) {
- emsg(_(e_nomatch));
- return FAIL;
- }
-
- if (what == AL_ADD) {
- alist_add_list(exp_count, exp_files, after, will_edit);
- xfree(exp_files);
- } else {
- assert(what == AL_SET);
- alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
- }
- }
-
- alist_check_arg_idx();
-
- return OK;
-}
-
-/// Check the validity of the arg_idx for each other window.
-static void alist_check_arg_idx(void)
-{
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_alist == curwin->w_alist) {
- check_arg_idx(win);
- }
- }
-}
-
-/// @return true if window "win" is editing the file at the current argument
-/// index.
-static bool editing_arg_idx(win_T *win)
-{
- return !(win->w_arg_idx >= WARGCOUNT(win)
- || (win->w_buffer->b_fnum
- != WARGLIST(win)[win->w_arg_idx].ae_fnum
- && (win->w_buffer->b_ffname == NULL
- || !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]),
- win->w_buffer->b_ffname, true,
- true) & kEqualFiles))));
-}
-
-/// Check if window "win" is editing the w_arg_idx file in its argument list.
-void check_arg_idx(win_T *win)
-{
- if (WARGCOUNT(win) > 1 && !editing_arg_idx(win)) {
- // We are not editing the current entry in the argument list.
- // Set "arg_had_last" if we are editing the last one.
- win->w_arg_idx_invalid = true;
- if (win->w_arg_idx != WARGCOUNT(win) - 1
- && arg_had_last == false
- && ALIST(win) == &global_alist
- && GARGCOUNT > 0
- && win->w_arg_idx < GARGCOUNT
- && (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
- || (win->w_buffer->b_ffname != NULL
- && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]),
- win->w_buffer->b_ffname, true, true)
- & kEqualFiles)))) {
- arg_had_last = true;
- }
- } else {
- // We are editing the current entry in the argument list.
- // Set "arg_had_last" if it's also the last one
- win->w_arg_idx_invalid = false;
- if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) {
- arg_had_last = true;
- }
- }
-}
-
-/// ":args", ":argslocal" and ":argsglobal".
-void ex_args(exarg_T *eap)
-{
- if (eap->cmdidx != CMD_args) {
- alist_unlink(ALIST(curwin));
- if (eap->cmdidx == CMD_argglobal) {
- ALIST(curwin) = &global_alist;
- } else { // eap->cmdidx == CMD_arglocal
- alist_new();
- }
- }
-
- if (*eap->arg != NUL) {
- // ":args file ..": define new argument list, handle like ":next"
- // Also for ":argslocal file .." and ":argsglobal file ..".
- ex_next(eap);
- } else if (eap->cmdidx == CMD_args) {
- // ":args": list arguments.
- if (ARGCOUNT > 0) {
- char **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT);
- // Overwrite the command, for a short list there is no scrolling
- // required and no wait_return().
- gotocmdline(true);
- for (int i = 0; i < ARGCOUNT; i++) {
- items[i] = alist_name(&ARGLIST[i]);
- }
- list_in_columns((char_u **)items, ARGCOUNT, curwin->w_arg_idx);
- xfree(items);
- }
- } else if (eap->cmdidx == CMD_arglocal) {
- garray_T *gap = &curwin->w_alist->al_ga;
-
- // ":argslocal": make a local copy of the global argument list.
- ga_grow(gap, GARGCOUNT);
- for (int i = 0; i < GARGCOUNT; i++) {
- if (GARGLIST[i].ae_fname != NULL) {
- AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
- vim_strsave(GARGLIST[i].ae_fname);
- AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
- GARGLIST[i].ae_fnum;
- gap->ga_len++;
- }
- }
- }
-}
-
-/// ":previous", ":sprevious", ":Next" and ":sNext".
-void ex_previous(exarg_T *eap)
-{
- // If past the last one already, go to the last one.
- if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT) {
- do_argfile(eap, ARGCOUNT - 1);
- } else {
- do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
- }
-}
-
-/// ":rewind", ":first", ":sfirst" and ":srewind".
-void ex_rewind(exarg_T *eap)
-{
- do_argfile(eap, 0);
-}
-
-/// ":last" and ":slast".
-void ex_last(exarg_T *eap)
-{
- do_argfile(eap, ARGCOUNT - 1);
-}
-
-/// ":argument" and ":sargument".
-void ex_argument(exarg_T *eap)
-{
- int i;
-
- if (eap->addr_count > 0) {
- i = (int)eap->line2 - 1;
- } else {
- i = curwin->w_arg_idx;
- }
- do_argfile(eap, i);
-}
-
-/// Edit file "argn" of the argument lists.
-void do_argfile(exarg_T *eap, int argn)
-{
- int other;
- char *p;
- int old_arg_idx = curwin->w_arg_idx;
-
- if (argn < 0 || argn >= ARGCOUNT) {
- if (ARGCOUNT <= 1) {
- emsg(_("E163: There is only one file to edit"));
- } else if (argn < 0) {
- emsg(_("E164: Cannot go before first file"));
- } else {
- emsg(_("E165: Cannot go beyond last file"));
- }
- } else {
- setpcmark();
-
- // split window or create new tab page first
- if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) {
- if (win_split(0, 0) == FAIL) {
- return;
- }
- RESET_BINDING(curwin);
- } else {
- // if 'hidden' set, only check for changed file when re-editing
- // the same buffer
- other = true;
- if (buf_hide(curbuf)) {
- p = fix_fname(alist_name(&ARGLIST[argn]));
- other = otherfile(p);
- xfree(p);
- }
- if ((!buf_hide(curbuf) || !other)
- && check_changed(curbuf, CCGD_AW
- | (other ? 0 : CCGD_MULTWIN)
- | (eap->forceit ? CCGD_FORCEIT : 0)
- | CCGD_EXCMD)) {
- return;
- }
- }
-
- curwin->w_arg_idx = argn;
- if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist) {
- arg_had_last = true;
- }
-
- // Edit the file; always use the last known line number.
- // When it fails (e.g. Abort for already edited file) restore the
- // argument index.
- if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
- eap, ECMD_LAST,
- (buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
- + (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL) {
- curwin->w_arg_idx = old_arg_idx;
- } else if (eap->cmdidx != CMD_argdo) {
- // like Vi: set the mark where the cursor is in the file.
- setmark('\'');
- }
- }
-}
-
-/// ":next", and commands that behave like it.
-void ex_next(exarg_T *eap)
-{
- int i;
-
- // check for changed buffer now, if this fails the argument list is not
- // redefined.
- if (buf_hide(curbuf)
- || eap->cmdidx == CMD_snext
- || !check_changed(curbuf, CCGD_AW
- | (eap->forceit ? CCGD_FORCEIT : 0)
- | CCGD_EXCMD)) {
- if (*eap->arg != NUL) { // redefine file list
- if (do_arglist(eap->arg, AL_SET, 0, true) == FAIL) {
- return;
- }
- i = 0;
- } else {
- i = curwin->w_arg_idx + (int)eap->line2;
- }
- do_argfile(eap, i);
- }
-}
-
-/// ":argedit"
-void ex_argedit(exarg_T *eap)
-{
- int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
- // Whether curbuf will be reused, curbuf->b_ffname will be set.
- bool curbuf_is_reusable = curbuf_reusable();
-
- if (do_arglist(eap->arg, AL_ADD, i, true) == FAIL) {
- return;
- }
- maketitle();
-
- if (curwin->w_arg_idx == 0
- && (curbuf->b_ml.ml_flags & ML_EMPTY)
- && (curbuf->b_ffname == NULL || curbuf_is_reusable)) {
- i = 0;
- }
- // Edit the argument.
- if (i < ARGCOUNT) {
- do_argfile(eap, i);
- }
-}
-
-/// ":argadd"
-void ex_argadd(exarg_T *eap)
-{
- do_arglist(eap->arg, AL_ADD,
- eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
- false);
- maketitle();
-}
-
-/// ":argdelete"
-void ex_argdelete(exarg_T *eap)
-{
- if (eap->addr_count > 0 || *eap->arg == NUL) {
- // ":argdel" works like ":.argdel"
- if (eap->addr_count == 0) {
- if (curwin->w_arg_idx >= ARGCOUNT) {
- emsg(_("E610: No argument to delete"));
- return;
- }
- eap->line1 = eap->line2 = curwin->w_arg_idx + 1;
- } else if (eap->line2 > ARGCOUNT) {
- // ":1,4argdel": Delete all arguments in the range.
- eap->line2 = ARGCOUNT;
- }
- linenr_T n = eap->line2 - eap->line1 + 1;
- if (*eap->arg != NUL) {
- // Can't have both a range and an argument.
- emsg(_(e_invarg));
- } else if (n <= 0) {
- // Don't give an error for ":%argdel" if the list is empty.
- if (eap->line1 != 1 || eap->line2 != 0) {
- emsg(_(e_invrange));
- }
- } else {
- for (linenr_T i = eap->line1; i <= eap->line2; i++) {
- xfree(ARGLIST[i - 1].ae_fname);
- }
- memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
- (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
- ALIST(curwin)->al_ga.ga_len -= (int)n;
- if (curwin->w_arg_idx >= eap->line2) {
- curwin->w_arg_idx -= (int)n;
- } else if (curwin->w_arg_idx > eap->line1) {
- curwin->w_arg_idx = (int)eap->line1;
- }
- if (ARGCOUNT == 0) {
- curwin->w_arg_idx = 0;
- } else if (curwin->w_arg_idx >= ARGCOUNT) {
- curwin->w_arg_idx = ARGCOUNT - 1;
- }
- }
- } else {
- do_arglist(eap->arg, AL_DEL, 0, false);
- }
- maketitle();
-}
-
/// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
void ex_listdo(exarg_T *eap)
{
@@ -1372,10 +539,10 @@ void ex_listdo(exarg_T *eap)
if (curwin->w_arg_idx != i || !editing_arg_idx(curwin)) {
// Clear 'shm' to avoid that the file message overwrites
// any output from the command.
- p_shm_save = (char *)vim_strsave(p_shm);
- set_option_value("shm", 0L, "", 0);
+ p_shm_save = xstrdup(p_shm);
+ set_option_value_give_err("shm", 0L, "", 0);
do_argfile(eap, i);
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
}
if (curwin->w_arg_idx != i) {
@@ -1441,10 +608,10 @@ void ex_listdo(exarg_T *eap)
// Go to the next buffer. Clear 'shm' to avoid that the file
// message overwrites any output from the command.
- p_shm_save = (char *)vim_strsave(p_shm);
- set_option_value("shm", 0L, "", 0);
+ p_shm_save = xstrdup(p_shm);
+ set_option_value_give_err("shm", 0L, "", 0);
goto_buffer(eap, DOBUF_FIRST, FORWARD, next_fnum);
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
// If autocommands took us elsewhere, quit here.
@@ -1464,10 +631,10 @@ void ex_listdo(exarg_T *eap)
// Clear 'shm' to avoid that the file message overwrites
// any output from the command.
- p_shm_save = (char *)vim_strsave(p_shm);
- set_option_value("shm", 0L, "", 0);
+ p_shm_save = xstrdup(p_shm);
+ set_option_value_give_err("shm", 0L, "", 0);
ex_cnext(eap);
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
// If jumping to the next quickfix entry fails, quit here.
@@ -1509,11 +676,11 @@ void ex_listdo(exarg_T *eap)
// buffer was opened while Syntax autocommands were disabled,
// need to trigger them now.
if (buf == curbuf) {
- apply_autocmds(EVENT_SYNTAX, (char *)curbuf->b_p_syn, curbuf->b_fname, true,
+ apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname, true,
curbuf);
} else {
aucmd_prepbuf(&aco, buf);
- apply_autocmds(EVENT_SYNTAX, (char *)buf->b_p_syn, buf->b_fname, true, buf);
+ apply_autocmds(EVENT_SYNTAX, buf->b_p_syn, buf->b_fname, true, buf);
aucmd_restbuf(&aco);
}
@@ -1524,51 +691,6 @@ void ex_listdo(exarg_T *eap)
}
}
-/// Add files[count] to the arglist of the current window after arg "after".
-/// The file names in files[count] must have been allocated and are taken over.
-/// Files[] itself is not taken over.
-///
-/// @param after: where to add: 0 = before first one
-/// @param will_edit will edit adding argument
-static void alist_add_list(int count, char **files, int after, bool will_edit)
- FUNC_ATTR_NONNULL_ALL
-{
- int old_argcount = ARGCOUNT;
- ga_grow(&ALIST(curwin)->al_ga, count);
- {
- if (after < 0) {
- after = 0;
- }
- if (after > ARGCOUNT) {
- after = ARGCOUNT;
- }
- if (after < ARGCOUNT) {
- memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
- (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
- }
- for (int i = 0; i < count; i++) {
- const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
- ARGLIST[after + i].ae_fname = (char_u *)files[i];
- ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
- }
- ALIST(curwin)->al_ga.ga_len += count;
- if (old_argcount > 0 && curwin->w_arg_idx >= after) {
- curwin->w_arg_idx += count;
- }
- return;
- }
-}
-
-// Function given to ExpandGeneric() to obtain the possible arguments of the
-// argedit and argdelete commands.
-char *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
-{
- if (idx >= ARGCOUNT) {
- return NULL;
- }
- return alist_name(&ARGLIST[idx]);
-}
-
/// ":compiler[!] {name}"
void ex_compiler(exarg_T *eap)
{
@@ -1632,987 +754,6 @@ void ex_compiler(exarg_T *eap)
}
}
-/// ":options"
-void ex_options(exarg_T *eap)
-{
- char buf[500];
- bool multi_mods = 0;
-
- buf[0] = NUL;
- (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
-
- os_setenv("OPTWIN_CMD", buf, 1);
- cmd_source(SYS_OPTWIN_FILE, NULL);
-}
-
-/// ":source [{fname}]"
-void ex_source(exarg_T *eap)
-{
- cmd_source(eap->arg, eap);
-}
-
-static void cmd_source(char *fname, exarg_T *eap)
-{
- if (eap != NULL && *fname == NUL) {
- cmd_source_buffer(eap);
- } else if (eap != NULL && eap->forceit) {
- // ":source!": read Normal mode commands
- // Need to execute the commands directly. This is required at least
- // for:
- // - ":g" command busy
- // - after ":argdo", ":windo" or ":bufdo"
- // - another command follows
- // - inside a loop
- openscript((char_u *)fname, global_busy || listcmd_busy || eap->nextcmd != NULL
- || eap->cstack->cs_idx >= 0);
-
- // ":source" read ex commands
- } else if (do_source(fname, false, DOSO_NONE) == FAIL) {
- semsg(_(e_notopen), fname);
- }
-}
-
-/// Concatenate VimL line if it starts with a line continuation into a growarray
-/// (excluding the continuation chars and leading whitespace)
-///
-/// @note Growsize of the growarray may be changed to speed up concatenations!
-///
-/// @param ga the growarray to append to
-/// @param init_growsize the starting growsize value of the growarray
-/// @param p pointer to the beginning of the line to consider
-/// @param len the length of this line
-///
-/// @return true if this line did begin with a continuation (the next line
-/// should also be considered, if it exists); false otherwise
-static bool concat_continued_line(garray_T *const ga, const int init_growsize,
- const char_u *const p, size_t len)
- FUNC_ATTR_NONNULL_ALL
-{
- const char *const line = (char *)skipwhite_len(p, len);
- len -= (size_t)((char_u *)line - p);
- // Skip lines starting with '\" ', concat lines starting with '\'
- if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) {
- return true;
- } else if (len == 0 || line[0] != '\\') {
- return false;
- }
- if (ga->ga_len > init_growsize) {
- ga_set_growsize(ga, MIN(ga->ga_len, 8000));
- }
- ga_concat_len(ga, line + 1, len - 1);
- return true;
-}
-
-typedef struct {
- linenr_T curr_lnum;
- const linenr_T final_lnum;
-} GetBufferLineCookie;
-
-/// ":source" and associated commands.
-///
-/// @return address holding the next breakpoint line for a source cookie
-linenr_T *source_breakpoint(void *cookie)
-{
- return &((struct source_cookie *)cookie)->breakpoint;
-}
-
-/// @return the address holding the debug tick for a source cookie.
-int *source_dbg_tick(void *cookie)
-{
- return &((struct source_cookie *)cookie)->dbg_tick;
-}
-
-/// @return the nesting level for a source cookie.
-int source_level(void *cookie)
- FUNC_ATTR_PURE
-{
- return ((struct source_cookie *)cookie)->level;
-}
-
-/// Special function to open a file without handle inheritance.
-/// If possible the handle is closed on exec().
-static FILE *fopen_noinh_readbin(char *filename)
-{
-#ifdef WIN32
- int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
-#else
- int fd_tmp = os_open(filename, O_RDONLY, 0);
-#endif
-
- if (fd_tmp < 0) {
- return NULL;
- }
-
- (void)os_set_cloexec(fd_tmp);
-
- return fdopen(fd_tmp, READBIN);
-}
-
-typedef struct {
- char *buf;
- size_t offset;
-} GetStrLineCookie;
-
-/// Get one full line from a sourced string (in-memory, no file).
-/// Called by do_cmdline() when it's called from do_source_str().
-///
-/// @return pointer to allocated line, or NULL for end-of-file or
-/// some error.
-static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
-{
- GetStrLineCookie *p = cookie;
- if (STRLEN(p->buf) <= p->offset) {
- return NULL;
- }
- const char *line = p->buf + p->offset;
- const char *eol = (char *)skip_to_newline((char_u *)line);
- garray_T ga;
- ga_init(&ga, sizeof(char_u), 400);
- ga_concat_len(&ga, line, (size_t)(eol - line));
- if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
- while (eol[0] != NUL) {
- line = eol + 1;
- const char_u *const next_eol = skip_to_newline((char_u *)line);
- if (!concat_continued_line(&ga, 400, (char_u *)line, (size_t)(next_eol - (char_u *)line))) {
- break;
- }
- eol = (char *)next_eol;
- }
- }
- ga_append(&ga, NUL);
- p->offset = (size_t)(eol - p->buf) + 1;
- return ga.ga_data;
-}
-
-/// Create a new script item and allocate script-local vars. @see new_script_vars
-///
-/// @param name File name of the script. NULL for anonymous :source.
-/// @param[out] sid_out SID of the new item.
-///
-/// @return pointer to the created script item.
-scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
-{
- static scid_T last_current_SID = 0;
- const scid_T sid = ++last_current_SID;
- if (sid_out != NULL) {
- *sid_out = sid;
- }
- ga_grow(&script_items, sid - script_items.ga_len);
- while (script_items.ga_len < sid) {
- script_items.ga_len++;
- SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
- SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
- }
- SCRIPT_ITEM(sid).sn_name = (char_u *)name;
- new_script_vars(sid); // Allocate the local script variables to use for this script.
- return &SCRIPT_ITEM(sid);
-}
-
-static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
-{
- char *save_sourcing_name = sourcing_name;
- linenr_T save_sourcing_lnum = sourcing_lnum;
- char sourcing_name_buf[256];
- if (save_sourcing_name == NULL) {
- sourcing_name = (char *)traceback_name;
- } else {
- snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf),
- "%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name,
- save_sourcing_lnum);
- sourcing_name = sourcing_name_buf; // -V507 reassigned below, before return.
- }
- sourcing_lnum = 0;
-
- const sctx_T save_current_sctx = current_sctx;
- if (current_sctx.sc_sid != SID_LUA) {
- current_sctx.sc_sid = SID_STR;
- }
- current_sctx.sc_seq = 0;
- current_sctx.sc_lnum = save_sourcing_lnum;
- funccal_entry_T entry;
- save_funccal(&entry);
- int retval = do_cmdline(NULL, fgetline, cookie,
- DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
- sourcing_lnum = save_sourcing_lnum;
- sourcing_name = save_sourcing_name;
- current_sctx = save_current_sctx;
- restore_funccal();
- return retval;
-}
-
-static void cmd_source_buffer(const exarg_T *const eap)
- FUNC_ATTR_NONNULL_ALL
-{
- if (curbuf == NULL) {
- return;
- }
- garray_T ga;
- ga_init(&ga, sizeof(char_u), 400);
- const linenr_T final_lnum = eap->line2;
- // Copy the contents to be executed.
- for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
- // Adjust growsize to current length to speed up concatenating many lines.
- if (ga.ga_len > 400) {
- ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
- }
- ga_concat(&ga, (char *)ml_get(curr_lnum));
- ga_append(&ga, NL);
- }
- ((char_u *)ga.ga_data)[ga.ga_len - 1] = NUL;
- const GetStrLineCookie cookie = {
- .buf = ga.ga_data,
- .offset = 0,
- };
- if (curbuf->b_fname
- && path_with_extension((const char *)curbuf->b_fname, "lua")) {
- nlua_source_using_linegetter(get_str_line, (void *)&cookie,
- ":source (no file)");
- } else {
- source_using_linegetter((void *)&cookie, get_str_line,
- ":source (no file)");
- }
- ga_clear(&ga);
-}
-
-/// Executes lines in `src` as Ex commands.
-///
-/// @see do_source()
-int do_source_str(const char *cmd, const char *traceback_name)
-{
- GetStrLineCookie cookie = {
- .buf = (char *)cmd,
- .offset = 0,
- };
- return source_using_linegetter((void *)&cookie, get_str_line, traceback_name);
-}
-
-/// When fname is a 'lua' file nlua_exec_file() is invoked to source it.
-/// Otherwise reads the file `fname` and executes its lines as Ex commands.
-///
-/// This function may be called recursively!
-///
-/// @see do_source_str
-///
-/// @param fname
-/// @param check_other check for .vimrc and _vimrc
-/// @param is_vimrc DOSO_ value
-///
-/// @return FAIL if file could not be opened, OK otherwise
-int do_source(char *fname, int check_other, int is_vimrc)
-{
- struct source_cookie cookie;
- char *save_sourcing_name;
- linenr_T save_sourcing_lnum;
- char *p;
- char *fname_exp;
- uint8_t *firstline = NULL;
- int retval = FAIL;
- int save_debug_break_level = debug_break_level;
- scriptitem_T *si = NULL;
- proftime_T wait_start;
- bool trigger_source_post = false;
-
- p = expand_env_save(fname);
- if (p == NULL) {
- return retval;
- }
- fname_exp = fix_fname(p);
- xfree(p);
- if (fname_exp == NULL) {
- return retval;
- }
- if (os_isdir((char_u *)fname_exp)) {
- smsg(_("Cannot source a directory: \"%s\""), fname);
- goto theend;
- }
-
- // Apply SourceCmd autocommands, they should get the file and source it.
- if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
- && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
- false, curbuf)) {
- retval = aborting() ? FAIL : OK;
- if (retval == OK) {
- // Apply SourcePost autocommands.
- apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
- }
- goto theend;
- }
-
- // Apply SourcePre autocommands, they may get the file.
- apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf);
-
- cookie.fp = fopen_noinh_readbin(fname_exp);
- if (cookie.fp == NULL && check_other) {
- // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
- // and ".exrc" by "_exrc" or vice versa.
- p = path_tail(fname_exp);
- if ((*p == '.' || *p == '_')
- && (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) {
- *p = (*p == '_') ? '.' : '_';
- cookie.fp = fopen_noinh_readbin(fname_exp);
- }
- }
-
- if (cookie.fp == NULL) {
- if (p_verbose > 1) {
- verbose_enter();
- if (sourcing_name == NULL) {
- smsg(_("could not source \"%s\""), fname);
- } else {
- smsg(_("line %" PRId64 ": could not source \"%s\""),
- (int64_t)sourcing_lnum, fname);
- }
- verbose_leave();
- }
- goto theend;
- }
-
- // The file exists.
- // - In verbose mode, give a message.
- // - For a vimrc file, may want to call vimrc_found().
- if (p_verbose > 1) {
- verbose_enter();
- if (sourcing_name == NULL) {
- smsg(_("sourcing \"%s\""), fname);
- } else {
- smsg(_("line %" PRId64 ": sourcing \"%s\""),
- (int64_t)sourcing_lnum, fname);
- }
- verbose_leave();
- }
- if (is_vimrc == DOSO_VIMRC) {
- vimrc_found(fname_exp, "MYVIMRC");
- }
-
-#ifdef USE_CRNL
- // If no automatic file format: Set default to CR-NL.
- if (*p_ffs == NUL) {
- cookie.fileformat = EOL_DOS;
- } else {
- cookie.fileformat = EOL_UNKNOWN;
- }
- cookie.error = false;
-#endif
-
- cookie.nextline = NULL;
- cookie.sourcing_lnum = 0;
- cookie.finished = false;
-
- // Check if this script has a breakpoint.
- cookie.breakpoint = dbg_find_breakpoint(true, (char_u *)fname_exp, (linenr_T)0);
- cookie.fname = fname_exp;
- cookie.dbg_tick = debug_tick;
-
- cookie.level = ex_nesting_level;
-
- // Keep the sourcing name/lnum, for recursive calls.
- save_sourcing_name = sourcing_name;
- sourcing_name = fname_exp;
- save_sourcing_lnum = sourcing_lnum;
- sourcing_lnum = 0;
-
- // start measuring script load time if --startuptime was passed and
- // time_fd was successfully opened afterwards.
- proftime_T rel_time;
- proftime_T start_time;
- FILE * const l_time_fd = time_fd;
- if (l_time_fd != NULL) {
- time_push(&rel_time, &start_time);
- }
-
- const int l_do_profiling = do_profiling;
- if (l_do_profiling == PROF_YES) {
- prof_child_enter(&wait_start); // entering a child now
- }
-
- // Don't use local function variables, if called from a function.
- // Also starts profiling timer for nested script.
- funccal_entry_T funccalp_entry;
- save_funccal(&funccalp_entry);
-
- const sctx_T save_current_sctx = current_sctx;
- si = get_current_script_id((char_u *)fname_exp, &current_sctx);
-
- if (l_do_profiling == PROF_YES) {
- bool forceit = false;
-
- // Check if we do profiling for this script.
- if (!si->sn_prof_on && has_profiling(true, si->sn_name, &forceit)) {
- profile_init(si);
- si->sn_pr_force = forceit;
- }
- if (si->sn_prof_on) {
- si->sn_pr_count++;
- si->sn_pr_start = profile_start();
- si->sn_pr_children = profile_zero();
- }
- }
-
- cookie.conv.vc_type = CONV_NONE; // no conversion
-
- // Read the first line so we can check for a UTF-8 BOM.
- firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true);
- if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
- && firstline[1] == 0xbb && firstline[2] == 0xbf) {
- // Found BOM; setup conversion, skip over BOM and recode the line.
- convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc);
- p = (char *)string_convert(&cookie.conv, (char_u *)firstline + 3, NULL);
- if (p == NULL) {
- p = xstrdup((char *)firstline + 3);
- }
- xfree(firstline);
- firstline = (uint8_t *)p;
- }
-
- if (path_with_extension((const char *)fname_exp, "lua")) {
- const sctx_T current_sctx_backup = current_sctx;
- const linenr_T sourcing_lnum_backup = sourcing_lnum;
- current_sctx.sc_sid = SID_LUA;
- current_sctx.sc_lnum = 0;
- sourcing_lnum = 0;
- // Source the file as lua
- nlua_exec_file((const char *)fname_exp);
- current_sctx = current_sctx_backup;
- sourcing_lnum = sourcing_lnum_backup;
- } else {
- // Call do_cmdline, which will call getsourceline() to get the lines.
- do_cmdline((char *)firstline, getsourceline, (void *)&cookie,
- DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
- }
- retval = OK;
-
- if (l_do_profiling == PROF_YES) {
- // Get "si" again, "script_items" may have been reallocated.
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on) {
- si->sn_pr_start = profile_end(si->sn_pr_start);
- si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start);
- si->sn_pr_total = profile_add(si->sn_pr_total, si->sn_pr_start);
- si->sn_pr_self = profile_self(si->sn_pr_self, si->sn_pr_start,
- si->sn_pr_children);
- }
- }
-
- if (got_int) {
- emsg(_(e_interr));
- }
- sourcing_name = save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
- if (p_verbose > 1) {
- verbose_enter();
- smsg(_("finished sourcing %s"), fname);
- if (sourcing_name != NULL) {
- smsg(_("continuing in %s"), sourcing_name);
- }
- verbose_leave();
- }
-
- if (l_time_fd != NULL) {
- vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
- time_msg((char *)IObuff, &start_time);
- time_pop(rel_time);
- }
-
- if (!got_int) {
- trigger_source_post = true;
- }
-
- // After a "finish" in debug mode, need to break at first command of next
- // sourced file.
- if (save_debug_break_level > ex_nesting_level
- && debug_break_level == ex_nesting_level) {
- debug_break_level++;
- }
-
- current_sctx = save_current_sctx;
- restore_funccal();
- if (l_do_profiling == PROF_YES) {
- prof_child_exit(&wait_start); // leaving a child now
- }
- fclose(cookie.fp);
- xfree(cookie.nextline);
- xfree(firstline);
- convert_setup(&cookie.conv, NULL, NULL);
-
- if (trigger_source_post) {
- apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
- }
-
-theend:
- xfree(fname_exp);
- return retval;
-}
-
-/// Check if fname was sourced before to finds its SID.
-/// If it's new, generate a new SID.
-///
-/// @param[in] fname file path of script
-/// @param[out] ret_sctx sctx of this script
-scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx)
-{
- static int last_current_SID_seq = 0;
-
- sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq,
- .sc_lnum = 0,
- .sc_sid = 0 };
- scriptitem_T *si = NULL;
-
- assert(script_items.ga_len >= 0);
- for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) {
- // We used to check inode here, but that doesn't work:
- // - If a script is edited and written, it may get a different
- // inode number, even though to the user it is the same script.
- // - If a script is deleted and another script is written, with a
- // different name, the inode may be re-used.
- si = &SCRIPT_ITEM(script_sctx.sc_sid);
- if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) {
- // Found it!
- break;
- }
- }
- if (script_sctx.sc_sid == 0) {
- si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid);
- }
- if (ret_sctx != NULL) {
- *ret_sctx = script_sctx;
- }
-
- return si;
-}
-
-/// ":scriptnames"
-void ex_scriptnames(exarg_T *eap)
-{
- if (eap->addr_count > 0) {
- // :script {scriptId}: edit the script
- if (eap->line2 < 1 || eap->line2 > script_items.ga_len) {
- emsg(_(e_invarg));
- } else {
- eap->arg = (char *)SCRIPT_ITEM(eap->line2).sn_name;
- do_exedit(eap, NULL);
- }
- return;
- }
-
- for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
- if (SCRIPT_ITEM(i).sn_name != NULL) {
- home_replace(NULL, (char *)SCRIPT_ITEM(i).sn_name, (char *)NameBuff, MAXPATHL, true);
- vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
- if (!message_filtered(IObuff)) {
- msg_putchar('\n');
- msg_outtrans((char *)IObuff);
- line_breakcheck();
- }
- }
- }
-}
-
-#if defined(BACKSLASH_IN_FILENAME)
-/// Fix slashes in the list of script names for 'shellslash'.
-void scriptnames_slash_adjust(void)
-{
- for (int i = 1; i <= script_items.ga_len; i++) {
- if (SCRIPT_ITEM(i).sn_name != NULL) {
- slash_adjust(SCRIPT_ITEM(i).sn_name);
- }
- }
-}
-
-#endif
-
-/// Get a pointer to a script name. Used for ":verbose set".
-/// Message appended to "Last set from "
-char_u *get_scriptname(LastSet last_set, bool *should_free)
-{
- *should_free = false;
-
- switch (last_set.script_ctx.sc_sid) {
- case SID_MODELINE:
- return (char_u *)_("modeline");
- case SID_CMDARG:
- return (char_u *)_("--cmd argument");
- case SID_CARG:
- return (char_u *)_("-c argument");
- case SID_ENV:
- return (char_u *)_("environment variable");
- case SID_ERROR:
- return (char_u *)_("error handler");
- case SID_WINLAYOUT:
- return (char_u *)_("changed window size");
- case SID_LUA:
- return (char_u *)_("Lua");
- case SID_API_CLIENT:
- snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
- return IObuff;
- case SID_STR:
- return (char_u *)_("anonymous :source");
- default: {
- char *const sname = (char *)SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
- if (sname == NULL) {
- snprintf((char *)IObuff, IOSIZE, _("anonymous :source (script id %d)"),
- last_set.script_ctx.sc_sid);
- return IObuff;
- }
-
- *should_free = true;
- return (char_u *)home_replace_save(NULL, sname);
- }
- }
-}
-
-#if defined(EXITFREE)
-void free_scriptnames(void)
-{
- profile_reset();
-
-# define FREE_SCRIPTNAME(item) xfree((item)->sn_name)
- GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME);
-}
-#endif
-
-linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie)
- FUNC_ATTR_PURE
-{
- return fgetline == getsourceline
- ? ((struct source_cookie *)cookie)->sourcing_lnum
- : sourcing_lnum;
-}
-
-/// Get one full line from a sourced file.
-/// Called by do_cmdline() when it's called from do_source().
-///
-/// @return pointer to the line in allocated memory, or NULL for end-of-file or
-/// some error.
-char *getsourceline(int c, void *cookie, int indent, bool do_concat)
-{
- struct source_cookie *sp = (struct source_cookie *)cookie;
- char *line;
- char *p;
-
- // If breakpoints have been added/deleted need to check for it.
- if (sp->dbg_tick < debug_tick) {
- sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum);
- sp->dbg_tick = debug_tick;
- }
- if (do_profiling == PROF_YES) {
- script_line_end();
- }
- // Set the current sourcing line number.
- sourcing_lnum = sp->sourcing_lnum + 1;
- // Get current line. If there is a read-ahead line, use it, otherwise get
- // one now.
- if (sp->finished) {
- line = NULL;
- } else if (sp->nextline == NULL) {
- line = get_one_sourceline(sp);
- } else {
- line = sp->nextline;
- sp->nextline = NULL;
- sp->sourcing_lnum++;
- }
- if (line != NULL && do_profiling == PROF_YES) {
- script_line_start();
- }
-
- // Only concatenate lines starting with a \ when 'cpoptions' doesn't
- // contain the 'C' flag.
- if (line != NULL && do_concat && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) {
- // compensate for the one line read-ahead
- sp->sourcing_lnum--;
-
- // Get the next line and concatenate it when it starts with a
- // backslash. We always need to read the next line, keep it in
- // sp->nextline.
- // Also check for a comment in between continuation lines: "\ .
- sp->nextline = get_one_sourceline(sp);
- if (sp->nextline != NULL
- && (*(p = skipwhite(sp->nextline)) == '\\'
- || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) {
- garray_T ga;
-
- ga_init(&ga, (int)sizeof(char_u), 400);
- ga_concat(&ga, line);
- while (sp->nextline != NULL
- && concat_continued_line(&ga, 400, (char_u *)sp->nextline,
- STRLEN(sp->nextline))) {
- xfree(sp->nextline);
- sp->nextline = get_one_sourceline(sp);
- }
- ga_append(&ga, NUL);
- xfree(line);
- line = ga.ga_data;
- }
- }
-
- if (line != NULL && sp->conv.vc_type != CONV_NONE) {
- char *s;
-
- // Convert the encoding of the script line.
- s = (char *)string_convert(&sp->conv, (char_u *)line, NULL);
- if (s != NULL) {
- xfree(line);
- line = s;
- }
- }
-
- // Did we encounter a breakpoint?
- if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) {
- dbg_breakpoint((char_u *)sp->fname, sourcing_lnum);
- // Find next breakpoint.
- sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum);
- sp->dbg_tick = debug_tick;
- }
-
- return line;
-}
-
-static char *get_one_sourceline(struct source_cookie *sp)
-{
- garray_T ga;
- int len;
- int c;
- char *buf;
-#ifdef USE_CRNL
- int has_cr; // CR-LF found
-#endif
- bool have_read = false;
-
- // use a growarray to store the sourced line
- ga_init(&ga, 1, 250);
-
- // Loop until there is a finished line (or end-of-file).
- sp->sourcing_lnum++;
- for (;;) {
- // make room to read at least 120 (more) characters
- ga_grow(&ga, 120);
- buf = ga.ga_data;
-
-retry:
- errno = 0;
- if (fgets(buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
- sp->fp) == NULL) {
- if (errno == EINTR) {
- goto retry;
- }
-
- break;
- }
- len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
-#ifdef USE_CRNL
- // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
- // CTRL-Z by its own, or after a NL.
- if ((len == 1 || (len >= 2 && buf[len - 2] == '\n'))
- && sp->fileformat == EOL_DOS
- && buf[len - 1] == Ctrl_Z) {
- buf[len - 1] = NUL;
- break;
- }
-#endif
-
- have_read = true;
- ga.ga_len = len;
-
- // If the line was longer than the buffer, read more.
- if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') {
- continue;
- }
-
- if (len >= 1 && buf[len - 1] == '\n') { // remove trailing NL
-#ifdef USE_CRNL
- has_cr = (len >= 2 && buf[len - 2] == '\r');
- if (sp->fileformat == EOL_UNKNOWN) {
- if (has_cr) {
- sp->fileformat = EOL_DOS;
- } else {
- sp->fileformat = EOL_UNIX;
- }
- }
-
- if (sp->fileformat == EOL_DOS) {
- if (has_cr) { // replace trailing CR
- buf[len - 2] = '\n';
- len--;
- ga.ga_len--;
- } else { // lines like ":map xx yy^M" will have failed
- if (!sp->error) {
- msg_source(HL_ATTR(HLF_W));
- emsg(_("W15: Warning: Wrong line separator, ^M may be missing"));
- }
- sp->error = true;
- sp->fileformat = EOL_UNIX;
- }
- }
-#endif
- // The '\n' is escaped if there is an odd number of ^V's just
- // before it, first set "c" just before the 'V's and then check
- // len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo
- for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) {}
- if ((len & 1) != (c & 1)) { // escaped NL, read more
- sp->sourcing_lnum++;
- continue;
- }
-
- buf[len - 1] = NUL; // remove the NL
- }
-
- // Check for ^C here now and then, so recursive :so can be broken.
- line_breakcheck();
- break;
- }
-
- if (have_read) {
- return ga.ga_data;
- }
-
- xfree(ga.ga_data);
- return NULL;
-}
-
-/// Called when starting to read a script line.
-/// "sourcing_lnum" must be correct!
-/// When skipping lines it may not actually be executed, but we won't find out
-/// until later and we need to store the time now.
-void script_line_start(void)
-{
- scriptitem_T *si;
- sn_prl_T *pp;
-
- if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
- return;
- }
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && sourcing_lnum >= 1) {
- // Grow the array before starting the timer, so that the time spent
- // here isn't counted.
- (void)ga_grow(&si->sn_prl_ga, sourcing_lnum - si->sn_prl_ga.ga_len);
- si->sn_prl_idx = sourcing_lnum - 1;
- while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
- && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
- // Zero counters for a line that was not used before.
- pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
- pp->snp_count = 0;
- pp->sn_prl_total = profile_zero();
- pp->sn_prl_self = profile_zero();
- si->sn_prl_ga.ga_len++;
- }
- si->sn_prl_execed = false;
- si->sn_prl_start = profile_start();
- si->sn_prl_children = profile_zero();
- si->sn_prl_wait = profile_get_wait();
- }
-}
-
-/// Called when actually executing a function line.
-void script_line_exec(void)
-{
- scriptitem_T *si;
-
- if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
- return;
- }
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && si->sn_prl_idx >= 0) {
- si->sn_prl_execed = true;
- }
-}
-
-/// Called when done with a function line.
-void script_line_end(void)
-{
- scriptitem_T *si;
- sn_prl_T *pp;
-
- if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
- return;
- }
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
- if (si->sn_prof_on && si->sn_prl_idx >= 0
- && si->sn_prl_idx < si->sn_prl_ga.ga_len) {
- if (si->sn_prl_execed) {
- pp = &PRL_ITEM(si, si->sn_prl_idx);
- pp->snp_count++;
- si->sn_prl_start = profile_end(si->sn_prl_start);
- si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start);
- pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start);
- pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start,
- si->sn_prl_children);
- }
- si->sn_prl_idx = -1;
- }
-}
-
-/// ":scriptencoding": Set encoding conversion for a sourced script.
-/// Without the multi-byte feature it's simply ignored.
-void ex_scriptencoding(exarg_T *eap)
-{
- struct source_cookie *sp;
- char *name;
-
- if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
- emsg(_("E167: :scriptencoding used outside of a sourced file"));
- return;
- }
-
- if (*eap->arg != NUL) {
- name = (char *)enc_canonize((char_u *)eap->arg);
- } else {
- name = eap->arg;
- }
-
- // Setup for conversion from the specified encoding to 'encoding'.
- sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
- convert_setup(&sp->conv, (char_u *)name, p_enc);
-
- if (name != eap->arg) {
- xfree(name);
- }
-}
-
-/// ":finish": Mark a sourced file as finished.
-void ex_finish(exarg_T *eap)
-{
- if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
- do_finish(eap, false);
- } else {
- emsg(_("E168: :finish used outside of a sourced file"));
- }
-}
-
-/// Mark a sourced file as finished. Possibly makes the ":finish" pending.
-/// Also called for a pending finish at the ":endtry" or after returning from
-/// an extra do_cmdline(). "reanimate" is used in the latter case.
-void do_finish(exarg_T *eap, int reanimate)
-{
- int idx;
-
- if (reanimate) {
- ((struct source_cookie *)getline_cookie(eap->getline,
- eap->cookie))->finished = false;
- }
-
- // Cleanup (and deactivate) conditionals, but stop when a try conditional
- // not in its finally clause (which then is to be executed next) is found.
- // In this case, make the ":finish" pending for execution at the ":endtry".
- // Otherwise, finish normally.
- idx = cleanup_conditionals(eap->cstack, 0, true);
- if (idx >= 0) {
- eap->cstack->cs_pending[idx] = CSTP_FINISH;
- report_make_pending(CSTP_FINISH, NULL);
- } else {
- ((struct source_cookie *)getline_cookie(eap->getline,
- eap->cookie))->finished = true;
- }
-}
-
-/// @return true when a sourced file had the ":finish" command: Don't give error
-/// message for missing ":endif".
-/// false when not sourcing a file.
-bool source_finished(LineGetter fgetline, void *cookie)
-{
- return getline_equal(fgetline, cookie, getsourceline)
- && ((struct source_cookie *)getline_cookie(fgetline, cookie))->finished;
-}
-
/// ":checktime [buffer]"
void ex_checktime(exarg_T *eap)
{
@@ -2631,324 +772,6 @@ void ex_checktime(exarg_T *eap)
no_check_timestamps = save_no_check_timestamps;
}
-#if defined(HAVE_LOCALE_H)
-# define HAVE_GET_LOCALE_VAL
-
-static char *get_locale_val(int what)
-{
- // Obtain the locale value from the libraries.
- char *loc = setlocale(what, NULL);
-
- return loc;
-}
-#endif
-
-/// @return true when "lang" starts with a valid language name.
-/// Rejects NULL, empty string, "C", "C.UTF-8" and others.
-static bool is_valid_mess_lang(char *lang)
-{
- return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
-}
-
-/// Obtain the current messages language. Used to set the default for
-/// 'helplang'. May return NULL or an empty string.
-char *get_mess_lang(void)
-{
- char *p;
-
-#ifdef HAVE_GET_LOCALE_VAL
-# if defined(LC_MESSAGES)
- p = get_locale_val(LC_MESSAGES);
-# else
- // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
- // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
- // and LC_MONETARY may be set differently for a Japanese working in the
- // US.
- p = get_locale_val(LC_COLLATE);
-# endif
-#else
- p = os_getenv("LC_ALL");
- if (!is_valid_mess_lang(p)) {
- p = os_getenv("LC_MESSAGES");
- if (!is_valid_mess_lang(p)) {
- p = os_getenv("LANG");
- }
- }
-#endif
- return is_valid_mess_lang(p) ? p : NULL;
-}
-
-// Complicated #if; matches with where get_mess_env() is used below.
-#ifdef HAVE_WORKING_LIBINTL
-/// Get the language used for messages from the environment.
-static char *get_mess_env(void)
-{
- char *p;
-
- p = (char *)os_getenv("LC_ALL");
- if (p == NULL) {
- p = (char *)os_getenv("LC_MESSAGES");
- if (p == NULL) {
- p = (char *)os_getenv("LANG");
- if (p != NULL && ascii_isdigit(*p)) {
- p = NULL; // ignore something like "1043"
- }
-# ifdef HAVE_GET_LOCALE_VAL
- if (p == NULL) {
- p = get_locale_val(LC_CTYPE);
- }
-# endif
- }
- }
- return p;
-}
-
-#endif
-
-/// Set the "v:lang" variable according to the current locale setting.
-/// Also do "v:lc_time"and "v:ctype".
-void set_lang_var(void)
-{
- const char *loc;
-
-#ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_CTYPE);
-#else
- // setlocale() not supported: use the default value
- loc = "C";
-#endif
- set_vim_var_string(VV_CTYPE, loc, -1);
-
- // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
- // back to LC_CTYPE if it's empty.
-#ifdef HAVE_WORKING_LIBINTL
- loc = get_mess_env();
-#elif defined(LC_MESSAGES)
- loc = get_locale_val(LC_MESSAGES);
-#else
- // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE
- loc = get_locale_val(LC_CTYPE);
-#endif
- set_vim_var_string(VV_LANG, loc, -1);
-
-#ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_TIME);
-#endif
- set_vim_var_string(VV_LC_TIME, loc, -1);
-
-#ifdef HAVE_GET_LOCALE_VAL
- loc = get_locale_val(LC_COLLATE);
-#else
- // setlocale() not supported: use the default value
- loc = "C";
-#endif
- set_vim_var_string(VV_COLLATE, loc, -1);
-}
-
-#ifdef HAVE_WORKING_LIBINTL
-
-/// ":language": Set the language (locale).
-///
-/// @param eap
-void ex_language(exarg_T *eap)
-{
- char *loc;
- char *p;
- char *name;
- int what = LC_ALL;
- char *whatstr = "";
-# ifdef LC_MESSAGES
-# define VIM_LC_MESSAGES LC_MESSAGES
-# else
-# define VIM_LC_MESSAGES 6789
-# endif
-
- name = eap->arg;
-
- // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
- // Allow abbreviation, but require at least 3 characters to avoid
- // confusion with a two letter language name "me" or "ct".
- p = (char *)skiptowhite((char_u *)eap->arg);
- if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) {
- if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) {
- what = VIM_LC_MESSAGES;
- name = skipwhite(p);
- whatstr = "messages ";
- } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) {
- what = LC_CTYPE;
- name = skipwhite(p);
- whatstr = "ctype ";
- } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) {
- what = LC_TIME;
- name = skipwhite(p);
- whatstr = "time ";
- } else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0) {
- what = LC_COLLATE;
- name = skipwhite(p);
- whatstr = "collate ";
- }
- }
-
- if (*name == NUL) {
- if (what == VIM_LC_MESSAGES) {
- p = get_mess_env();
- } else {
- p = setlocale(what, NULL);
- }
- if (p == NULL || *p == NUL) {
- p = "Unknown";
- }
- smsg(_("Current %slanguage: \"%s\""), whatstr, p);
- } else {
-# ifndef LC_MESSAGES
- if (what == VIM_LC_MESSAGES) {
- loc = "";
- } else {
-# endif
- loc = setlocale(what, name);
-# ifdef LC_NUMERIC
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
-# ifndef LC_MESSAGES
- }
-# endif
- if (loc == NULL) {
- semsg(_("E197: Cannot set language to \"%s\""), name);
- } else {
-# ifdef HAVE_NL_MSG_CAT_CNTR
- // Need to do this for GNU gettext, otherwise cached translations
- // will be used again.
- extern int _nl_msg_cat_cntr;
-
- _nl_msg_cat_cntr++;
-# endif
- // Reset $LC_ALL, otherwise it would overrule everything.
- os_setenv("LC_ALL", "", 1);
-
- if (what != LC_TIME && what != LC_COLLATE) {
- // Tell gettext() what to translate to. It apparently doesn't
- // use the currently effective locale.
- if (what == LC_ALL) {
- os_setenv("LANG", name, 1);
-
- // Clear $LANGUAGE because GNU gettext uses it.
- os_setenv("LANGUAGE", "", 1);
- }
- if (what != LC_CTYPE) {
- os_setenv("LC_MESSAGES", name, 1);
- set_helplang_default(name);
- }
- }
-
- // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
- set_lang_var();
- maketitle();
- }
- }
-}
-
-static char **locales = NULL; // Array of all available locales
-
-# ifndef WIN32
-static bool did_init_locales = false;
-
-/// @return an array of strings for all available locales + NULL for the
-/// last element or,
-/// NULL in case of error.
-static char **find_locales(void)
-{
- garray_T locales_ga;
- char *loc;
- char *saveptr = NULL;
-
- // Find all available locales by running command "locale -a". If this
- // doesn't work we won't have completion.
- char *locale_a = (char *)get_cmd_output((char_u *)"locale -a", NULL,
- kShellOptSilent, NULL);
- if (locale_a == NULL) {
- return NULL;
- }
- ga_init(&locales_ga, sizeof(char_u *), 20);
-
- // Transform locale_a string where each locale is separated by "\n"
- // into an array of locale strings.
- loc = os_strtok(locale_a, "\n", &saveptr);
-
- while (loc != NULL) {
- loc = xstrdup(loc);
- GA_APPEND(char *, &locales_ga, loc);
- loc = os_strtok(NULL, "\n", &saveptr);
- }
- xfree(locale_a);
- // Guarantee that .ga_data is NULL terminated
- ga_grow(&locales_ga, 1);
- ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
- return locales_ga.ga_data;
-}
-# endif
-
-/// Lazy initialization of all available locales.
-static void init_locales(void)
-{
-# ifndef WIN32
- if (!did_init_locales) {
- did_init_locales = true;
- locales = find_locales();
- }
-# endif
-}
-
-# if defined(EXITFREE)
-void free_locales(void)
-{
- int i;
- if (locales != NULL) {
- for (i = 0; locales[i] != NULL; i++) {
- xfree(locales[i]);
- }
- XFREE_CLEAR(locales);
- }
-}
-
-# endif
-
-/// Function given to ExpandGeneric() to obtain the possible arguments of the
-/// ":language" command.
-char *get_lang_arg(expand_T *xp, int idx)
-{
- if (idx == 0) {
- return "messages";
- }
- if (idx == 1) {
- return "ctype";
- }
- if (idx == 2) {
- return "time";
- }
- if (idx == 3) {
- return "collate";
- }
-
- init_locales();
- if (locales == NULL) {
- return NULL;
- }
- return locales[idx - 4];
-}
-
-/// Function given to ExpandGeneric() to obtain the available locales.
-char *get_locales(expand_T *xp, int idx)
-{
- init_locales();
- if (locales == NULL) {
- return NULL;
- }
- return locales[idx];
-}
-
-#endif
-
static void script_host_execute(char *name, exarg_T *eap)
{
size_t len;
@@ -3007,7 +830,7 @@ void ex_drop(exarg_T *eap)
// and mostly only one file is dropped.
// This also ignores wildcards, since it is very unlikely the user is
// editing a file name with a wildcard character.
- do_arglist(eap->arg, AL_SET, 0, false);
+ set_arglist(eap->arg);
// Expanding wildcards may result in an empty argument list. E.g. when
// editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we
diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h
index c463bfa5ab..3a41e105f3 100644
--- a/src/nvim/ex_cmds2.h
+++ b/src/nvim/ex_cmds2.h
@@ -1,10 +1,7 @@
#ifndef NVIM_EX_CMDS2_H
#define NVIM_EX_CMDS2_H
-#include <stdbool.h>
-
-#include "nvim/ex_docmd.h"
-#include "nvim/runtime.h"
+#include "nvim/ex_cmds_defs.h"
//
// flags for check_changed()
@@ -15,27 +12,6 @@
#define CCGD_ALLBUF 8 // may write all buffers
#define CCGD_EXCMD 16 // may suggest using !
-typedef struct scriptitem_S {
- char_u *sn_name;
- bool sn_prof_on; ///< true when script is/was profiled
- bool sn_pr_force; ///< forceit: profile functions in this script
- proftime_T sn_pr_child; ///< time set when going into first child
- int sn_pr_nest; ///< nesting for sn_pr_child
- // profiling the script as a whole
- int sn_pr_count; ///< nr of times sourced
- proftime_T sn_pr_total; ///< time spent in script + children
- proftime_T sn_pr_self; ///< time spent in script itself
- proftime_T sn_pr_start; ///< time at script start
- proftime_T sn_pr_children; ///< time in children after script start
- // profiling the script per line
- garray_T sn_prl_ga; ///< things stored for every line
- proftime_T sn_prl_start; ///< start time for current line
- proftime_T sn_prl_children; ///< time spent in children for this line
- proftime_T sn_prl_wait; ///< wait start time for current line
- linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
- int sn_prl_execed; ///< line being timed was executed
-} scriptitem_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds2.h.generated.h"
#endif
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index e80e47bcff..71956b2246 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -188,7 +188,7 @@ struct exarg {
cmdidx_T cmdidx; ///< the index for the command
uint32_t argt; ///< flags for the command
int skip; ///< don't execute the command, only parse it
- int forceit; ///< TRUE if ! present
+ int forceit; ///< true if ! present
int addr_count; ///< the number of addresses given
linenr_T line1; ///< the first line number
linenr_T line2; ///< the second line number or count
@@ -196,8 +196,8 @@ struct exarg {
int flags; ///< extra flags after count: EXFLAG_
char *do_ecmd_cmd; ///< +command arg to be used in edited file
linenr_T do_ecmd_lnum; ///< the line number in an edited file
- int append; ///< TRUE with ":w >>file" command
- int usefilter; ///< TRUE with ":w !command" and ":r!command"
+ int append; ///< true with ":w >>file" command
+ int usefilter; ///< true with ":w !command" and ":r!command"
int amount; ///< number of '>' or '<' for shift command
int regname; ///< register name (NUL if none)
int force_bin; ///< 0, FORCE_BIN or FORCE_NOBIN
@@ -230,13 +230,15 @@ struct expand {
sctx_T xp_script_ctx; // SCTX for completion function
int xp_backslash; // one of the XP_BS_ values
#ifndef BACKSLASH_IN_FILENAME
- int xp_shell; // TRUE for a shell command, more
+ int xp_shell; // true for a shell command, more
// characters need to be escaped
#endif
int xp_numfiles; // number of files found by file name completion
int xp_col; // cursor position in line
char **xp_files; // list of files
char *xp_line; // text being completed
+#define EXPAND_BUF_LEN 256
+ char xp_buf[EXPAND_BUF_LEN]; // buffer for returned match
};
// values for xp_backslash
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 0dde82c235..26b0c0f1ef 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -9,14 +9,18 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/debugger.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
@@ -38,10 +42,12 @@
#include "nvim/getchar.h"
#include "nvim/globals.h"
#include "nvim/hardcopy.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/if_cscope.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
+#include "nvim/locale.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
@@ -57,15 +63,16 @@
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
+#include "nvim/profile.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
@@ -88,6 +95,12 @@ static char e_ambiguous_use_of_user_defined_command[]
= N_("E464: Ambiguous use of user-defined command");
static char e_not_an_editor_command[]
= N_("E492: Not an editor command");
+static char e_no_source_file_name_to_substitute_for_sfile[]
+ = N_("E498: no :source file name to substitute for \"<sfile>\"");
+static char e_no_call_stack_to_substitute_for_stack[]
+ = N_("E489: no call stack to substitute for \"<stack>\"");
+static char e_no_script_file_name_to_substitute_for_script[]
+ = N_("E1274: No script file name to substitute for \"<script>\"");
static int quitmore = 0;
static bool ex_pressedreturn = false;
@@ -100,16 +113,14 @@ typedef struct {
#define FREE_WCMD(wcmd) xfree((wcmd)->line)
-/*
- * Structure used to store info for line position in a while or for loop.
- * This is required, because do_one_cmd() may invoke ex_function(), which
- * reads more lines that may come from the while/for loop.
- */
+/// Structure used to store info for line position in a while or for loop.
+/// This is required, because do_one_cmd() may invoke ex_function(), which
+/// reads more lines that may come from the while/for loop.
struct loop_cookie {
garray_T *lines_gap; // growarray with line info
int current_line; // last read line from growarray
- int repeating; // TRUE when looping a second time
- // When "repeating" is FALSE use "getline" and "cookie" to get lines
+ int repeating; // true when looping a second time
+ // When "repeating" is false use "getline" and "cookie" to get lines
char *(*getline)(int, void *, int, bool);
void *cookie;
};
@@ -123,6 +134,7 @@ struct dbg_stuff {
char *vv_throwpoint;
int did_emsg;
int got_int;
+ bool did_throw;
int need_rethrow;
int check_cstack;
except_T *current_exception;
@@ -136,9 +148,7 @@ struct dbg_stuff {
# define ex_language ex_ni
#endif
-/*
- * Declare cmdnames[].
- */
+// Declare cmdnames[].
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_cmds_defs.generated.h"
#endif
@@ -148,7 +158,7 @@ static char dollar_command[2] = { '$', 0 };
static void save_dbg_stuff(struct dbg_stuff *dsp)
{
dsp->trylevel = trylevel; trylevel = 0;
- dsp->force_abort = force_abort; force_abort = FALSE;
+ dsp->force_abort = force_abort; force_abort = false;
dsp->caught_stack = caught_stack; caught_stack = NULL;
dsp->vv_exception = v_exception(NULL);
dsp->vv_throwpoint = v_throwpoint(NULL);
@@ -156,6 +166,7 @@ static void save_dbg_stuff(struct dbg_stuff *dsp)
// Necessary for debugging an inactive ":catch", ":finally", ":endtry".
dsp->did_emsg = did_emsg; did_emsg = false;
dsp->got_int = got_int; got_int = false;
+ dsp->did_throw = did_throw; did_throw = false;
dsp->need_rethrow = need_rethrow; need_rethrow = false;
dsp->check_cstack = check_cstack; check_cstack = false;
dsp->current_exception = current_exception; current_exception = NULL;
@@ -171,6 +182,7 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp)
(void)v_throwpoint(dsp->vv_throwpoint);
did_emsg = dsp->did_emsg;
got_int = dsp->got_int;
+ did_throw = dsp->did_throw;
need_rethrow = dsp->need_rethrow;
check_cstack = dsp->check_cstack;
current_exception = dsp->current_exception;
@@ -179,11 +191,6 @@ static void restore_dbg_stuff(struct dbg_stuff *dsp)
/// Repeatedly get commands for Ex mode, until the ":vi" command is given.
void do_exmode(void)
{
- int save_msg_scroll;
- int prev_msg_row;
- linenr_T prev_line;
- varnumber_T changedtick;
-
exmode_active = true;
State = MODE_NORMAL;
may_trigger_modechanged();
@@ -194,7 +201,7 @@ void do_exmode(void)
return;
}
- save_msg_scroll = msg_scroll;
+ int save_msg_scroll = msg_scroll;
RedrawingDisabled++; // don't redisplay the window
no_wait_return++; // don't wait for return
@@ -209,9 +216,9 @@ void do_exmode(void)
need_wait_return = false;
ex_pressedreturn = false;
ex_no_reprint = false;
- changedtick = buf_get_changedtick(curbuf);
- prev_msg_row = msg_row;
- prev_line = curwin->w_cursor.lnum;
+ varnumber_T changedtick = buf_get_changedtick(curbuf);
+ int prev_msg_row = msg_row;
+ linenr_T prev_line = curwin->w_cursor.lnum;
cmdline_row = msg_row;
do_cmdline(NULL, getexline, NULL, 0);
lines_left = Rows - 1;
@@ -232,7 +239,7 @@ void do_exmode(void)
}
}
msg_col = 0;
- print_line_no_prefix(curwin->w_cursor.lnum, FALSE, FALSE);
+ print_line_no_prefix(curwin->w_cursor.lnum, false, false);
msg_clr_eos();
}
} else if (ex_pressedreturn && !ex_no_reprint) { // must be at EOF
@@ -246,8 +253,8 @@ void do_exmode(void)
RedrawingDisabled--;
no_wait_return--;
- redraw_all_later(NOT_VALID);
- update_screen(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
+ update_screen(UPD_NOT_VALID);
need_wait_return = false;
msg_scroll = save_msg_scroll;
}
@@ -301,26 +308,26 @@ int do_cmdline_cmd(const char *cmd)
/// @return FAIL if cmdline could not be executed, OK otherwise
int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
{
- char *next_cmdline; // next cmd to execute
- char *cmdline_copy = NULL; // copy of cmd line
+ char *next_cmdline; // next cmd to execute
+ char *cmdline_copy = NULL; // copy of cmd line
bool used_getline = false; // used "fgetline" to obtain command
static int recursive = 0; // recursive depth
bool msg_didout_before_start = false;
int count = 0; // line number count
- int did_inc = FALSE; // incremented RedrawingDisabled
+ bool did_inc = false; // incremented RedrawingDisabled
int retval = OK;
cstack_T cstack = { // conditional stack
.cs_idx = -1,
};
garray_T lines_ga; // keep lines for ":while"/":for"
int current_line = 0; // active line in lines_ga
- char *fname = NULL; // function or script name
+ char *fname = NULL; // function or script name
linenr_T *breakpoint = NULL; // ptr to breakpoint field in cookie
- int *dbg_tick = NULL; // ptr to dbg_tick field in cookie
+ int *dbg_tick = NULL; // ptr to dbg_tick field in cookie
struct dbg_stuff debug_saved; // saved things for debug mode
int initial_trylevel;
- struct msglist **saved_msg_list = NULL;
- struct msglist *private_msg_list;
+ msglist_T **saved_msg_list = NULL;
+ msglist_T *private_msg_list;
// "fgetline" and "cookie" passed to do_one_cmd()
char *(*cmd_getline)(int, void *, int, bool);
@@ -361,7 +368,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// Inside a function use a higher nesting level.
getline_is_func = getline_equal(fgetline, cookie, get_func_line);
if (getline_is_func && ex_nesting_level == func_level(real_cookie)) {
- ++ex_nesting_level;
+ ex_nesting_level++;
}
// Get the function or script name and the address where the next breakpoint
@@ -371,14 +378,12 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
breakpoint = func_breakpoint(real_cookie);
dbg_tick = func_dbg_tick(real_cookie);
} else if (getline_equal(fgetline, cookie, getsourceline)) {
- fname = sourcing_name;
+ fname = SOURCING_NAME;
breakpoint = source_breakpoint(real_cookie);
dbg_tick = source_dbg_tick(real_cookie);
}
- /*
- * Initialize "force_abort" and "suppress_errthrow" at the top level.
- */
+ // Initialize "force_abort" and "suppress_errthrow" at the top level.
if (!recursive) {
force_abort = false;
suppress_errthrow = false;
@@ -390,13 +395,14 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (flags & DOCMD_EXCRESET) {
save_dbg_stuff(&debug_saved);
} else {
- memset(&debug_saved, 0, sizeof(debug_saved));
+ CLEAR_FIELD(debug_saved);
}
initial_trylevel = trylevel;
- current_exception = NULL;
- // "did_emsg" will be set to TRUE when emsg() is used, in which case we
+ // "did_throw" will be set to true when an exception is being thrown.
+ did_throw = false;
+ // "did_emsg" will be set to true when emsg() is used, in which case we
// cancel the whole command line, and any if/endif or loop.
// If force_abort is set, we cancel everything.
did_emsg = false;
@@ -408,12 +414,10 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
KeyTyped = false;
}
- /*
- * Continue executing command lines:
- * - when inside an ":if", ":while" or ":for"
- * - for multiple commands on one line, separated with '|'
- * - when repeating until there are no more lines (for ":source")
- */
+ // Continue executing command lines:
+ // - when inside an ":if", ":while" or ":for"
+ // - for multiple commands on one line, separated with '|'
+ // - when repeating until there are no more lines (for ":source")
next_cmdline = cmdline;
do {
getline_is_func = getline_equal(fgetline, cookie, get_func_line);
@@ -427,11 +431,9 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
did_emsg = false;
}
- /*
- * 1. If repeating a line in a loop, get a line from lines_ga.
- * 2. If no line given: Get an allocated line with fgetline().
- * 3. If a line is given: Make a copy, so we can mess with it.
- */
+ // 1. If repeating a line in a loop, get a line from lines_ga.
+ // 2. If no line given: Get an allocated line with fgetline().
+ // 3. If a line is given: Make a copy, so we can mess with it.
// 1. If repeating, get a previous line from lines_ga.
if (cstack.cs_looplevel > 0 && current_line < lines_ga.ga_len) {
@@ -464,20 +466,19 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (breakpoint != NULL && dbg_tick != NULL
&& *dbg_tick != debug_tick) {
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname, sourcing_lnum);
+ fname, SOURCING_LNUM);
*dbg_tick = debug_tick;
}
next_cmdline = ((wcmd_T *)(lines_ga.ga_data))[current_line].line;
- sourcing_lnum = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
+ SOURCING_LNUM = ((wcmd_T *)(lines_ga.ga_data))[current_line].lnum;
// Did we encounter a breakpoint?
- if (breakpoint != NULL && *breakpoint != 0
- && *breakpoint <= sourcing_lnum) {
- dbg_breakpoint((char_u *)fname, sourcing_lnum);
+ if (breakpoint != NULL && *breakpoint != 0 && *breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint(fname, SOURCING_LNUM);
// Find next breakpoint.
*breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname, sourcing_lnum);
+ fname, SOURCING_LNUM);
*dbg_tick = debug_tick;
}
if (do_profiling == PROF_YES) {
@@ -509,10 +510,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// 2. If no line given, get an allocated line with fgetline().
if (next_cmdline == NULL) {
- /*
- * Need to set msg_didout for the first line after an ":if",
- * otherwise the ":if" will be overwritten.
- */
+ // Need to set msg_didout for the first line after an ":if",
+ // otherwise the ":if" will be overwritten.
if (count == 1 && getline_equal(fgetline, cookie, getexline)) {
msg_didout = true;
}
@@ -521,7 +520,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
cstack.cs_idx <
0 ? 0 : (cstack.cs_idx + 1) * 2,
true)) == NULL) {
- // Don't call wait_return for aborted command line. The NULL
+ // Don't call wait_return() for aborted command line. The NULL
// returned for the end of a sourced file or executed function
// doesn't do this.
if (KeyTyped && !(flags & DOCMD_REPEAT)) {
@@ -532,13 +531,11 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
used_getline = true;
- /*
- * Keep the first typed line. Clear it when more lines are typed.
- */
+ // Keep the first typed line. Clear it when more lines are typed.
if (flags & DOCMD_KEEPLINE) {
xfree(repeat_cmdline);
if (count == 0) {
- repeat_cmdline = vim_strsave((char_u *)next_cmdline);
+ repeat_cmdline = (char *)vim_strsave((char_u *)next_cmdline);
} else {
repeat_cmdline = NULL;
}
@@ -549,13 +546,11 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
cmdline_copy = next_cmdline;
- /*
- * Save the current line when inside a ":while" or ":for", and when
- * the command looks like a ":while" or ":for", because we may need it
- * later. When there is a '|' and another command, it is stored
- * separately, because we need to be able to jump back to it from an
- * :endwhile/:endfor.
- */
+ // Save the current line when inside a ":while" or ":for", and when
+ // the command looks like a ":while" or ":for", because we may need it
+ // later. When there is a '|' and another command, it is stored
+ // separately, because we need to be able to jump back to it from an
+ // :endwhile/:endfor.
if (current_line == lines_ga.ga_len
&& (cstack.cs_looplevel || has_loop_cmd(next_cmdline))) {
store_loop_line(&lines_ga, next_cmdline);
@@ -563,32 +558,28 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
did_endif = false;
if (count++ == 0) {
- /*
- * All output from the commands is put below each other, without
- * waiting for a return. Don't do this when executing commands
- * from a script or when being called recursive (e.g. for ":e
- * +command file").
- */
+ // All output from the commands is put below each other, without
+ // waiting for a return. Don't do this when executing commands
+ // from a script or when being called recursive (e.g. for ":e
+ // +command file").
if (!(flags & DOCMD_NOWAIT) && !recursive) {
msg_didout_before_start = msg_didout;
msg_didany = false; // no output yet
msg_start();
- msg_scroll = TRUE; // put messages below each other
- ++no_wait_return; // don't wait for return until finished
- ++RedrawingDisabled;
- did_inc = TRUE;
+ msg_scroll = true; // put messages below each other
+ no_wait_return++; // don't wait for return until finished
+ RedrawingDisabled++;
+ did_inc = true;
}
}
- if ((p_verbose >= 15 && sourcing_name != NULL) || p_verbose >= 16) {
- msg_verbose_cmd(sourcing_lnum, cmdline_copy);
+ if ((p_verbose >= 15 && SOURCING_NAME != NULL) || p_verbose >= 16) {
+ msg_verbose_cmd(SOURCING_LNUM, cmdline_copy);
}
- /*
- * 2. Execute one '|' separated command.
- * do_one_cmd() will return NULL if there is no trailing '|'.
- * "cmdline_copy" can change, e.g. for '%' and '#' expansion.
- */
+ // 2. Execute one '|' separated command.
+ // do_one_cmd() will return NULL if there is no trailing '|'.
+ // "cmdline_copy" can change, e.g. for '%' and '#' expansion.
recursive++;
next_cmdline = do_one_cmd(&cmdline_copy, flags, &cstack, cmd_getline, cmd_cookie);
recursive--;
@@ -601,10 +592,9 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (next_cmdline == NULL) {
XFREE_CLEAR(cmdline_copy);
- //
+
// If the command was typed, remember it for the ':' register.
// Do this AFTER executing the command to make :@: work.
- //
if (getline_equal(fgetline, cookie, getexline)
&& new_last_cmdline != NULL) {
xfree(last_cmdline);
@@ -622,18 +612,16 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
if (did_emsg && !force_abort
&& getline_equal(fgetline, cookie, get_func_line)
&& !func_has_abort(real_cookie)) {
- did_emsg = FALSE;
+ did_emsg = false;
}
if (cstack.cs_looplevel > 0) {
- ++current_line;
-
- /*
- * An ":endwhile", ":endfor" and ":continue" is handled here.
- * If we were executing commands, jump back to the ":while" or
- * ":for".
- * If we were not executing commands, decrement cs_looplevel.
- */
+ current_line++;
+
+ // An ":endwhile", ":endfor" and ":continue" is handled here.
+ // If we were executing commands, jump back to the ":while" or
+ // ":for".
+ // If we were not executing commands, decrement cs_looplevel.
if (cstack.cs_lflags & (CSL_HAD_CONT | CSL_HAD_ENDLOOP)) {
cstack.cs_lflags &= ~(CSL_HAD_CONT | CSL_HAD_ENDLOOP);
@@ -641,7 +629,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// not to use a cs_line[] from an entry that isn't a ":while"
// or ":for": It would make "current_line" invalid and can
// cause a crash.
- if (!did_emsg && !got_int && !current_exception
+ if (!did_emsg && !got_int && !did_throw
&& cstack.cs_idx >= 0
&& (cstack.cs_flags[cstack.cs_idx]
& (CSF_WHILE | CSF_FOR))
@@ -655,8 +643,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// Check for the next breakpoint at or after the ":while"
// or ":for".
if (breakpoint != NULL) {
- *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline),
- (char_u *)fname,
+ *breakpoint = dbg_find_breakpoint(getline_equal(fgetline, cookie, getsourceline), fname,
((wcmd_T *)lines_ga.ga_data)[current_line].lnum - 1);
*dbg_tick = debug_tick;
}
@@ -667,41 +654,33 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
CSF_WHILE | CSF_FOR, &cstack.cs_looplevel);
}
}
- }
- /*
- * For a ":while" or ":for" we need to remember the line number.
- */
- else if (cstack.cs_lflags & CSL_HAD_LOOP) {
+ } else if (cstack.cs_lflags & CSL_HAD_LOOP) {
+ // For a ":while" or ":for" we need to remember the line number.
cstack.cs_lflags &= ~CSL_HAD_LOOP;
cstack.cs_line[cstack.cs_idx] = current_line - 1;
}
}
- /*
- * When not inside any ":while" loop, clear remembered lines.
- */
+ // When not inside any ":while" loop, clear remembered lines.
if (cstack.cs_looplevel == 0) {
if (!GA_EMPTY(&lines_ga)) {
- sourcing_lnum = ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
+ SOURCING_LNUM = ((wcmd_T *)lines_ga.ga_data)[lines_ga.ga_len - 1].lnum;
GA_DEEP_CLEAR(&lines_ga, wcmd_T, FREE_WCMD);
}
current_line = 0;
}
- /*
- * A ":finally" makes did_emsg, got_int and current_exception pending for
- * being restored at the ":endtry". Reset them here and set the
- * ACTIVE and FINALLY flags, so that the finally clause gets executed.
- * This includes the case where a missing ":endif", ":endwhile" or
- * ":endfor" was detected by the ":finally" itself.
- */
+ // A ":finally" makes did_emsg, got_int and did_throw pending for
+ // being restored at the ":endtry". Reset them here and set the
+ // ACTIVE and FINALLY flags, so that the finally clause gets executed.
+ // This includes the case where a missing ":endif", ":endwhile" or
+ // ":endfor" was detected by the ":finally" itself.
if (cstack.cs_lflags & CSL_HAD_FINA) {
cstack.cs_lflags &= ~CSL_HAD_FINA;
report_make_pending((cstack.cs_pending[cstack.cs_idx]
& (CSTP_ERROR | CSTP_INTERRUPT | CSTP_THROW)),
- current_exception);
- did_emsg = got_int = false;
- current_exception = NULL;
+ did_throw ? current_exception : NULL);
+ did_emsg = got_int = did_throw = false;
cstack.cs_flags[cstack.cs_idx] |= CSF_ACTIVE | CSF_FINALLY;
}
@@ -714,45 +693,41 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// exception, cancel everything. If it is left normally, reset
// force_abort to get the non-EH compatible abortion behavior for
// the rest of the script.
- if (trylevel == 0 && !did_emsg && !got_int && !current_exception) {
+ if (trylevel == 0 && !did_emsg && !got_int && !did_throw) {
force_abort = false;
}
// Convert an interrupt to an exception if appropriate.
(void)do_intthrow(&cstack);
- }
- /*
- * Continue executing command lines when:
- * - no CTRL-C typed, no aborting error, no exception thrown or try
- * conditionals need to be checked for executing finally clauses or
- * catching an interrupt exception
- * - didn't get an error message or lines are not typed
- * - there is a command after '|', inside a :if, :while, :for or :try, or
- * looping for ":source" command or function call.
- */
- while (!((got_int || (did_emsg && force_abort) || current_exception)
- && cstack.cs_trylevel == 0)
- && !(did_emsg
- // Keep going when inside try/catch, so that the error can be
- // deal with, except when it is a syntax error, it may cause
- // the :endtry to be missed.
- && (cstack.cs_trylevel == 0 || did_emsg_syntax)
- && used_getline
- && getline_equal(fgetline, cookie, getexline))
- && (next_cmdline != NULL
- || cstack.cs_idx >= 0
- || (flags & DOCMD_REPEAT)));
+
+ // Continue executing command lines when:
+ // - no CTRL-C typed, no aborting error, no exception thrown or try
+ // conditionals need to be checked for executing finally clauses or
+ // catching an interrupt exception
+ // - didn't get an error message or lines are not typed
+ // - there is a command after '|', inside a :if, :while, :for or :try, or
+ // looping for ":source" command or function call.
+ } while (!((got_int || (did_emsg && force_abort) || did_throw)
+ && cstack.cs_trylevel == 0)
+ && !(did_emsg
+ // Keep going when inside try/catch, so that the error can be
+ // deal with, except when it is a syntax error, it may cause
+ // the :endtry to be missed.
+ && (cstack.cs_trylevel == 0 || did_emsg_syntax)
+ && used_getline
+ && getline_equal(fgetline, cookie, getexline))
+ && (next_cmdline != NULL
+ || cstack.cs_idx >= 0
+ || (flags & DOCMD_REPEAT)));
xfree(cmdline_copy);
did_emsg_syntax = false;
GA_DEEP_CLEAR(&lines_ga, wcmd_T, FREE_WCMD);
if (cstack.cs_idx >= 0) {
- /*
- * If a sourced file or executed function ran to its end, report the
- * unclosed conditional.
- */
- if (!got_int && !current_exception
+ // If a sourced file or executed function ran to its end, report the
+ // unclosed conditional.
+ if (!got_int && !did_throw
&& ((getline_equal(fgetline, cookie, getsourceline)
&& !source_finished(fgetline, cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
@@ -768,18 +743,16 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
}
- /*
- * Reset "trylevel" in case of a ":finish" or ":return" or a missing
- * ":endtry" in a sourced file or executed function. If the try
- * conditional is in its finally clause, ignore anything pending.
- * If it is in a catch clause, finish the caught exception.
- * Also cleanup any "cs_forinfo" structures.
- */
+ // Reset "trylevel" in case of a ":finish" or ":return" or a missing
+ // ":endtry" in a sourced file or executed function. If the try
+ // conditional is in its finally clause, ignore anything pending.
+ // If it is in a catch clause, finish the caught exception.
+ // Also cleanup any "cs_forinfo" structures.
do {
- int idx = cleanup_conditionals(&cstack, 0, TRUE);
+ int idx = cleanup_conditionals(&cstack, 0, true);
if (idx >= 0) {
- --idx; // remove try block not in its finally clause
+ idx--; // remove try block not in its finally clause
}
rewind_conditionals(&cstack, idx, CSF_WHILE | CSF_FOR,
&cstack.cs_looplevel);
@@ -797,19 +770,16 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// conditional, discard the uncaught exception, disable the conversion
// of interrupts or errors to exceptions, and ensure that no more
// commands are executed.
- if (current_exception) {
+ if (did_throw) {
+ assert(current_exception != NULL);
char *p = NULL;
- char *saved_sourcing_name;
- linenr_T saved_sourcing_lnum;
- struct msglist *messages = NULL;
- struct msglist *next;
-
- /*
- * If the uncaught exception is a user exception, report it as an
- * error. If it is an error exception, display the saved error
- * message now. For an interrupt exception, do nothing; the
- * interrupt message is given elsewhere.
- */
+ msglist_T *messages = NULL;
+ msglist_T *next;
+
+ // If the uncaught exception is a user exception, report it as an
+ // error. If it is an error exception, display the saved error
+ // message now. For an interrupt exception, do nothing; the
+ // interrupt message is given elsewhere.
switch (current_exception->type) {
case ET_USER:
vim_snprintf((char *)IObuff, IOSIZE,
@@ -825,10 +795,7 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
break;
}
- saved_sourcing_name = sourcing_name;
- saved_sourcing_lnum = sourcing_lnum;
- sourcing_name = current_exception->throw_name;
- sourcing_lnum = current_exception->throw_lnum;
+ estack_push(ETYPE_EXCEPT, current_exception->throw_name, current_exception->throw_lnum);
current_exception->throw_name = NULL;
discard_current_exception(); // uses IObuff if 'verbose'
@@ -848,9 +815,8 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
emsg(p);
xfree(p);
}
- xfree(sourcing_name);
- sourcing_name = saved_sourcing_name;
- sourcing_lnum = saved_sourcing_lnum;
+ xfree(SOURCING_NAME);
+ estack_pop();
} else if (got_int || (did_emsg && force_abort)) {
// On an interrupt or an aborting error not converted to an exception,
// disable the conversion of errors to exceptions. (Interrupts are not
@@ -867,38 +833,34 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
// cstack belongs to the same function or, respectively, script file, it
// will have to be checked for finally clauses to be executed due to the
// ":return" or ":finish". This is done in do_one_cmd().
- if (current_exception) {
+ if (did_throw) {
need_rethrow = true;
}
if ((getline_equal(fgetline, cookie, getsourceline)
&& ex_nesting_level > source_level(real_cookie))
|| (getline_equal(fgetline, cookie, get_func_line)
&& ex_nesting_level > func_level(real_cookie) + 1)) {
- if (!current_exception) {
+ if (!did_throw) {
check_cstack = true;
}
} else {
// When leaving a function, reduce nesting level.
if (getline_equal(fgetline, cookie, get_func_line)) {
- --ex_nesting_level;
+ ex_nesting_level--;
}
- /*
- * Go to debug mode when returning from a function in which we are
- * single-stepping.
- */
+ // Go to debug mode when returning from a function in which we are
+ // single-stepping.
if ((getline_equal(fgetline, cookie, getsourceline)
|| getline_equal(fgetline, cookie, get_func_line))
&& ex_nesting_level + 1 <= debug_break_level) {
do_debug(getline_equal(fgetline, cookie, getsourceline)
- ? (char_u *)_("End of sourced file")
- : (char_u *)_("End of function"));
+ ? _("End of sourced file")
+ : _("End of function"));
}
}
- /*
- * Restore the exception environment (done after returning from the
- * debugger).
- */
+ // Restore the exception environment (done after returning from the
+ // debugger).
if (flags & DOCMD_EXCRESET) {
restore_dbg_stuff(&debug_saved);
}
@@ -914,32 +876,26 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
}
}
- /*
- * If there was too much output to fit on the command line, ask the user to
- * hit return before redrawing the screen. With the ":global" command we do
- * this only once after the command is finished.
- */
+ // If there was too much output to fit on the command line, ask the user to
+ // hit return before redrawing the screen. With the ":global" command we do
+ // this only once after the command is finished.
if (did_inc) {
- --RedrawingDisabled;
- --no_wait_return;
- msg_scroll = FALSE;
-
- /*
- * When just finished an ":if"-":else" which was typed, no need to
- * wait for hit-return. Also for an error situation.
- */
+ RedrawingDisabled--;
+ no_wait_return--;
+ msg_scroll = false;
+
+ // When just finished an ":if"-":else" which was typed, no need to
+ // wait for hit-return. Also for an error situation.
if (retval == FAIL
|| (did_endif && KeyTyped && !did_emsg)) {
need_wait_return = false;
msg_didany = false; // don't wait when restarting edit
} else if (need_wait_return) {
- /*
- * The msg_start() above clears msg_didout. The wait_return we do
- * here should not overwrite the command that may be shown before
- * doing that.
- */
+ // The msg_start() above clears msg_didout. The wait_return() we do
+ // here should not overwrite the command that may be shown before
+ // doing that.
msg_didout |= msg_didout_before_start;
- wait_return(FALSE);
+ wait_return(false);
}
}
@@ -954,13 +910,12 @@ int do_cmdline(char *cmdline, LineGetter fgetline, void *cookie, int flags)
static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
{
struct loop_cookie *cp = (struct loop_cookie *)cookie;
- wcmd_T *wp;
- char *line;
if (cp->current_line + 1 >= cp->lines_gap->ga_len) {
if (cp->repeating) {
return NULL; // trying to read past ":endwhile"/":endfor"
}
+ char *line;
// First time inside the ":while"/":for": get line normally.
if (cp->getline == NULL) {
line = (char *)getcmdline(c, 0L, indent, do_concat);
@@ -969,7 +924,7 @@ static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
}
if (line != NULL) {
store_loop_line(cp->lines_gap, line);
- ++cp->current_line;
+ cp->current_line++;
}
return line;
@@ -977,8 +932,8 @@ static char *get_loop_line(int c, void *cookie, int indent, bool do_concat)
KeyTyped = false;
cp->current_line++;
- wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
- sourcing_lnum = wp->lnum;
+ wcmd_T *wp = (wcmd_T *)(cp->lines_gap->ga_data) + cp->current_line;
+ SOURCING_LNUM = wp->lnum;
return xstrdup(wp->line);
}
@@ -987,23 +942,20 @@ static void store_loop_line(garray_T *gap, char *line)
{
wcmd_T *p = GA_APPEND_VIA_PTR(wcmd_T, gap);
p->line = xstrdup(line);
- p->lnum = sourcing_lnum;
+ p->lnum = SOURCING_LNUM;
}
-/// If "fgetline" is get_loop_line(), return TRUE if the getline it uses equals
-/// "func". * Otherwise return TRUE when "fgetline" equals "func".
+/// If "fgetline" is get_loop_line(), return true if the getline it uses equals
+/// "func". * Otherwise return true when "fgetline" equals "func".
///
/// @param cookie argument for fgetline()
-int getline_equal(LineGetter fgetline, void *cookie, LineGetter func)
+bool getline_equal(LineGetter fgetline, void *cookie, LineGetter func)
{
- LineGetter gp;
- struct loop_cookie *cp;
-
// When "fgetline" is "get_loop_line()" use the "cookie" to find the
// function that's originally used to obtain the lines. This may be
// nested several levels.
- gp = fgetline;
- cp = (struct loop_cookie *)cookie;
+ LineGetter gp = fgetline;
+ struct loop_cookie *cp = (struct loop_cookie *)cookie;
while (gp == get_loop_line) {
gp = cp->getline;
cp = cp->cookie;
@@ -1017,14 +969,11 @@ int getline_equal(LineGetter fgetline, void *cookie, LineGetter func)
/// @param cookie argument for fgetline()
void *getline_cookie(LineGetter fgetline, void *cookie)
{
- LineGetter gp;
- struct loop_cookie *cp;
-
// When "fgetline" is "get_loop_line()" use the "cookie" to find the
// cookie that's originally used to obtain the lines. This may be nested
// several levels.
- gp = fgetline;
- cp = (struct loop_cookie *)cookie;
+ LineGetter gp = fgetline;
+ struct loop_cookie *cp = (struct loop_cookie *)cookie;
while (gp == get_loop_line) {
gp = cp->getline;
cp = cp->cookie;
@@ -1038,11 +987,10 @@ void *getline_cookie(LineGetter fgetline, void *cookie)
/// @return the buffer number.
static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, long offset)
{
- buf_T *buf;
buf_T *nextbuf;
long count = offset;
- buf = firstbuf;
+ buf_T *buf = firstbuf;
while (buf->b_next != NULL && buf->b_fnum < lnum) {
buf = buf->b_next;
}
@@ -1085,7 +1033,7 @@ static int current_win_nr(const win_T *win)
int nr = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- ++nr;
+ nr++;
if (wp == win) {
break;
}
@@ -1098,7 +1046,7 @@ static int current_tab_nr(tabpage_T *tab)
int nr = 0;
FOR_ALL_TABS(tp) {
- ++nr;
+ nr++;
if (tp == tab) {
break;
}
@@ -1385,12 +1333,11 @@ static int parse_count(exarg_T *eap, char **errormsg, bool validate)
// Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
// count, it's a buffer name.
char *p;
- long n;
if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg)
&& (!(eap->argt & EX_BUFNAME) || *(p = skipdigits(eap->arg + 1)) == NUL
|| ascii_iswhite(*p))) {
- n = getdigits_long(&eap->arg, false, -1);
+ long n = getdigits_long(&eap->arg, false, -1);
eap->arg = skipwhite(eap->arg);
if (n <= 0 && (eap->argt & EX_ZEROR) == 0) {
if (errormsg != NULL) {
@@ -1423,56 +1370,59 @@ bool is_cmd_ni(cmdidx_T cmdidx)
/// @return Success or failure
bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg)
{
- char *cmd;
- char *p;
char *after_modifier = NULL;
+ bool retval = false;
+ // parsing the command modifiers may set ex_pressedreturn
+ const bool save_ex_pressedreturn = ex_pressedreturn;
+ // parsing the command range may require moving the cursor
+ const pos_T save_cursor = curwin->w_cursor;
+ // parsing the command range may set the last search pattern
+ save_last_search_pattern();
// Initialize cmdinfo
- memset(cmdinfo, 0, sizeof(*cmdinfo));
+ CLEAR_POINTER(cmdinfo);
// Initialize eap
- memset(eap, 0, sizeof(*eap));
- eap->line1 = 1;
- eap->line2 = 1;
- eap->cmd = cmdline;
- eap->cmdlinep = &cmdline;
- eap->getline = NULL;
- eap->cookie = NULL;
+ *eap = (exarg_T){
+ .line1 = 1,
+ .line2 = 1,
+ .cmd = cmdline,
+ .cmdlinep = &cmdline,
+ .getline = NULL,
+ .cookie = NULL,
+ };
- const bool save_ex_pressedreturn = ex_pressedreturn;
// Parse command modifiers
if (parse_command_modifiers(eap, errormsg, &cmdinfo->cmdmod, false) == FAIL) {
- ex_pressedreturn = save_ex_pressedreturn;
- goto err;
+ goto end;
}
- ex_pressedreturn = save_ex_pressedreturn;
after_modifier = eap->cmd;
// Save location after command modifiers
- cmd = eap->cmd;
+ char *cmd = eap->cmd;
// Skip ranges to find command name since we need the command to know what kind of range it uses
eap->cmd = skip_range(eap->cmd, NULL);
if (*eap->cmd == '*') {
eap->cmd = skipwhite(eap->cmd + 1);
}
- p = find_ex_command(eap, NULL);
+ char *p = find_ex_command(eap, NULL);
if (p == NULL) {
*errormsg = _(e_ambiguous_use_of_user_defined_command);
- goto err;
+ goto end;
}
// Set command address type and parse command range
set_cmd_addr_type(eap, p);
eap->cmd = cmd;
- if (parse_cmd_address(eap, errormsg, false) == FAIL) {
- goto err;
+ if (parse_cmd_address(eap, errormsg, true) == FAIL) {
+ goto end;
}
// Skip colon and whitespace
eap->cmd = skip_colon_white(eap->cmd, true);
// Fail if command is a comment or if command doesn't exist
if (*eap->cmd == NUL || *eap->cmd == '"') {
- goto err;
+ goto end;
}
// Fail if command is invalid
if (eap->cmdidx == CMD_SIZE) {
@@ -1481,7 +1431,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
char *cmdname = after_modifier ? after_modifier : cmdline;
append_command(cmdname);
*errormsg = (char *)IObuff;
- goto err;
+ goto end;
}
// Correctly set 'forceit' for commands
@@ -1520,12 +1470,12 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
// Fail if command doesn't support bang but is used with a bang
if (!(eap->argt & EX_BANG) && eap->forceit) {
*errormsg = _(e_nobang);
- goto err;
+ goto end;
}
// Fail if command doesn't support a range but it is given a range
if (!(eap->argt & EX_RANGE) && eap->addr_count > 0) {
*errormsg = _(e_norange);
- goto err;
+ goto end;
}
// Set default range for command if required
if ((eap->argt & EX_DFLALL) && eap->addr_count == 0) {
@@ -1535,7 +1485,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
// Parse register and count
parse_register(eap);
if (parse_count(eap, errormsg, false) == FAIL) {
- goto err;
+ goto end;
}
// Remove leading whitespace and colon from next command
@@ -1551,10 +1501,15 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
cmdinfo->magic.bar = true;
}
- return true;
-err:
- undo_cmdmod(&cmdinfo->cmdmod);
- return false;
+ retval = true;
+end:
+ if (!retval) {
+ undo_cmdmod(&cmdinfo->cmdmod);
+ }
+ ex_pressedreturn = save_ex_pressedreturn;
+ curwin->w_cursor = save_cursor;
+ restore_last_search_pattern();
+ return retval;
}
static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview)
@@ -1719,12 +1674,12 @@ end:
static void profile_cmd(const exarg_T *eap, cstack_T *cstack, LineGetter fgetline, void *cookie)
{
- // Count this line for profiling if skip is TRUE.
+ // Count this line for profiling if skip is true.
if (do_profiling == PROF_YES
&& (!eap->skip || cstack->cs_idx == 0
|| (cstack->cs_idx > 0
&& (cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))) {
- int skip = did_emsg || got_int || current_exception;
+ bool skip = did_emsg || got_int || did_throw;
if (eap->cmdidx == CMD_catch) {
skip = !skip && !(cstack->cs_idx >= 0
@@ -1855,7 +1810,7 @@ static bool skip_cmd(const exarg_T *eap)
/// Execute one Ex command.
///
-/// If 'sourcing' is TRUE, the command will be included in the error message.
+/// If 'sourcing' is true, the command will be included in the error message.
///
/// 1. skip comment lines and leading space
/// 2. handle command modifiers
@@ -1877,10 +1832,10 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
const int save_reg_executing = reg_executing;
const bool save_pending_end_reg_executing = pending_end_reg_executing;
- exarg_T ea;
- memset(&ea, 0, sizeof(ea));
- ea.line1 = 1;
- ea.line2 = 1;
+ exarg_T ea = {
+ .line1 = 1,
+ .line2 = 1,
+ };
ex_nesting_level++;
// When the last file has not been edited :q has to be typed twice.
@@ -1890,7 +1845,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
// avoid that an autocommand, e.g. QuitPre, does this
&& !getline_equal(fgetline, cookie,
getnextac)) {
- --quitmore;
+ quitmore--;
}
// Reset browse, confirm, etc.. They are restored when returning, for
@@ -1921,7 +1876,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
ea.skip = (did_emsg
|| got_int
- || current_exception
+ || did_throw
|| (cstack->cs_idx >= 0
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
@@ -1941,7 +1896,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
// used, throw an interrupt exception and skip the next command.
dbg_check_breakpoint(&ea);
if (!ea.skip && got_int) {
- ea.skip = TRUE;
+ ea.skip = true;
(void)do_intthrow(cstack);
}
@@ -2015,7 +1970,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
&& has_event(EVENT_CMDUNDEFINED)) {
p = ea.cmd;
while (ASCII_ISALNUM(*p)) {
- ++p;
+ p++;
}
p = xstrnsave(ea.cmd, (size_t)(p - ea.cmd));
int ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, true, NULL);
@@ -2194,16 +2149,16 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
ea.arg = skipwhite(ea.arg + 1);
ea.append = true;
} else if (*ea.arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
- ++ea.arg;
- ea.usefilter = TRUE;
+ ea.arg++;
+ ea.usefilter = true;
}
} else if (ea.cmdidx == CMD_read) {
if (ea.forceit) {
- ea.usefilter = TRUE; // :r! filter if ea.forceit
- ea.forceit = FALSE;
+ ea.usefilter = true; // :r! filter if ea.forceit
+ ea.forceit = false;
} else if (*ea.arg == '!') { // :r !filter
- ++ea.arg;
- ea.usefilter = TRUE;
+ ea.arg++;
+ ea.usefilter = true;
}
} else if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
ea.amount = 1;
@@ -2294,10 +2249,10 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
do_throw(cstack);
} else if (check_cstack) {
if (source_finished(fgetline, cookie)) {
- do_finish(&ea, TRUE);
+ do_finish(&ea, true);
} else if (getline_equal(fgetline, cookie, get_func_line)
&& current_func_returned()) {
- do_return(&ea, TRUE, FALSE, NULL);
+ do_return(&ea, true, false, NULL);
}
}
need_rethrow = check_cstack = false;
@@ -2332,7 +2287,7 @@ doend:
ea.nextcmd = NULL;
}
- --ex_nesting_level;
+ ex_nesting_level--;
return ea.nextcmd;
}
@@ -2368,8 +2323,6 @@ char *ex_errmsg(const char *const msg, const char *const arg)
/// @return FAIL when the command is not to be executed.
int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool skip_only)
{
- char *p;
-
CLEAR_POINTER(cmod);
// Repeat until no more command modifiers are found.
@@ -2401,7 +2354,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool
return FAIL;
}
- p = skip_range(eap->cmd, NULL);
+ char *p = skip_range(eap->cmd, NULL);
switch (*p) {
// When adding an entry, also modify cmd_exists().
case 'a':
@@ -2631,7 +2584,7 @@ static void apply_cmdmod(cmdmod_T *cmod)
if ((cmod->cmod_flags & CMOD_NOAUTOCMD) && cmod->cmod_save_ei == NULL) {
// Set 'eventignore' to "all".
// First save the existing option value for restoring it later.
- cmod->cmod_save_ei = (char *)vim_strsave(p_ei);
+ cmod->cmod_save_ei = xstrdup(p_ei);
set_string_option_direct("ei", -1, "all", OPT_FREE, SID_NONE);
}
}
@@ -2653,7 +2606,7 @@ void undo_cmdmod(cmdmod_T *cmod)
if (cmod->cmod_save_ei != NULL) {
// Restore 'eventignore' to the value before ":noautocmd".
set_string_option_direct("ei", -1, cmod->cmod_save_ei, OPT_FREE, SID_NONE);
- free_string_option((char_u *)cmod->cmod_save_ei);
+ free_string_option(cmod->cmod_save_ei);
cmod->cmod_save_ei = NULL;
}
@@ -2840,12 +2793,12 @@ theend:
}
/// Check for an Ex command with optional tail.
-/// If there is a match advance "pp" to the argument and return TRUE.
+/// If there is a match advance "pp" to the argument and return true.
///
/// @param pp start of command
/// @param cmd name of command
/// @param len required length
-int checkforcmd(char **pp, char *cmd, int len)
+bool checkforcmd(char **pp, char *cmd, int len)
{
int i;
@@ -2858,7 +2811,7 @@ int checkforcmd(char **pp, char *cmd, int len)
*pp = skipwhite(*pp + i);
return true;
}
- return FALSE;
+ return false;
}
/// Append "cmd" to the error message in IObuff.
@@ -2886,7 +2839,7 @@ static void append_command(char *cmd)
} else if ((char_u *)d - IObuff + utfc_ptr2len(s) + 1 >= IOSIZE) {
break;
} else {
- mb_copy_char((const char_u **)&s, (char_u **)&d);
+ mb_copy_char((const char **)&s, &d);
}
}
*d = NUL;
@@ -2895,29 +2848,23 @@ static void append_command(char *cmd)
/// Find an Ex command by its name, either built-in or user.
/// Start of the name can be found at eap->cmd.
/// Sets eap->cmdidx and returns a pointer to char after the command name.
-/// "full" is set to TRUE if the whole command name matched.
+/// "full" is set to true if the whole command name matched.
///
/// @return NULL for an ambiguous user command.
char *find_ex_command(exarg_T *eap, int *full)
FUNC_ATTR_NONNULL_ARG(1)
{
- int len;
- char *p;
- int i;
-
- /*
- * Isolate the command and search for it in the command table.
- * Exceptions:
- * - the 'k' command can directly be followed by any character.
- * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
- * but :sre[wind] is another command, as are :scr[iptnames],
- * :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
- * - the "d" command can directly be followed by 'l' or 'p' flag.
- */
- p = eap->cmd;
+ // Isolate the command and search for it in the command table.
+ // Exceptions:
+ // - the 'k' command can directly be followed by any character.
+ // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
+ // but :sre[wind] is another command, as are :scr[iptnames],
+ // :scs[cope], :sim[alt], :sig[ns] and :sil[ent].
+ // - the "d" command can directly be followed by 'l' or 'p' flag.
+ char *p = eap->cmd;
if (*p == 'k') {
eap->cmdidx = CMD_k;
- ++p;
+ p++;
} else if (p[0] == 's'
&& ((p[1] == 'c'
&& (p[2] == NUL
@@ -2929,15 +2876,15 @@ char *find_ex_command(exarg_T *eap, int *full)
|| p[1] == 'I'
|| (p[1] == 'r' && p[2] != 'e'))) {
eap->cmdidx = CMD_substitute;
- ++p;
+ p++;
} else {
while (ASCII_ISALPHA(*p)) {
- ++p;
+ p++;
}
// for python 3.x support ":py3", ":python3", ":py3file", etc.
if (eap->cmd[0] == 'p' && eap->cmd[1] == 'y') {
while (ASCII_ISALNUM(*p)) {
- ++p;
+ p++;
}
}
@@ -2945,17 +2892,18 @@ char *find_ex_command(exarg_T *eap, int *full)
if (p == eap->cmd && vim_strchr("@!=><&~#", *p) != NULL) {
p++;
}
- len = (int)(p - eap->cmd);
+ int len = (int)(p - eap->cmd);
if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) {
// Check for ":dl", ":dell", etc. to ":deletel": that's
// :delete with the 'l' flag. Same for 'p'.
+ int i;
for (i = 0; i < len; i++) {
if (eap->cmd[i] != ("delete")[i]) {
break;
}
}
if (i == len - 1) {
- --len;
+ len--;
if (p[-1] == 'l') {
eap->flags |= EXFLAG_LIST;
} else {
@@ -2992,7 +2940,7 @@ char *find_ex_command(exarg_T *eap, int *full)
(size_t)len) == 0) {
if (full != NULL
&& cmdnames[(int)eap->cmdidx].cmd_name[len] == NUL) {
- *full = TRUE;
+ *full = true;
}
break;
}
@@ -3003,7 +2951,7 @@ char *find_ex_command(exarg_T *eap, int *full)
&& *eap->cmd >= 'A' && *eap->cmd <= 'Z') {
// User defined commands may contain digits.
while (ASCII_ISALNUM(*p)) {
- ++p;
+ p++;
}
p = find_ucmd(eap, p, full, NULL, NULL);
}
@@ -3075,9 +3023,6 @@ int modifier_len(char *cmd)
/// 3 if there is an ambiguous match.
int cmd_exists(const char *const name)
{
- exarg_T ea;
- char *p;
-
// Check command modifiers.
for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
int j;
@@ -3093,11 +3038,12 @@ int cmd_exists(const char *const name)
// Check built-in commands and user defined commands.
// For ":2match" and ":3match" we need to skip the number.
+ exarg_T ea;
ea.cmd = (char *)((*name == '2' || *name == '3') ? name + 1 : name);
ea.cmdidx = (cmdidx_T)0;
ea.flags = 0;
int full = false;
- p = find_ex_command(&ea, &full);
+ char *p = find_ex_command(&ea, &full);
if (p == NULL) {
return 3;
}
@@ -3111,9 +3057,8 @@ int cmd_exists(const char *const name)
}
/// "fullcommand" function
-void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- exarg_T ea;
char *name = argvars[0].vval.v_string;
rettv->v_type = VAR_STRING;
@@ -3127,6 +3072,7 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
name = skip_range(name, NULL);
+ exarg_T ea;
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
ea.cmdidx = (cmdidx_T)0;
ea.flags = 0;
@@ -3141,843 +3087,22 @@ void f_fullcommand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
: (char_u *)cmdnames[ea.cmdidx].cmd_name);
}
-/// This is all pretty much copied from do_one_cmd(), with all the extra stuff
-/// we don't need/want deleted. Maybe this could be done better if we didn't
-/// repeat all this stuff. The only problem is that they may not stay
-/// perfectly compatible with each other, but then the command line syntax
-/// probably won't change that much -- webb.
-///
-/// @param buff buffer for command string
-const char *set_one_cmd_context(expand_T *xp, const char *buff)
+cmdidx_T excmd_get_cmdidx(const char *cmd, size_t len)
{
- size_t len = 0;
- exarg_T ea;
- int context = EXPAND_NOTHING;
- bool forceit = false;
- bool usefilter = false; // Filter instead of file name.
-
- ExpandInit(xp);
- xp->xp_pattern = (char *)buff;
- xp->xp_line = (char *)buff;
- xp->xp_context = EXPAND_COMMANDS; // Default until we get past command
- ea.argt = 0;
-
- // 2. skip comment lines and leading space, colons or bars
- const char *cmd;
- for (cmd = buff; vim_strchr(" \t:|", *cmd) != NULL; cmd++) {}
- xp->xp_pattern = (char *)cmd;
-
- if (*cmd == NUL) {
- return NULL;
- }
- if (*cmd == '"') { // ignore comment lines
- xp->xp_context = EXPAND_NOTHING;
- return NULL;
- }
-
- /*
- * 3. parse a range specifier of the form: addr [,addr] [;addr] ..
- */
- cmd = (const char *)skip_range(cmd, &xp->xp_context);
-
- /*
- * 4. parse command
- */
- xp->xp_pattern = (char *)cmd;
- if (*cmd == NUL) {
- return NULL;
- }
- if (*cmd == '"') {
- xp->xp_context = EXPAND_NOTHING;
- return NULL;
- }
-
- if (*cmd == '|' || *cmd == '\n') {
- return cmd + 1; // There's another command
- }
- /*
- * Isolate the command and search for it in the command table.
- * Exceptions:
- * - the 'k' command can directly be followed by any character, but
- * do accept "keepmarks", "keepalt" and "keepjumps".
- * - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r'
- */
- const char *p;
- if (*cmd == 'k' && cmd[1] != 'e') {
- ea.cmdidx = CMD_k;
- p = cmd + 1;
- } else {
- p = cmd;
- while (ASCII_ISALPHA(*p) || *p == '*') { // Allow * wild card
- p++;
- }
- // a user command may contain digits
- if (ASCII_ISUPPER(cmd[0])) {
- while (ASCII_ISALNUM(*p) || *p == '*') {
- p++;
- }
- }
- // for python 3.x: ":py3*" commands completion
- if (cmd[0] == 'p' && cmd[1] == 'y' && p == cmd + 2 && *p == '3') {
- p++;
- while (ASCII_ISALPHA(*p) || *p == '*') {
- p++;
- }
- }
- // check for non-alpha command
- if (p == cmd && vim_strchr("@*!=><&~#", *p) != NULL) {
- p++;
- }
- len = (size_t)(p - cmd);
-
- if (len == 0) {
- xp->xp_context = EXPAND_UNSUCCESSFUL;
- return NULL;
- }
- for (ea.cmdidx = (cmdidx_T)0; (int)ea.cmdidx < CMD_SIZE;
- ea.cmdidx = (cmdidx_T)((int)ea.cmdidx + 1)) {
- if (STRNCMP(cmdnames[(int)ea.cmdidx].cmd_name, cmd, len) == 0) {
- break;
- }
- }
-
- if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
- while (ASCII_ISALNUM(*p) || *p == '*') { // Allow * wild card
- p++;
- }
- }
- }
-
- //
- // If the cursor is touching the command, and it ends in an alphanumeric
- // character, complete the command name.
- //
- if (*p == NUL && ASCII_ISALNUM(p[-1])) {
- return NULL;
- }
-
- if (ea.cmdidx == CMD_SIZE) {
- if (*cmd == 's' && vim_strchr("cgriI", cmd[1]) != NULL) {
- ea.cmdidx = CMD_substitute;
- p = cmd + 1;
- } else if (cmd[0] >= 'A' && cmd[0] <= 'Z') {
- ea.cmd = (char *)cmd;
- p = (const char *)find_ucmd(&ea, (char *)p, NULL, xp, &context);
- if (p == NULL) {
- ea.cmdidx = CMD_SIZE; // Ambiguous user command.
- }
- }
- }
- if (ea.cmdidx == CMD_SIZE) {
- // Not still touching the command and it was an illegal one
- xp->xp_context = EXPAND_UNSUCCESSFUL;
- return NULL;
- }
-
- xp->xp_context = EXPAND_NOTHING; // Default now that we're past command
-
- if (*p == '!') { // forced commands
- forceit = true;
- p++;
- }
-
- /*
- * 5. parse arguments
- */
- if (!IS_USER_CMDIDX(ea.cmdidx)) {
- ea.argt = cmdnames[(int)ea.cmdidx].cmd_argt;
- }
-
- const char *arg = (const char *)skipwhite(p);
-
- // Skip over ++argopt argument
- if ((ea.argt & EX_ARGOPT) && *arg != NUL && strncmp(arg, "++", 2) == 0) {
- p = arg;
- while (*p && !ascii_isspace(*p)) {
- MB_PTR_ADV(p);
- }
- arg = (const char *)skipwhite(p);
- }
-
- if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) {
- if (*arg == '>') { // Append.
- if (*++arg == '>') {
- arg++;
- }
- arg = (const char *)skipwhite(arg);
- } else if (*arg == '!' && ea.cmdidx == CMD_write) { // :w !filter
- arg++;
- usefilter = true;
- }
- }
+ cmdidx_T idx;
- if (ea.cmdidx == CMD_read) {
- usefilter = forceit; // :r! filter if forced
- if (*arg == '!') { // :r !filter
- arg++;
- usefilter = true;
- }
- }
-
- if (ea.cmdidx == CMD_lshift || ea.cmdidx == CMD_rshift) {
- while (*arg == *cmd) { // allow any number of '>' or '<'
- arg++;
- }
- arg = (const char *)skipwhite(arg);
- }
-
- // Does command allow "+command"?
- if ((ea.argt & EX_CMDARG) && !usefilter && *arg == '+') {
- // Check if we're in the +command
- p = arg + 1;
- arg = (const char *)skip_cmd_arg((char *)arg, false);
-
- // Still touching the command after '+'?
- if (*arg == NUL) {
- return p;
- }
-
- // Skip space(s) after +command to get to the real argument.
- arg = (const char *)skipwhite(arg);
- }
-
- /*
- * Check for '|' to separate commands and '"' to start comments.
- * Don't do this for ":read !cmd" and ":write !cmd".
- */
- if ((ea.argt & EX_TRLBAR) && !usefilter) {
- p = arg;
- // ":redir @" is not the start of a comment
- if (ea.cmdidx == CMD_redir && p[0] == '@' && p[1] == '"') {
- p += 2;
- }
- while (*p) {
- if (*p == Ctrl_V) {
- if (p[1] != NUL) {
- p++;
- }
- } else if ((*p == '"' && !(ea.argt & EX_NOTRLCOM))
- || *p == '|'
- || *p == '\n') {
- if (*(p - 1) != '\\') {
- if (*p == '|' || *p == '\n') {
- return p + 1;
- }
- return NULL; // It's a comment
- }
- }
- MB_PTR_ADV(p);
- }
- }
-
- if (!(ea.argt & EX_EXTRA) && *arg != NUL && strchr("|\"", *arg) == NULL) {
- // no arguments allowed but there is something
- return NULL;
- }
-
- // Find start of last argument (argument just before cursor):
- p = buff;
- xp->xp_pattern = (char *)p;
- len = strlen(buff);
- while (*p && p < buff + len) {
- if (*p == ' ' || *p == TAB) {
- // Argument starts after a space.
- xp->xp_pattern = (char *)++p;
- } else {
- if (*p == '\\' && *(p + 1) != NUL) {
- p++; // skip over escaped character
- }
- MB_PTR_ADV(p);
- }
- }
-
- if (ea.argt & EX_XFILE) {
- int c;
- int in_quote = false;
- const char *bow = NULL; // Beginning of word.
-
- /*
- * Allow spaces within back-quotes to count as part of the argument
- * being expanded.
- */
- xp->xp_pattern = skipwhite(arg);
- p = (const char *)xp->xp_pattern;
- while (*p != NUL) {
- c = utf_ptr2char(p);
- if (c == '\\' && p[1] != NUL) {
- p++;
- } else if (c == '`') {
- if (!in_quote) {
- xp->xp_pattern = (char *)p;
- bow = p + 1;
- }
- in_quote = !in_quote;
- }
- /* An argument can contain just about everything, except
- * characters that end the command and white space. */
- else if (c == '|'
- || c == '\n'
- || c == '"'
- || ascii_iswhite(c)) {
- len = 0; // avoid getting stuck when space is in 'isfname'
- while (*p != NUL) {
- c = utf_ptr2char(p);
- if (c == '`' || vim_isfilec_or_wc(c)) {
- break;
- }
- len = (size_t)utfc_ptr2len(p);
- MB_PTR_ADV(p);
- }
- if (in_quote) {
- bow = p;
- } else {
- xp->xp_pattern = (char *)p;
- }
- p -= len;
- }
- MB_PTR_ADV(p);
- }
-
- /*
- * If we are still inside the quotes, and we passed a space, just
- * expand from there.
- */
- if (bow != NULL && in_quote) {
- xp->xp_pattern = (char *)bow;
- }
- xp->xp_context = EXPAND_FILES;
-
- // For a shell command more chars need to be escaped.
- if (usefilter || ea.cmdidx == CMD_bang || ea.cmdidx == CMD_terminal) {
-#ifndef BACKSLASH_IN_FILENAME
- xp->xp_shell = TRUE;
-#endif
- // When still after the command name expand executables.
- if (xp->xp_pattern == skipwhite(arg)) {
- xp->xp_context = EXPAND_SHELLCMD;
- }
- }
-
- // Check for environment variable.
- if (*xp->xp_pattern == '$') {
- for (p = (const char *)xp->xp_pattern + 1; *p != NUL; p++) {
- if (!vim_isIDc((uint8_t)(*p))) {
- break;
- }
- }
- if (*p == NUL) {
- xp->xp_context = EXPAND_ENV_VARS;
- xp->xp_pattern++;
- // Avoid that the assignment uses EXPAND_FILES again.
- if (context != EXPAND_USER_DEFINED && context != EXPAND_USER_LIST) {
- context = EXPAND_ENV_VARS;
- }
- }
- }
- // Check for user names.
- if (*xp->xp_pattern == '~') {
- for (p = (const char *)xp->xp_pattern + 1; *p != NUL && *p != '/'; p++) {}
- // Complete ~user only if it partially matches a user name.
- // A full match ~user<Tab> will be replaced by user's home
- // directory i.e. something like ~user<Tab> -> /home/user/
- if (*p == NUL && p > (const char *)xp->xp_pattern + 1
- && match_user((char_u *)xp->xp_pattern + 1) >= 1) {
- xp->xp_context = EXPAND_USER;
- ++xp->xp_pattern;
- }
- }
- }
-
- /*
- * 6. switch on command name
- */
- switch (ea.cmdidx) {
- case CMD_find:
- case CMD_sfind:
- case CMD_tabfind:
- if (xp->xp_context == EXPAND_FILES) {
- xp->xp_context = EXPAND_FILES_IN_PATH;
- }
- break;
- case CMD_cd:
- case CMD_chdir:
- case CMD_lcd:
- case CMD_lchdir:
- case CMD_tcd:
- case CMD_tchdir:
- if (xp->xp_context == EXPAND_FILES) {
- xp->xp_context = EXPAND_DIRECTORIES;
- }
- break;
- case CMD_help:
- xp->xp_context = EXPAND_HELP;
- xp->xp_pattern = (char *)arg;
- break;
-
- /* Command modifiers: return the argument.
- * Also for commands with an argument that is a command. */
- case CMD_aboveleft:
- case CMD_argdo:
- case CMD_belowright:
- case CMD_botright:
- case CMD_browse:
- case CMD_bufdo:
- case CMD_cdo:
- case CMD_cfdo:
- case CMD_confirm:
- case CMD_debug:
- case CMD_folddoclosed:
- case CMD_folddoopen:
- case CMD_hide:
- case CMD_keepalt:
- case CMD_keepjumps:
- case CMD_keepmarks:
- case CMD_keeppatterns:
- case CMD_ldo:
- case CMD_leftabove:
- case CMD_lfdo:
- case CMD_lockmarks:
- case CMD_noautocmd:
- case CMD_noswapfile:
- case CMD_rightbelow:
- case CMD_sandbox:
- case CMD_silent:
- case CMD_tab:
- case CMD_tabdo:
- case CMD_topleft:
- case CMD_verbose:
- case CMD_vertical:
- case CMD_windo:
- return arg;
-
- case CMD_filter:
- if (*arg != NUL) {
- arg = (const char *)skip_vimgrep_pat((char *)arg, NULL, NULL);
- }
- if (arg == NULL || *arg == NUL) {
- xp->xp_context = EXPAND_NOTHING;
- return NULL;
- }
- return (const char *)skipwhite(arg);
-
- case CMD_match:
- if (*arg == NUL || !ends_excmd(*arg)) {
- // also complete "None"
- set_context_in_echohl_cmd(xp, arg);
- arg = (const char *)skipwhite((char *)skiptowhite((const char_u *)arg));
- if (*arg != NUL) {
- xp->xp_context = EXPAND_NOTHING;
- arg = (const char *)skip_regexp((char_u *)arg + 1, (uint8_t)(*arg),
- p_magic, NULL);
- }
- }
- return (const char *)find_nextcmd((char_u *)arg);
-
- /*
- * All completion for the +cmdline_compl feature goes here.
- */
-
- case CMD_command:
- return set_context_in_user_cmd(xp, arg);
-
- case CMD_delcommand:
- xp->xp_context = EXPAND_USER_COMMANDS;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_global:
- case CMD_vglobal: {
- const int delim = (uint8_t)(*arg); // Get the delimiter.
- if (delim) {
- arg++; // Skip delimiter if there is one.
- }
-
- while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
- if (arg[0] == '\\' && arg[1] != NUL) {
- arg++;
- }
- arg++;
- }
- if (arg[0] != NUL) {
- return arg + 1;
- }
- break;
- }
- case CMD_and:
- case CMD_substitute: {
- const int delim = (uint8_t)(*arg);
- if (delim) {
- // Skip "from" part.
- arg++;
- arg = (const char *)skip_regexp((char_u *)arg, delim, p_magic, NULL);
- }
- // Skip "to" part.
- while (arg[0] != NUL && (uint8_t)arg[0] != delim) {
- if (arg[0] == '\\' && arg[1] != NUL) {
- arg++;
- }
- arg++;
- }
- if (arg[0] != NUL) { // Skip delimiter.
- arg++;
- }
- while (arg[0] && strchr("|\"#", arg[0]) == NULL) {
- arg++;
- }
- if (arg[0] != NUL) {
- return arg;
+ for (idx = (cmdidx_T)0; (int)idx < CMD_SIZE; idx = (cmdidx_T)((int)idx + 1)) {
+ if (strncmp(cmdnames[(int)idx].cmd_name, cmd, len) == 0) {
+ break;
}
- break;
}
- case CMD_isearch:
- case CMD_dsearch:
- case CMD_ilist:
- case CMD_dlist:
- case CMD_ijump:
- case CMD_psearch:
- case CMD_djump:
- case CMD_isplit:
- case CMD_dsplit:
- // Skip count.
- arg = (const char *)skipwhite(skipdigits(arg));
- if (*arg == '/') { // Match regexp, not just whole words.
- for (++arg; *arg && *arg != '/'; arg++) {
- if (*arg == '\\' && arg[1] != NUL) {
- arg++;
- }
- }
- if (*arg) {
- arg = (const char *)skipwhite(arg + 1);
-
- // Check for trailing illegal characters.
- if (*arg && strchr("|\"\n", *arg) == NULL) {
- xp->xp_context = EXPAND_NOTHING;
- } else {
- return arg;
- }
- }
- }
- break;
- case CMD_autocmd:
- return (const char *)set_context_in_autocmd(xp, (char *)arg, false);
-
- case CMD_doautocmd:
- case CMD_doautoall:
- return (const char *)set_context_in_autocmd(xp, (char *)arg, true);
- case CMD_set:
- set_context_in_set_cmd(xp, (char_u *)arg, 0);
- break;
- case CMD_setglobal:
- set_context_in_set_cmd(xp, (char_u *)arg, OPT_GLOBAL);
- break;
- case CMD_setlocal:
- set_context_in_set_cmd(xp, (char_u *)arg, OPT_LOCAL);
- break;
- case CMD_tag:
- case CMD_stag:
- case CMD_ptag:
- case CMD_ltag:
- case CMD_tselect:
- case CMD_stselect:
- case CMD_ptselect:
- case CMD_tjump:
- case CMD_stjump:
- case CMD_ptjump:
- if (wop_flags & WOP_TAGFILE) {
- xp->xp_context = EXPAND_TAGS_LISTFILES;
- } else {
- xp->xp_context = EXPAND_TAGS;
- }
- xp->xp_pattern = (char *)arg;
- break;
- case CMD_augroup:
- xp->xp_context = EXPAND_AUGROUP;
- xp->xp_pattern = (char *)arg;
- break;
- case CMD_syntax:
- set_context_in_syntax_cmd(xp, arg);
- break;
- case CMD_const:
- case CMD_let:
- case CMD_if:
- case CMD_elseif:
- case CMD_while:
- case CMD_for:
- case CMD_echo:
- case CMD_echon:
- case CMD_execute:
- case CMD_echomsg:
- case CMD_echoerr:
- case CMD_call:
- case CMD_return:
- case CMD_cexpr:
- case CMD_caddexpr:
- case CMD_cgetexpr:
- case CMD_lexpr:
- case CMD_laddexpr:
- case CMD_lgetexpr:
- set_context_for_expression(xp, (char *)arg, ea.cmdidx);
- break;
-
- case CMD_unlet:
- while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
- arg = (const char *)xp->xp_pattern + 1;
- }
-
- xp->xp_context = EXPAND_USER_VARS;
- xp->xp_pattern = (char *)arg;
-
- if (*xp->xp_pattern == '$') {
- xp->xp_context = EXPAND_ENV_VARS;
- xp->xp_pattern++;
- }
- break;
-
- case CMD_function:
- case CMD_delfunction:
- xp->xp_context = EXPAND_USER_FUNC;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_echohl:
- set_context_in_echohl_cmd(xp, arg);
- break;
- case CMD_highlight:
- set_context_in_highlight_cmd(xp, arg);
- break;
- case CMD_cscope:
- case CMD_lcscope:
- case CMD_scscope:
- set_context_in_cscope_cmd(xp, arg, ea.cmdidx);
- break;
- case CMD_sign:
- set_context_in_sign_cmd(xp, (char_u *)arg);
- break;
- case CMD_bdelete:
- case CMD_bwipeout:
- case CMD_bunload:
- while ((xp->xp_pattern = strchr(arg, ' ')) != NULL) {
- arg = (const char *)xp->xp_pattern + 1;
- }
- FALLTHROUGH;
- case CMD_buffer:
- case CMD_sbuffer:
- case CMD_checktime:
- xp->xp_context = EXPAND_BUFFERS;
- xp->xp_pattern = (char *)arg;
- break;
- case CMD_diffget:
- case CMD_diffput:
- // If current buffer is in diff mode, complete buffer names
- // which are in diff mode, and different than current buffer.
- xp->xp_context = EXPAND_DIFF_BUFFERS;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_USER:
- case CMD_USER_BUF:
- if (context != EXPAND_NOTHING) {
- // EX_XFILE: file names are handled above.
- if (!(ea.argt & EX_XFILE)) {
- if (context == EXPAND_MENUS) {
- return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
- } else if (context == EXPAND_COMMANDS) {
- return arg;
- } else if (context == EXPAND_MAPPINGS) {
- return (const char *)set_context_in_map_cmd(xp, (char_u *)"map", (char_u *)arg, forceit,
- false, false,
- CMD_map);
- }
- // Find start of last argument.
- p = arg;
- while (*p) {
- if (*p == ' ') {
- // argument starts after a space
- arg = p + 1;
- } else if (*p == '\\' && *(p + 1) != NUL) {
- p++; // skip over escaped character
- }
- MB_PTR_ADV(p);
- }
- xp->xp_pattern = (char *)arg;
- }
- xp->xp_context = context;
- }
- break;
- case CMD_map:
- case CMD_noremap:
- case CMD_nmap:
- case CMD_nnoremap:
- case CMD_vmap:
- case CMD_vnoremap:
- case CMD_omap:
- case CMD_onoremap:
- case CMD_imap:
- case CMD_inoremap:
- case CMD_cmap:
- case CMD_cnoremap:
- case CMD_lmap:
- case CMD_lnoremap:
- case CMD_smap:
- case CMD_snoremap:
- case CMD_xmap:
- case CMD_xnoremap:
- return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, false,
- false, ea.cmdidx);
- case CMD_unmap:
- case CMD_nunmap:
- case CMD_vunmap:
- case CMD_ounmap:
- case CMD_iunmap:
- case CMD_cunmap:
- case CMD_lunmap:
- case CMD_sunmap:
- case CMD_xunmap:
- return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, false,
- true, ea.cmdidx);
- case CMD_mapclear:
- case CMD_nmapclear:
- case CMD_vmapclear:
- case CMD_omapclear:
- case CMD_imapclear:
- case CMD_cmapclear:
- case CMD_lmapclear:
- case CMD_smapclear:
- case CMD_xmapclear:
- xp->xp_context = EXPAND_MAPCLEAR;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_abbreviate:
- case CMD_noreabbrev:
- case CMD_cabbrev:
- case CMD_cnoreabbrev:
- case CMD_iabbrev:
- case CMD_inoreabbrev:
- return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, true,
- false, ea.cmdidx);
- case CMD_unabbreviate:
- case CMD_cunabbrev:
- case CMD_iunabbrev:
- return (const char *)set_context_in_map_cmd(xp, (char_u *)cmd, (char_u *)arg, forceit, true,
- true, ea.cmdidx);
- case CMD_menu:
- case CMD_noremenu:
- case CMD_unmenu:
- case CMD_amenu:
- case CMD_anoremenu:
- case CMD_aunmenu:
- case CMD_nmenu:
- case CMD_nnoremenu:
- case CMD_nunmenu:
- case CMD_vmenu:
- case CMD_vnoremenu:
- case CMD_vunmenu:
- case CMD_omenu:
- case CMD_onoremenu:
- case CMD_ounmenu:
- case CMD_imenu:
- case CMD_inoremenu:
- case CMD_iunmenu:
- case CMD_cmenu:
- case CMD_cnoremenu:
- case CMD_cunmenu:
- case CMD_tlmenu:
- case CMD_tlnoremenu:
- case CMD_tlunmenu:
- case CMD_tmenu:
- case CMD_tunmenu:
- case CMD_popup:
- case CMD_emenu:
- return (const char *)set_context_in_menu_cmd(xp, cmd, (char *)arg, forceit);
-
- case CMD_colorscheme:
- xp->xp_context = EXPAND_COLORS;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_compiler:
- xp->xp_context = EXPAND_COMPILER;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_ownsyntax:
- xp->xp_context = EXPAND_OWNSYNTAX;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_setfiletype:
- xp->xp_context = EXPAND_FILETYPE;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_packadd:
- xp->xp_context = EXPAND_PACKADD;
- xp->xp_pattern = (char *)arg;
- break;
-
-#ifdef HAVE_WORKING_LIBINTL
- case CMD_language:
- p = (const char *)skiptowhite((const char_u *)arg);
- if (*p == NUL) {
- xp->xp_context = EXPAND_LANGUAGE;
- xp->xp_pattern = (char *)arg;
- } else {
- if (strncmp(arg, "messages", (size_t)(p - arg)) == 0
- || strncmp(arg, "ctype", (size_t)(p - arg)) == 0
- || strncmp(arg, "time", (size_t)(p - arg)) == 0
- || strncmp(arg, "collate", (size_t)(p - arg)) == 0) {
- xp->xp_context = EXPAND_LOCALES;
- xp->xp_pattern = skipwhite(p);
- } else {
- xp->xp_context = EXPAND_NOTHING;
- }
- }
- break;
-#endif
- case CMD_profile:
- set_context_in_profile_cmd(xp, arg);
- break;
- case CMD_checkhealth:
- xp->xp_context = EXPAND_CHECKHEALTH;
- xp->xp_pattern = (char *)arg;
- break;
- case CMD_behave:
- xp->xp_context = EXPAND_BEHAVE;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_messages:
- xp->xp_context = EXPAND_MESSAGES;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_history:
- xp->xp_context = EXPAND_HISTORY;
- xp->xp_pattern = (char *)arg;
- break;
- case CMD_syntime:
- xp->xp_context = EXPAND_SYNTIME;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_argdelete:
- while ((xp->xp_pattern = vim_strchr(arg, ' ')) != NULL) {
- arg = (const char *)(xp->xp_pattern + 1);
- }
- xp->xp_context = EXPAND_ARGLIST;
- xp->xp_pattern = (char *)arg;
- break;
-
- case CMD_lua:
- xp->xp_context = EXPAND_LUA;
- break;
+ return idx;
+}
- default:
- break;
- }
- return NULL;
+uint32_t excmd_get_argt(cmdidx_T idx)
+{
+ return cmdnames[(int)idx].cmd_argt;
}
/// Skip a range specifier of the form: addr [,addr] [;addr] ..
@@ -3992,8 +3117,6 @@ const char *set_one_cmd_context(expand_T *xp, const char *buff)
/// @return the "cmd" pointer advanced to beyond the range.
char *skip_range(const char *cmd, int *ctx)
{
- unsigned delim;
-
while (vim_strchr(" \t0123456789.$%'/?-+,;\\", *cmd) != NULL) {
if (*cmd == '\\') {
if (cmd[1] == '?' || cmd[1] == '/' || cmd[1] == '&') {
@@ -4006,10 +3129,10 @@ char *skip_range(const char *cmd, int *ctx)
*ctx = EXPAND_NOTHING;
}
} else if (*cmd == '/' || *cmd == '?') {
- delim = (unsigned)(*cmd++);
+ unsigned delim = (unsigned)(*cmd++);
while (*cmd != NUL && *cmd != (char)delim) {
if (*cmd++ == '\\' && *cmd != NUL) {
- ++cmd;
+ cmd++;
}
}
if (*cmd == NUL && ctx != NULL) {
@@ -4055,17 +3178,15 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
int c;
int i;
linenr_T n;
- char *cmd;
pos_T pos;
- linenr_T lnum;
buf_T *buf;
- cmd = skipwhite(*ptr);
- lnum = MAXLNUM;
+ char *cmd = skipwhite(*ptr);
+ linenr_T lnum = MAXLNUM;
do {
switch (*cmd) {
case '.': // '.' - Cursor position
- ++cmd;
+ cmd++;
switch (addr_type) {
case ADDR_LINES:
case ADDR_OTHER:
@@ -4101,7 +3222,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
break;
case '$': // '$' - last line
- ++cmd;
+ cmd++;
switch (addr_type) {
case ADDR_LINES:
case ADDR_OTHER:
@@ -4192,9 +3313,9 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
goto error;
}
if (skip) { // skip "/pat/"
- cmd = (char *)skip_regexp((char_u *)cmd, c, p_magic, NULL);
+ cmd = skip_regexp(cmd, c, p_magic, NULL);
if (*cmd == c) {
- ++cmd;
+ cmd++;
}
} else {
int flags;
@@ -4234,7 +3355,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
break;
case '\\': // "\?", "\/" or "\&", repeat search
- ++cmd;
+ cmd++;
if (addr_type != ADDR_LINES) {
addr_error(addr_type);
cmd = NULL;
@@ -4265,7 +3386,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int
goto error;
}
}
- ++cmd;
+ cmd++;
break;
default:
@@ -4468,6 +3589,9 @@ char *invalid_range(exarg_T *eap)
assert(eap->line2 >= 0);
// No error for value that is too big, will use the last entry.
if (eap->line2 <= 0) {
+ if (eap->addr_count == 0) {
+ return _(e_no_errors);
+ }
return _(e_invrange);
}
break;
@@ -4529,8 +3653,8 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep)
// Don't do it when ":vimgrep" is used for ":grep".
if ((eap->cmdidx == CMD_make || eap->cmdidx == CMD_lmake || isgrep)
&& !grep_internal(eap->cmdidx)) {
- const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? (char *)p_gp : (char *)curbuf->b_p_gp)
- : (*curbuf->b_p_mp == NUL ? (char *)p_mp : (char *)curbuf->b_p_mp);
+ const char *program = isgrep ? (*curbuf->b_p_gp == NUL ? (char *)p_gp : curbuf->b_p_gp)
+ : (*curbuf->b_p_mp == NUL ? (char *)p_mp : curbuf->b_p_mp);
arg = skipwhite(arg);
@@ -4544,7 +3668,7 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep)
STRCAT(new_cmdline, arg);
}
- msg_make((char_u *)arg);
+ msg_make(arg);
// 'eap->cmd' is not set here, because it is not used at CMD_make
xfree(*cmdlinep);
@@ -4560,45 +3684,35 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep)
/// @return FAIL for failure, OK otherwise.
int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
{
- int has_wildcards; // need to expand wildcards
- char *repl;
- size_t srclen;
- char *p;
- int escaped;
-
// Skip a regexp pattern for ":vimgrep[add] pat file..."
- p = skip_grep_pat(eap);
-
- /*
- * Decide to expand wildcards *before* replacing '%', '#', etc. If
- * the file name contains a wildcard it should not cause expanding.
- * (it will be expanded anyway if there is a wildcard before replacing).
- */
- has_wildcards = path_has_wildcard((char_u *)p);
+ char *p = skip_grep_pat(eap);
+
+ // Decide to expand wildcards *before* replacing '%', '#', etc. If
+ // the file name contains a wildcard it should not cause expanding.
+ // (it will be expanded anyway if there is a wildcard before replacing).
+ int has_wildcards = path_has_wildcard((char_u *)p);
while (*p != NUL) {
// Skip over `=expr`, wildcards in it are not expanded.
if (p[0] == '`' && p[1] == '=') {
p += 2;
(void)skip_expr(&p);
if (*p == '`') {
- ++p;
+ p++;
}
continue;
}
- /*
- * Quick check if this cannot be the start of a special string.
- * Also removes backslash before '%', '#' and '<'.
- */
+ // Quick check if this cannot be the start of a special string.
+ // Also removes backslash before '%', '#' and '<'.
if (vim_strchr("%#<", *p) == NULL) {
p++;
continue;
}
- /*
- * Try to find a match at this position.
- */
- repl = (char *)eval_vars((char_u *)p, (char_u *)eap->arg, &srclen, &(eap->do_ecmd_lnum),
- errormsgp, &escaped);
+ // Try to find a match at this position.
+ size_t srclen;
+ int escaped;
+ char *repl = (char *)eval_vars((char_u *)p, (char_u *)eap->arg, &srclen, &(eap->do_ecmd_lnum),
+ errormsgp, &escaped, true);
if (*errormsgp != NULL) { // error detected
return FAIL;
}
@@ -4668,24 +3782,20 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
xfree(repl);
}
- /*
- * One file argument: Expand wildcards.
- * Don't do this with ":r !command" or ":w !command".
- */
+ // One file argument: Expand wildcards.
+ // Don't do this with ":r !command" or ":w !command".
if ((eap->argt & EX_NOSPC) && !eap->usefilter) {
// Replace environment variables.
if (has_wildcards) {
- /*
- * May expand environment variables. This
- * can be done much faster with expand_env() than with
- * something else (e.g., calling a shell).
- * After expanding environment variables, check again
- * if there are still wildcards present.
- */
+ // May expand environment variables. This
+ // can be done much faster with expand_env() than with
+ // something else (e.g., calling a shell).
+ // After expanding environment variables, check again
+ // if there are still wildcards present.
if (vim_strchr(eap->arg, '$') != NULL
|| vim_strchr(eap->arg, '~') != NULL) {
- expand_env_esc((char_u *)eap->arg, NameBuff, MAXPATHL, true, true, NULL);
- has_wildcards = path_has_wildcard(NameBuff);
+ expand_env_esc((char_u *)eap->arg, (char_u *)NameBuff, MAXPATHL, true, true, NULL);
+ has_wildcards = path_has_wildcard((char_u *)NameBuff);
p = (char *)NameBuff;
} else {
p = NULL;
@@ -4695,15 +3805,16 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
}
}
- /*
- * Halve the number of backslashes (this is Vi compatible).
- * For Unix, when wildcards are expanded, this is
- * done by ExpandOne() below.
- */
+ // Halve the number of backslashes (this is Vi compatible).
+ // For Unix, when wildcards are expanded, this is
+ // done by ExpandOne() below.
#ifdef UNIX
- if (!has_wildcards)
-#endif
+ if (!has_wildcards) {
+ backslash_halve(eap->arg);
+ }
+#else
backslash_halve((char_u *)eap->arg);
+#endif
if (has_wildcards) {
expand_T xpc;
@@ -4733,11 +3844,9 @@ int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp)
/// @return a pointer to the character after the replaced string.
static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, char **cmdlinep)
{
- /*
- * The new command line is build in new_cmdline[].
- * First allocate it.
- * Careful: a "+cmd" argument may have been NUL terminated.
- */
+ // The new command line is build in new_cmdline[].
+ // First allocate it.
+ // Careful: a "+cmd" argument may have been NUL terminated.
size_t len = STRLEN(repl);
size_t i = (size_t)(src - *cmdlinep) + STRLEN(src + srclen) + len + 3;
if (eap->nextcmd != NULL) {
@@ -4746,12 +3855,10 @@ static char *repl_cmdline(exarg_T *eap, char *src, size_t srclen, char *repl, ch
char *new_cmdline = xmalloc(i);
size_t offset = (size_t)(src - *cmdlinep);
- /*
- * Copy the stuff before the expanded part.
- * Copy the expanded stuff.
- * Copy what came after the expanded part.
- * Copy the next commands, if there are any.
- */
+ // Copy the stuff before the expanded part.
+ // Copy the expanded stuff.
+ // Copy what came after the expanded part.
+ // Copy the next commands, if there are any.
i = offset; // length of part before match
memmove(new_cmdline, *cmdlinep, i);
@@ -4847,12 +3954,12 @@ static char *getargcmd(char **argp)
char *command = NULL;
if (*arg == '+') { // +[command]
- ++arg;
+ arg++;
if (ascii_isspace(*arg) || *arg == '\0') {
command = (char *)dollar_command;
} else {
command = arg;
- arg = skip_cmd_arg(command, TRUE);
+ arg = skip_cmd_arg(command, true);
if (*arg != NUL) {
*arg++ = NUL; // terminate command with NUL
}
@@ -4866,15 +3973,15 @@ static char *getargcmd(char **argp)
/// Find end of "+command" argument. Skip over "\ " and "\\".
///
-/// @param rembs TRUE to halve the number of backslashes
-static char *skip_cmd_arg(char *p, int rembs)
+/// @param rembs true to halve the number of backslashes
+char *skip_cmd_arg(char *p, int rembs)
{
while (*p && !ascii_isspace(*p)) {
if (*p == '\\' && p[1] != NUL) {
if (rembs) {
STRMOVE(p, p + 1);
} else {
- ++p;
+ p++;
}
}
MB_PTR_ADV(p);
@@ -4905,7 +4012,6 @@ static int getargopt(exarg_T *eap)
char *arg = eap->arg + 2;
int *pp = NULL;
int bad_char_idx;
- char *p;
// ":edit ++[no]bin[ary] file"
if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) {
@@ -4958,13 +4064,13 @@ static int getargopt(exarg_T *eap)
*arg = NUL;
if (pp == &eap->force_ff) {
- if (check_ff_value((char_u *)eap->cmd + eap->force_ff) == FAIL) {
+ if (check_ff_value(eap->cmd + eap->force_ff) == FAIL) {
return FAIL;
}
eap->force_ff = (char_u)eap->cmd[eap->force_ff];
} else if (pp == &eap->force_enc) {
// Make 'fileencoding' lower case.
- for (p = eap->cmd + eap->force_enc; *p != NUL; p++) {
+ for (char *p = eap->cmd + eap->force_enc; *p != NUL; p++) {
*p = (char)TOLOWER_ASC(*p);
}
} else {
@@ -4989,7 +4095,6 @@ static int get_tabpage_arg(exarg_T *eap)
if (eap->arg && *eap->arg != NUL) {
char *p = eap->arg;
- char *p_save;
int relative = 0; // argument +N/-N means: go to N places to the
// right/left relative to the current position.
@@ -5001,7 +4106,7 @@ static int get_tabpage_arg(exarg_T *eap)
p++;
}
- p_save = p;
+ char *p_save = p;
tab_number = (int)getdigits(&p, false, tab_number);
if (relative == 0) {
@@ -5193,7 +4298,7 @@ int ends_excmd(int c) FUNC_ATTR_CONST
/// @return the next command, after the first '|' or '\n' or,
/// NULL if not found.
-char_u *find_nextcmd(const char_u *p)
+char *find_nextcmd(const char *p)
{
while (*p != '|' && *p != '\n') {
if (*p == NUL) {
@@ -5201,7 +4306,7 @@ char_u *find_nextcmd(const char_u *p)
}
p++;
}
- return (char_u *)p + 1;
+ return (char *)p + 1;
}
/// Check if *p is a separator between Ex commands, skipping over white space.
@@ -5223,9 +4328,9 @@ char_u *check_nextcmd(char_u *p)
/// - and forceit not used
/// - and not repeated twice on a row
///
-/// @param message when FALSE check only, no messages
+/// @param message when false check only, no messages
///
-/// @return FAIL and give error message if 'message' TRUE, return OK otherwise
+/// @return FAIL and give error message if 'message' true, return OK otherwise
static int check_more(int message, bool forceit)
{
int n = ARGCOUNT - curwin->w_arg_idx - 1;
@@ -5266,10 +4371,9 @@ static void ex_colorscheme(exarg_T *eap)
{
if (*eap->arg == NUL) {
char *expr = xstrdup("g:colors_name");
- char *p = NULL;
emsg_off++;
- p = eval_to_string(expr, NULL, false);
+ char *p = eval_to_string(expr, NULL, false);
emsg_off--;
xfree(expr);
@@ -5473,16 +4577,15 @@ static void ex_pclose(exarg_T *eap)
/// @param tp NULL or the tab page "win" is in
void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
{
- int need_hide;
- buf_T *buf = win->w_buffer;
-
// Never close the autocommand window.
if (win == aucmd_win) {
emsg(_(e_autocmd_close));
return;
}
- need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
+ buf_T *buf = win->w_buffer;
+
+ bool need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1);
if (need_hide && !buf_hide(buf) && !forceit) {
if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) {
bufref_T bufref;
@@ -5510,8 +4613,6 @@ void ex_win_close(int forceit, win_T *win, tabpage_T *tp)
/// ":tabclose N": close tab page N.
static void ex_tabclose(exarg_T *eap)
{
- tabpage_T *tp;
-
if (cmdwin_type != 0) {
cmdwin_result = K_IGNORE;
} else if (first_tabpage->tp_next == NULL) {
@@ -5519,7 +4620,7 @@ static void ex_tabclose(exarg_T *eap)
} else {
int tab_number = get_tabpage_arg(eap);
if (eap->errmsg == NULL) {
- tp = find_tabpage(tab_number);
+ tabpage_T *tp = find_tabpage(tab_number);
if (tp == NULL) {
beep_flush();
return;
@@ -5591,7 +4692,6 @@ void tabpage_close(int forceit)
void tabpage_close_other(tabpage_T *tp, int forceit)
{
int done = 0;
- win_T *wp;
int h = tabline_height();
char prev_idx[NUMBUFLEN];
@@ -5599,7 +4699,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
// one. OK, so I'm paranoid...
while (++done < 1000) {
snprintf((char *)prev_idx, sizeof(prev_idx), "%i", tabpage_index(tp));
- wp = tp->tp_lastwin;
+ win_T *wp = tp->tp_lastwin;
ex_win_close(forceit, wp, tp);
// Autocommands may delete the tab page under our fingers and we may
@@ -5619,10 +4719,9 @@ void tabpage_close_other(tabpage_T *tp, int forceit)
static void ex_only(exarg_T *eap)
{
win_T *wp;
- linenr_T wnr;
if (eap->addr_count > 0) {
- wnr = eap->line2;
+ linenr_T wnr = eap->line2;
for (wp = firstwin; --wnr > 0;) {
if (wp->w_next == NULL) {
break;
@@ -5636,17 +4735,7 @@ static void ex_only(exarg_T *eap)
if (wp != curwin) {
win_goto(wp);
}
- close_others(TRUE, eap->forceit);
-}
-
-/// ":all" and ":sall".
-/// Also used for ":tab drop file ..." after setting the argument list.
-void ex_all(exarg_T *eap)
-{
- if (eap->addr_count == 0) {
- eap->line2 = 9999;
- }
- do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop);
+ close_others(true, eap->forceit);
}
static void ex_hide(exarg_T *eap)
@@ -5761,162 +4850,6 @@ static void ex_goto(exarg_T *eap)
goto_byte(eap->line2);
}
-/// Clear an argument list: free all file names and reset it to zero entries.
-void alist_clear(alist_T *al)
-{
-#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
- GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
-}
-
-/// Init an argument list.
-void alist_init(alist_T *al)
-{
- ga_init(&al->al_ga, (int)sizeof(aentry_T), 5);
-}
-
-/// Remove a reference from an argument list.
-/// Ignored when the argument list is the global one.
-/// If the argument list is no longer used by any window, free it.
-void alist_unlink(alist_T *al)
-{
- if (al != &global_alist && --al->al_refcount <= 0) {
- alist_clear(al);
- xfree(al);
- }
-}
-
-/// Create a new argument list and use it for the current window.
-void alist_new(void)
-{
- curwin->w_alist = xmalloc(sizeof(*curwin->w_alist));
- curwin->w_alist->al_refcount = 1;
- curwin->w_alist->id = ++max_alist_id;
- alist_init(curwin->w_alist);
-}
-
-#if !defined(UNIX)
-
-/// Expand the file names in the global argument list.
-/// If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
-/// numbers to be re-used.
-void alist_expand(int *fnum_list, int fnum_len)
-{
- char **old_arg_files;
- int old_arg_count;
- char **new_arg_files;
- int new_arg_file_count;
- char *save_p_su = p_su;
- int i;
-
- /* Don't use 'suffixes' here. This should work like the shell did the
- * expansion. Also, the vimrc file isn't read yet, thus the user
- * can't set the options. */
- p_su = empty_option;
- old_arg_files = xmalloc(sizeof(*old_arg_files) * GARGCOUNT);
- for (i = 0; i < GARGCOUNT; ++i) {
- old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname);
- }
- old_arg_count = GARGCOUNT;
- if (expand_wildcards(old_arg_count, old_arg_files,
- &new_arg_file_count, &new_arg_files,
- EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK
- && new_arg_file_count > 0) {
- alist_set(&global_alist, new_arg_file_count, new_arg_files,
- TRUE, fnum_list, fnum_len);
- FreeWild(old_arg_count, old_arg_files);
- }
- p_su = save_p_su;
-}
-#endif
-
-/// Set the argument list for the current window.
-/// Takes over the allocated files[] and the allocated fnames in it.
-void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len)
-{
- int i;
- static int recursive = 0;
-
- if (recursive) {
- emsg(_(e_au_recursive));
- return;
- }
- recursive++;
-
- alist_clear(al);
- ga_grow(&al->al_ga, count);
- {
- for (i = 0; i < count; ++i) {
- if (got_int) {
- /* When adding many buffers this can take a long time. Allow
- * interrupting here. */
- while (i < count) {
- xfree(files[i++]);
- }
- break;
- }
-
- /* May set buffer name of a buffer previously used for the
- * argument list, so that it's re-used by alist_add. */
- if (fnum_list != NULL && i < fnum_len) {
- buf_set_name(fnum_list[i], files[i]);
- }
-
- alist_add(al, files[i], use_curbuf ? 2 : 1);
- os_breakcheck();
- }
- xfree(files);
- }
-
- if (al == &global_alist) {
- arg_had_last = false;
- }
- recursive--;
-}
-
-/// Add file "fname" to argument list "al".
-/// "fname" must have been allocated and "al" must have been checked for room.
-///
-/// @param set_fnum 1: set buffer number; 2: re-use curbuf
-void alist_add(alist_T *al, char *fname, int set_fnum)
-{
- if (fname == NULL) { // don't add NULL file names
- return;
- }
-#ifdef BACKSLASH_IN_FILENAME
- slash_adjust(fname);
-#endif
- AARGLIST(al)[al->al_ga.ga_len].ae_fname = (char_u *)fname;
- if (set_fnum > 0) {
- AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
- buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
- }
- ++al->al_ga.ga_len;
-}
-
-#if defined(BACKSLASH_IN_FILENAME)
-
-/// Adjust slashes in file names. Called after 'shellslash' was set.
-void alist_slash_adjust(void)
-{
- for (int i = 0; i < GARGCOUNT; ++i) {
- if (GARGLIST[i].ae_fname != NULL) {
- slash_adjust(GARGLIST[i].ae_fname);
- }
- }
-
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->w_alist != &global_alist) {
- for (int i = 0; i < WARGCOUNT(wp); ++i) {
- if (WARGLIST(wp)[i].ae_fname != NULL) {
- slash_adjust(WARGLIST(wp)[i].ae_fname);
- }
- }
- }
- }
-}
-
-#endif
-
/// ":preserve".
static void ex_preserve(exarg_T *eap)
{
@@ -5985,9 +4918,7 @@ void ex_splitview(exarg_T *eap)
eap->arg = fname;
}
- /*
- * Either open new tab page or split the window.
- */
+ // Either open new tab page or split the window.
if (use_tab) {
if (win_new_tabpage(cmdmod.cmod_tab != 0 ? cmdmod.cmod_tab : eap->addr_count == 0
? 0 : (int)eap->line2 + 1, (char_u *)eap->arg) != FAIL) {
@@ -6021,12 +4952,11 @@ theend:
/// Open a new tab page.
void tabpage_new(void)
{
- exarg_T ea;
-
- memset(&ea, 0, sizeof(ea));
- ea.cmdidx = CMD_tabnew;
- ea.cmd = "tabn";
- ea.arg = "";
+ exarg_T ea = {
+ .cmdidx = CMD_tabnew,
+ .cmd = "tabn",
+ .arg = "",
+ };
ex_splitview(&ea);
}
@@ -6092,7 +5022,7 @@ static void ex_tabs(exarg_T *eap)
int tabcount = 1;
msg_start();
- msg_scroll = TRUE;
+ msg_scroll = true;
win_T *lastused_win = valid_tabpage(lastused_tabpage)
? lastused_tabpage->tp_curwin
@@ -6105,7 +5035,7 @@ static void ex_tabs(exarg_T *eap)
msg_putchar('\n');
vim_snprintf((char *)IObuff, IOSIZE, _("Tab page %d"), tabcount++);
- msg_outtrans_attr(IObuff, HL_ATTR(HLF_T));
+ msg_outtrans_attr((char *)IObuff, HL_ATTR(HLF_T));
ui_flush(); // output one line at a time
os_breakcheck();
@@ -6136,7 +5066,7 @@ static void ex_tabs(exarg_T *eap)
static void ex_mode(exarg_T *eap)
{
if (*eap->arg == NUL) {
- must_redraw = CLEAR;
+ must_redraw = UPD_CLEAR;
ex_redraw(eap);
} else {
emsg(_(e_screenmode));
@@ -6147,15 +5077,14 @@ static void ex_mode(exarg_T *eap)
/// set, increment or decrement current window height
static void ex_resize(exarg_T *eap)
{
- int n;
win_T *wp = curwin;
if (eap->addr_count > 0) {
- n = (int)eap->line2;
+ int n = (int)eap->line2;
for (wp = firstwin; wp->w_next != NULL && --n > 0; wp = wp->w_next) {}
}
- n = (int)atol(eap->arg);
+ int n = (int)atol(eap->arg);
if (cmdmod.cmod_split & WSP_VERT) {
if (*eap->arg == '-' || *eap->arg == '+') {
n += wp->w_width;
@@ -6176,15 +5105,12 @@ static void ex_resize(exarg_T *eap)
/// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap)
{
- char *fname;
- linenr_T count;
-
- fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
- FNAME_MESS, true, (char_u *)curbuf->b_ffname);
+ char *fname = (char *)find_file_in_path((char_u *)eap->arg, STRLEN(eap->arg),
+ FNAME_MESS, true, (char_u *)curbuf->b_ffname);
if (eap->addr_count > 0) {
// Repeat finding the file "count" times. This matters when it
// appears several times in the path.
- count = eap->line2;
+ linenr_T count = eap->line2;
while (fname != NULL && --count > 0) {
xfree(fname);
fname = (char *)find_file_in_path(NULL, 0, FNAME_MESS, false, (char_u *)curbuf->b_ffname);
@@ -6210,11 +5136,8 @@ static void ex_edit(exarg_T *eap)
void do_exedit(exarg_T *eap, win_T *old_curwin)
{
int n;
- int need_hide;
- /*
- * ":vi" command ends Ex mode.
- */
+ // ":vi" command ends Ex mode.
if (exmode_active && (eap->cmdidx == CMD_visual
|| eap->cmdidx == CMD_view)) {
exmode_active = false;
@@ -6235,7 +5158,7 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
no_wait_return = 0;
need_wait_return = false;
msg_scroll = 0;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
pending_exmode_active = true;
normal_enter(false, true);
@@ -6286,12 +5209,12 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
old_curwin == NULL ? curwin : NULL) == FAIL) {
// Editing the file failed. If the window was split, close it.
if (old_curwin != NULL) {
- need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1);
+ bool need_hide = (curbufIsChanged() && curbuf->b_nwindows <= 1);
if (!need_hide || buf_hide(curbuf)) {
cleanup_T cs;
// Reset the error/interrupt/exception state here so that
- // aborting() returns FALSE when closing a window.
+ // aborting() returns false when closing a window.
enter_cleanup(&cs);
win_close(curwin, !need_hide && !buf_hide(curbuf), false);
@@ -6320,10 +5243,8 @@ void do_exedit(exarg_T *eap, win_T *old_curwin)
}
}
- /*
- * if ":split file" worked, set alternate file name in old window to new
- * file
- */
+ // if ":split file" worked, set alternate file name in old window to new
+ // file
if (old_curwin != NULL
&& *eap->arg != NUL
&& curwin != old_curwin
@@ -6369,9 +5290,7 @@ static void ex_syncbind(exarg_T *eap)
setpcmark();
- /*
- * determine max topline
- */
+ // determine max topline
if (curwin->w_p_scb) {
topline = curwin->w_topline;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
@@ -6389,23 +5308,21 @@ static void ex_syncbind(exarg_T *eap)
topline = 1;
}
- /*
- * Set all scrollbind windows to the same topline.
- */
+ // Set all scrollbind windows to the same topline.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
curwin = wp;
if (curwin->w_p_scb) {
curbuf = curwin->w_buffer;
y = topline - curwin->w_topline;
if (y > 0) {
- scrollup(y, TRUE);
+ scrollup(y, true);
} else {
- scrolldown(-y, TRUE);
+ scrolldown(-y, true);
}
curwin->w_scbind_pos = topline;
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
cursor_correct();
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
}
}
curwin = save_curwin;
@@ -6425,9 +5342,7 @@ static void ex_syncbind(exarg_T *eap)
static void ex_read(exarg_T *eap)
{
- int i;
int empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
- linenr_T lnum;
if (eap->usefilter) { // :r!cmd
do_bang(1, eap, false, false, true);
@@ -6435,6 +5350,7 @@ static void ex_read(exarg_T *eap)
if (u_save(eap->line2, (linenr_T)(eap->line2 + 1)) == FAIL) {
return;
}
+ int i;
if (*eap->arg == NUL) {
if (check_fname() == FAIL) { // check for no file name
@@ -6457,6 +5373,7 @@ static void ex_read(exarg_T *eap)
if (empty && exmode_active) {
// Delete the empty line that remains. Historically ex does
// this but vi doesn't.
+ linenr_T lnum;
if (eap->line2 == 0) {
lnum = curbuf->b_ml.ml_line_count;
} else {
@@ -6471,7 +5388,7 @@ static void ex_read(exarg_T *eap)
deleted_lines_mark(lnum, 1L);
}
}
- redraw_curbuf_later(VALID);
+ redraw_curbuf_later(UPD_VALID);
}
}
}
@@ -6571,8 +5488,8 @@ bool changedir_func(char *new_dir, CdScope scope)
new_dir = pdir;
}
- if (os_dirname(NameBuff, MAXPATHL) == OK) {
- pdir = (char *)vim_strsave(NameBuff);
+ if (os_dirname((char_u *)NameBuff, MAXPATHL) == OK) {
+ pdir = xstrdup(NameBuff);
} else {
pdir = NULL;
}
@@ -6585,7 +5502,7 @@ bool changedir_func(char *new_dir, CdScope scope)
if (*new_dir == NUL && p_cdh) {
#endif
// Use NameBuff for home directory name.
- expand_env((char_u *)"$HOME", NameBuff, MAXPATHL);
+ expand_env("$HOME", NameBuff, MAXPATHL);
new_dir = (char *)NameBuff;
}
@@ -6654,7 +5571,7 @@ void ex_cd(exarg_T *eap)
/// ":pwd".
static void ex_pwd(exarg_T *eap)
{
- if (os_dirname(NameBuff, MAXPATHL) == OK) {
+ if (os_dirname((char_u *)NameBuff, MAXPATHL) == OK) {
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(NameBuff);
#endif
@@ -6685,17 +5602,14 @@ static void ex_equal(exarg_T *eap)
static void ex_sleep(exarg_T *eap)
{
- int n;
- long len;
-
if (cursor_valid()) {
- n = curwin->w_winrow + curwin->w_wrow - msg_scrolled;
+ int n = curwin->w_winrow + curwin->w_wrow - msg_scrolled;
if (n >= 0) {
ui_cursor_goto(n, curwin->w_wincol + curwin->w_wcol);
}
}
- len = eap->line2;
+ long len = eap->line2;
switch (*eap->arg) {
case 'm':
break;
@@ -6815,7 +5729,7 @@ static void ex_operators(exarg_T *eap)
} else {
oa.op_type = OP_LSHIFT;
}
- op_shift(&oa, FALSE, eap->amount);
+ op_shift(&oa, false, eap->amount);
break;
}
virtual_op = kNone;
@@ -6828,7 +5742,7 @@ static void ex_put(exarg_T *eap)
// ":0put" works like ":1put!".
if (eap->line2 == 0) {
eap->line2 = 1;
- eap->forceit = TRUE;
+ eap->forceit = true;
}
curwin->w_cursor.lnum = eap->line2;
check_cursor_col();
@@ -6846,9 +5760,7 @@ static void ex_copymove(exarg_T *eap)
}
get_flags(eap);
- /*
- * move or copy lines from 'eap->line1'-'eap->line2' to below line 'n'
- */
+ // move or copy lines from 'eap->line1'-'eap->line2' to below line 'n'
if (n == MAXLNUM || n < 0 || n > curbuf->b_ml.ml_line_count) {
emsg(_(e_invrange));
return;
@@ -6910,7 +5822,7 @@ static void ex_join(exarg_T *eap)
beep_flush();
return;
}
- ++eap->line2;
+ eap->line2++;
}
do_join((size_t)((ssize_t)eap->line2 - eap->line1 + 1), !eap->forceit, true, true, true);
beginline(BL_WHITE | BL_FIX);
@@ -6939,11 +5851,9 @@ static void ex_at(exarg_T *eap)
exec_from_reg = true;
- /*
- * Execute from the typeahead buffer.
- * Continue until the stuff buffer is empty and all added characters
- * have been consumed.
- */
+ // Execute from the typeahead buffer.
+ // Continue until the stuff buffer is empty and all added characters
+ // have been consumed.
while (!stuff_empty() || typebuf.tb_len > prev_len) {
(void)do_cmdline(NULL, getexline, NULL, DOCMD_NOWAIT|DOCMD_VERBOSE);
}
@@ -7032,15 +5942,15 @@ static void ex_later(exarg_T *eap)
count = getdigits_long(&p, false, 0);
switch (*p) {
case 's':
- ++p; sec = true; break;
+ p++; sec = true; break;
case 'm':
- ++p; sec = true; count *= 60; break;
+ p++; sec = true; count *= 60; break;
case 'h':
- ++p; sec = true; count *= 60 * 60; break;
+ p++; sec = true; count *= 60 * 60; break;
case 'd':
- ++p; sec = true; count *= 24 * 60 * 60; break;
+ p++; sec = true; count *= 24 * 60 * 60; break;
case 'f':
- ++p; file = true; break;
+ p++; file = true; break;
}
}
@@ -7055,17 +5965,16 @@ static void ex_later(exarg_T *eap)
/// ":redir": start/stop redirection.
static void ex_redir(exarg_T *eap)
{
- char *mode;
- char *fname;
char *arg = eap->arg;
if (STRICMP(eap->arg, "END") == 0) {
close_redir();
} else {
if (*arg == '>') {
- ++arg;
+ arg++;
+ char *mode;
if (*arg == '>') {
- ++arg;
+ arg++;
mode = "a";
} else {
mode = "w";
@@ -7075,7 +5984,7 @@ static void ex_redir(exarg_T *eap)
close_redir();
// Expand environment variables and "~/".
- fname = expand_env_save(arg);
+ char *fname = expand_env_save(arg);
if (fname == NULL) {
return;
}
@@ -7085,7 +5994,7 @@ static void ex_redir(exarg_T *eap)
} else if (*arg == '@') {
// redirect to a register a-z (resp. A-Z for appending)
close_redir();
- ++arg;
+ arg++;
if (valid_yank_reg(*arg, true) && *arg != '_') {
redir_reg = (char_u)(*arg++);
if (*arg == '>' && arg[1] == '>') { // append
@@ -7114,10 +6023,10 @@ static void ex_redir(exarg_T *eap)
arg += 2;
if (*arg == '>') {
- ++arg;
- append = TRUE;
+ arg++;
+ append = true;
} else {
- append = FALSE;
+ append = false;
}
if (var_redir_start(skipwhite(arg), append) == OK) {
@@ -7146,14 +6055,15 @@ static void ex_redraw(exarg_T *eap)
int p = p_lz;
RedrawingDisabled = 0;
- p_lz = FALSE;
+ p_lz = false;
validate_cursor();
update_topline(curwin);
if (eap->forceit) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
+ redraw_cmdline = true;
}
- update_screen(eap->forceit ? NOT_VALID
- : VIsual_active ? INVERTED : 0);
+ update_screen(eap->forceit ? UPD_NOT_VALID
+ : VIsual_active ? UPD_INVERTED : 0);
if (need_maketitle) {
maketitle();
}
@@ -7180,13 +6090,13 @@ static void ex_redrawstatus(exarg_T *eap)
int p = p_lz;
RedrawingDisabled = 0;
- p_lz = FALSE;
+ p_lz = false;
if (eap->forceit) {
status_redraw_all();
} else {
status_redraw_curbuf();
}
- update_screen(VIsual_active ? INVERTED : 0);
+ update_screen(VIsual_active ? UPD_INVERTED : 0);
RedrawingDisabled = r;
p_lz = p;
ui_flush();
@@ -7245,11 +6155,9 @@ int vim_mkdir_emsg(const char *const name, const int prot)
/// @return file descriptor, or NULL on failure.
FILE *open_exfile(char_u *fname, int forceit, char *mode)
{
- FILE *fd;
-
#ifdef UNIX
// with Unix it is possible to open a directory
- if (os_isdir(fname)) {
+ if (os_isdir((char *)fname)) {
semsg(_(e_isadir2), fname);
return NULL;
}
@@ -7259,6 +6167,7 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode)
return NULL;
}
+ FILE *fd;
if ((fd = os_fopen((char *)fname, mode)) == NULL) {
semsg(_("E190: Cannot open \"%s\" for writing"), fname);
}
@@ -7269,14 +6178,12 @@ FILE *open_exfile(char_u *fname, int forceit, char *mode)
/// ":mark" and ":k".
static void ex_mark(exarg_T *eap)
{
- pos_T pos;
-
if (*eap->arg == NUL) { // No argument?
emsg(_(e_argreq));
} else if (eap->arg[1] != NUL) { // more than one character?
semsg(_(e_trailing_arg), eap->arg);
} else {
- pos = curwin->w_cursor; // save curwin->w_cursor
+ pos_T pos = curwin->w_cursor; // save curwin->w_cursor
curwin->w_cursor.lnum = eap->line2;
beginline(BL_WHITE | BL_FIX);
if (setmark(*eap->arg) == FAIL) { // set mark
@@ -7357,10 +6264,7 @@ static void ex_normal(exarg_T *eap)
emsg("Can't re-enter normal mode from terminal mode");
return;
}
- save_state_T save_state;
char *arg = NULL;
- int l;
- char *p;
if (ex_normal_lock > 0) {
emsg(_(e_secure));
@@ -7378,6 +6282,8 @@ static void ex_normal(exarg_T *eap)
int len = 0;
// Count the number of characters to be escaped.
+ int l;
+ char *p;
for (p = eap->arg; *p != NUL; p++) {
for (l = utfc_ptr2len(p) - 1; l > 0; l--) {
if (*++p == (char)K_SPECIAL) { // trailbyte K_SPECIAL
@@ -7403,6 +6309,7 @@ static void ex_normal(exarg_T *eap)
}
ex_normal_busy++;
+ save_state_T save_state;
if (save_current_state(&save_state)) {
// Repeat the :normal command for each line in the range. When no
// range given, execute it just once, without positioning the cursor
@@ -7521,8 +6428,6 @@ static void ex_psearch(exarg_T *eap)
static void ex_findpat(exarg_T *eap)
{
bool whole = true;
- long n;
- char *p;
int action;
switch (cmdnames[eap->cmdidx].cmd_name[2]) {
@@ -7544,7 +6449,7 @@ static void ex_findpat(exarg_T *eap)
break;
}
- n = 1;
+ long n = 1;
if (ascii_isdigit(*eap->arg)) { // get count
n = getdigits_long(&eap->arg, false, 0);
eap->arg = skipwhite(eap->arg);
@@ -7552,7 +6457,7 @@ static void ex_findpat(exarg_T *eap)
if (*eap->arg == '/') { // Match regexp, not just whole words
whole = false;
eap->arg++;
- p = (char *)skip_regexp((char_u *)eap->arg, '/', p_magic, NULL);
+ char *p = skip_regexp(eap->arg, '/', p_magic, NULL);
if (*p) {
*p++ = NUL;
p = skipwhite(p);
@@ -7594,7 +6499,7 @@ static void ex_pedit(exarg_T *eap)
if (curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
g_do_tagpreview = 0;
@@ -7672,6 +6577,7 @@ enum {
SPEC_SFILE,
SPEC_SLNUM,
SPEC_STACK,
+ SPEC_SCRIPT,
SPEC_AFILE,
SPEC_ABUF,
SPEC_AMATCH,
@@ -7686,7 +6592,6 @@ enum {
ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
FUNC_ATTR_NONNULL_ALL
{
- size_t len;
static char *(spec_str[]) = {
[SPEC_PERC] = "%",
[SPEC_HASH] = "#",
@@ -7697,6 +6602,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
[SPEC_SFILE] = "<sfile>", // ":so" file name
[SPEC_SLNUM] = "<slnum>", // ":so" file line number
[SPEC_STACK] = "<stack>", // call stack
+ [SPEC_SCRIPT] = "<script>", // script file name
[SPEC_AFILE] = "<afile>", // autocommand file name
[SPEC_ABUF] = "<abuf>", // autocommand buffer number
[SPEC_AMATCH] = "<amatch>", // autocommand match name
@@ -7705,8 +6611,8 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
// [SPEC_CLIENT] = "<client>",
};
- for (size_t i = 0; i < ARRAY_SIZE(spec_str); ++i) {
- len = STRLEN(spec_str[i]);
+ for (size_t i = 0; i < ARRAY_SIZE(spec_str); i++) {
+ size_t len = STRLEN(spec_str[i]);
if (STRNCMP(src, spec_str[i], len) == 0) {
*usedlen = len;
assert(i <= SSIZE_MAX);
@@ -7718,40 +6624,40 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen)
/// Evaluate cmdline variables.
///
-/// change '%' to curbuf->b_ffname
-/// '#' to curwin->w_alt_fnum
-/// '<cword>' to word under the cursor
-/// '<cWORD>' to WORD under the cursor
-/// '<cexpr>' to C-expression under the cursor
-/// '<cfile>' to path name under the cursor
-/// '<sfile>' to sourced file name
-/// '<slnum>' to sourced file line number
-/// '<afile>' to file name for autocommand
-/// '<abuf>' to buffer number for autocommand
-/// '<amatch>' to matching name for autocommand
+/// change "%" to curbuf->b_ffname
+/// "#" to curwin->w_alt_fnum
+/// "<cword>" to word under the cursor
+/// "<cWORD>" to WORD under the cursor
+/// "<cexpr>" to C-expression under the cursor
+/// "<cfile>" to path name under the cursor
+/// "<sfile>" to sourced file name
+/// "<stack>" to call stack
+/// "<script>" to current script name
+/// "<slnum>" to sourced file line number
+/// "<afile>" to file name for autocommand
+/// "<abuf>" to buffer number for autocommand
+/// "<amatch>" to matching name for autocommand
///
/// When an error is detected, "errormsg" is set to a non-NULL pointer (may be
/// "" for error without a message) and NULL is returned.
///
-/// @param src pointer into commandline
-/// @param srcstart beginning of valid memory for src
-/// @param usedlen characters after src that are used
-/// @param lnump line number for :e command, or NULL
-/// @param errormsg pointer to error message
-/// @param escaped return value has escaped white space (can be NULL)
+/// @param src pointer into commandline
+/// @param srcstart beginning of valid memory for src
+/// @param usedlen characters after src that are used
+/// @param lnump line number for :e command, or NULL
+/// @param errormsg pointer to error message
+/// @param escaped return value has escaped white space (can be NULL)
+/// @param empty_is_error empty result is considered an error
///
/// @return an allocated string if a valid match was found.
/// Returns NULL if no match was found. "usedlen" then still contains the
/// number of characters to skip.
char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg,
- int *escaped)
+ int *escaped, bool empty_is_error)
{
- int i;
- char *s;
char *result;
char *resultbuf = NULL;
size_t resultlen;
- buf_T *buf;
int valid = VALID_HEAD | VALID_PATH; // Assume valid result.
bool tilde_file = false;
bool skip_mod = false;
@@ -7759,40 +6665,34 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
*errormsg = NULL;
if (escaped != NULL) {
- *escaped = FALSE;
+ *escaped = false;
}
- /*
- * Check if there is something to do.
- */
+ // Check if there is something to do.
ssize_t spec_idx = find_cmdline_var(src, usedlen);
if (spec_idx < 0) { // no match
*usedlen = 1;
return NULL;
}
- /*
- * Skip when preceded with a backslash "\%" and "\#".
- * Note: In "\\%" the % is also not recognized!
- */
+ // Skip when preceded with a backslash "\%" and "\#".
+ // Note: In "\\%" the % is also not recognized!
if (src > srcstart && src[-1] == '\\') {
*usedlen = 0;
STRMOVE(src - 1, src); // remove backslash
return NULL;
}
- /*
- * word or WORD under cursor
- */
+ // word or WORD under cursor
if (spec_idx == SPEC_CWORD
|| spec_idx == SPEC_CCWORD
|| spec_idx == SPEC_CEXPR) {
- resultlen = find_ident_under_cursor((char_u **)&result,
+ resultlen = find_ident_under_cursor(&result,
spec_idx == SPEC_CWORD
- ? (FIND_IDENT | FIND_STRING)
- : (spec_idx == SPEC_CEXPR
- ? (FIND_IDENT | FIND_STRING | FIND_EVAL)
- : FIND_STRING));
+ ? (FIND_IDENT | FIND_STRING)
+ : (spec_idx == SPEC_CEXPR
+ ? (FIND_IDENT | FIND_STRING | FIND_EVAL)
+ : FIND_STRING));
if (resultlen == 0) {
*errormsg = "";
return NULL;
@@ -7822,16 +6722,16 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
resultbuf = result;
*usedlen = 2;
if (escaped != NULL) {
- *escaped = TRUE;
+ *escaped = true;
}
skip_mod = true;
break;
}
- s = (char *)src + 1;
+ char *s = (char *)src + 1;
if (*s == '<') { // "#<99" uses v:oldfiles.
s++;
}
- i = getdigits_int(&s, false, 0);
+ int i = getdigits_int(&s, false, 0);
if ((char_u *)s == src + 2 && src[1] == '-') {
// just a minus sign, don't skip over it
s--;
@@ -7853,7 +6753,7 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
if (i == 0 && src[1] == '<' && *usedlen > 1) {
*usedlen = 1;
}
- buf = buflist_findnr(i);
+ buf_T *buf = buflist_findnr(i);
if (buf == NULL) {
*errormsg = _("E194: No alternate file name to substitute for '#'");
return NULL;
@@ -7918,29 +6818,46 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
break;
case SPEC_SFILE: // file name for ":so" command
- result = sourcing_name;
+ result = estack_sfile(ESTACK_SFILE);
if (result == NULL) {
- *errormsg = _("E498: no :source file name to substitute for \"<sfile>\"");
+ *errormsg = _(e_no_source_file_name_to_substitute_for_sfile);
return NULL;
}
+ resultbuf = result; // remember allocated string
+ break;
+ case SPEC_STACK: // call stack
+ result = estack_sfile(ESTACK_STACK);
+ if (result == NULL) {
+ *errormsg = _(e_no_call_stack_to_substitute_for_stack);
+ return NULL;
+ }
+ resultbuf = result; // remember allocated string
+ break;
+ case SPEC_SCRIPT: // script file name
+ result = estack_sfile(ESTACK_SCRIPT);
+ if (result == NULL) {
+ *errormsg = _(e_no_script_file_name_to_substitute_for_script);
+ return NULL;
+ }
+ resultbuf = result; // remember allocated string
break;
case SPEC_SLNUM: // line in file for ":so" command
- if (sourcing_name == NULL || sourcing_lnum == 0) {
+ if (SOURCING_NAME == NULL || SOURCING_LNUM == 0) {
*errormsg = _("E842: no line number to use for \"<slnum>\"");
return NULL;
}
- snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, sourcing_lnum);
+ snprintf(strbuf, sizeof(strbuf), "%" PRIdLINENR, SOURCING_LNUM);
result = strbuf;
break;
case SPEC_SFLNUM: // line in script file
- if (current_sctx.sc_lnum + sourcing_lnum == 0) {
+ if (current_sctx.sc_lnum + SOURCING_LNUM == 0) {
*errormsg = _("E961: no line number to use for \"<sflnum>\"");
return NULL;
}
snprintf((char *)strbuf, sizeof(strbuf), "%" PRIdLINENR,
- current_sctx.sc_lnum + sourcing_lnum);
+ current_sctx.sc_lnum + SOURCING_LNUM);
result = strbuf;
break;
@@ -7966,7 +6883,8 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
// Remove the file name extension.
if (src[*usedlen] == '<') {
(*usedlen)++;
- if ((s = (char *)STRRCHR(result, '.')) != NULL
+ char *s;
+ if ((s = strrchr(result, '.')) != NULL
&& s >= path_tail(result)) {
resultlen = (size_t)(s - result);
}
@@ -7981,11 +6899,13 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
}
if (resultlen == 0 || valid != VALID_HEAD + VALID_PATH) {
- if (valid != VALID_HEAD + VALID_PATH) {
- // xgettext:no-c-format
- *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
- } else {
- *errormsg = _("E500: Evaluates to an empty string");
+ if (empty_is_error) {
+ if (valid != VALID_HEAD + VALID_PATH) {
+ // xgettext:no-c-format
+ *errormsg = _("E499: Empty file name for '%' or '#', only works with \":p:h\"");
+ } else {
+ *errormsg = _("E500: Evaluates to an empty string");
+ }
}
result = NULL;
} else {
@@ -7995,88 +6915,22 @@ char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnum
return (char_u *)result;
}
-/// Concatenate all files in the argument list, separated by spaces, and return
-/// it in one allocated string.
-/// Spaces and backslashes in the file names are escaped with a backslash.
-static char *arg_all(void)
-{
- int len;
- int idx;
- char *retval = NULL;
- char *p;
-
- /*
- * Do this loop two times:
- * first time: compute the total length
- * second time: concatenate the names
- */
- for (;;) {
- len = 0;
- for (idx = 0; idx < ARGCOUNT; idx++) {
- p = alist_name(&ARGLIST[idx]);
- if (p == NULL) {
- continue;
- }
- if (len > 0) {
- // insert a space in between names
- if (retval != NULL) {
- retval[len] = ' ';
- }
- ++len;
- }
- for (; *p != NUL; p++) {
- if (*p == ' '
-#ifndef BACKSLASH_IN_FILENAME
- || *p == '\\'
-#endif
- || *p == '`') {
- // insert a backslash
- if (retval != NULL) {
- retval[len] = '\\';
- }
- len++;
- }
- if (retval != NULL) {
- retval[len] = *p;
- }
- len++;
- }
- }
-
- // second time: break here
- if (retval != NULL) {
- retval[len] = NUL;
- break;
- }
-
- // allocate memory
- retval = xmalloc((size_t)len + 1);
- }
-
- return retval;
-}
-
/// Expand the <sfile> string in "arg".
///
/// @return an allocated string, or NULL for any error.
char *expand_sfile(char *arg)
{
- char *errormsg;
- size_t len;
- char *result;
- char *newres;
- char *repl;
- size_t srclen;
- char *p;
+ char *result = xstrdup(arg);
- result = xstrdup(arg);
-
- for (p = result; *p;) {
+ for (char *p = result; *p;) {
if (STRNCMP(p, "<sfile>", 7) != 0) {
- ++p;
+ p++;
} else {
// replace "<sfile>" with the sourced file name, and do ":" stuff
- repl = (char *)eval_vars((char_u *)p, (char_u *)result, &srclen, NULL, &errormsg, NULL);
+ size_t srclen;
+ char *errormsg;
+ char *repl = (char *)eval_vars((char_u *)p, (char_u *)result, &srclen, NULL, &errormsg, NULL,
+ true);
if (errormsg != NULL) {
if (*errormsg) {
emsg(errormsg);
@@ -8088,8 +6942,8 @@ char *expand_sfile(char *arg)
p += srclen;
continue;
}
- len = STRLEN(result) - srclen + STRLEN(repl) + 1;
- newres = xmalloc(len);
+ size_t len = STRLEN(result) - srclen + STRLEN(repl) + 1;
+ char *newres = xmalloc(len);
memmove(newres, result, (size_t)(p - result));
STRCPY(newres + (p - result), repl);
len = STRLEN(newres);
@@ -8107,18 +6961,16 @@ char *expand_sfile(char *arg)
/// ":rshada" and ":wshada".
static void ex_shada(exarg_T *eap)
{
- char *save_shada;
-
- save_shada = (char *)p_shada;
+ char *save_shada = p_shada;
if (*p_shada == NUL) {
- p_shada = (char_u *)"'100";
+ p_shada = "'100";
}
if (eap->cmdidx == CMD_rviminfo || eap->cmdidx == CMD_rshada) {
(void)shada_read_everything(eap->arg, eap->forceit, false);
} else {
shada_write_file(eap->arg, eap->forceit);
}
- p_shada = (char_u *)save_shada;
+ p_shada = save_shada;
}
/// Make a dialog message in "buff[DIALOG_MSG_SIZE]".
@@ -8135,51 +6987,20 @@ void dialog_msg(char *buff, char *format, char *fname)
static void ex_behave(exarg_T *eap)
{
if (STRCMP(eap->arg, "mswin") == 0) {
- set_option_value("selection", 0L, "exclusive", 0);
- set_option_value("selectmode", 0L, "mouse,key", 0);
- set_option_value("mousemodel", 0L, "popup", 0);
- set_option_value("keymodel", 0L, "startsel,stopsel", 0);
+ set_option_value_give_err("selection", 0L, "exclusive", 0);
+ set_option_value_give_err("selectmode", 0L, "mouse,key", 0);
+ set_option_value_give_err("mousemodel", 0L, "popup", 0);
+ set_option_value_give_err("keymodel", 0L, "startsel,stopsel", 0);
} else if (STRCMP(eap->arg, "xterm") == 0) {
- set_option_value("selection", 0L, "inclusive", 0);
- set_option_value("selectmode", 0L, "", 0);
- set_option_value("mousemodel", 0L, "extend", 0);
- set_option_value("keymodel", 0L, "", 0);
+ set_option_value_give_err("selection", 0L, "inclusive", 0);
+ set_option_value_give_err("selectmode", 0L, "", 0);
+ set_option_value_give_err("mousemodel", 0L, "extend", 0);
+ set_option_value_give_err("keymodel", 0L, "", 0);
} else {
semsg(_(e_invarg2), eap->arg);
}
}
-/// Function given to ExpandGeneric() to obtain the possible arguments of the
-/// ":behave {mswin,xterm}" command.
-char *get_behave_arg(expand_T *xp, int idx)
-{
- if (idx == 0) {
- return "mswin";
- }
- if (idx == 1) {
- return "xterm";
- }
- return NULL;
-}
-
-/// Function given to ExpandGeneric() to obtain the possible arguments of the
-/// ":messages {clear}" command.
-char *get_messages_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
-{
- if (idx == 0) {
- return "clear";
- }
- return NULL;
-}
-
-char *get_mapclear_arg(expand_T *xp FUNC_ATTR_UNUSED, int idx)
-{
- if (idx == 0) {
- return "<buffer>";
- }
- return NULL;
-}
-
static TriState filetype_detect = kNone;
static TriState filetype_plugin = kNone;
static TriState filetype_indent = kNone;
@@ -8193,10 +7014,6 @@ static TriState filetype_indent = kNone;
/// indent off: load indoff.vim
static void ex_filetype(exarg_T *eap)
{
- char *arg = eap->arg;
- bool plugin = false;
- bool indent = false;
-
if (*eap->arg == NUL) {
// Print current status.
smsg("filetype detection:%s plugin:%s indent:%s",
@@ -8206,6 +7023,10 @@ static void ex_filetype(exarg_T *eap)
return;
}
+ char *arg = eap->arg;
+ bool plugin = false;
+ bool indent = false;
+
// Accept "plugin" and "indent" in any order.
for (;;) {
if (STRNCMP(arg, "plugin", 6) == 0) {
@@ -8294,7 +7115,7 @@ static void ex_setfiletype(exarg_T *eap)
arg += 9;
}
- set_option_value("filetype", 0L, arg, OPT_LOCAL);
+ set_option_value_give_err("filetype", 0L, arg, OPT_LOCAL);
if (arg != eap->arg) {
did_filetype = false;
}
@@ -8320,7 +7141,7 @@ void set_no_hlsearch(bool flag)
static void ex_nohlsearch(exarg_T *eap)
{
set_no_hlsearch(true);
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
static void ex_fold(exarg_T *eap)
@@ -8342,7 +7163,7 @@ static void ex_foldopen(exarg_T *eap)
static void ex_folddo(exarg_T *eap)
{
// First set the marks for all lines closed/open.
- for (linenr_T lnum = eap->line1; lnum <= eap->line2; ++lnum) {
+ for (linenr_T lnum = eap->line1; lnum <= eap->line2; lnum++) {
if (hasFolding(lnum, NULL, NULL) == (eap->cmdidx == CMD_folddoclosed)) {
ml_setmarked(lnum);
}
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index f67c8a6720..6dddafcfcb 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -16,12 +16,12 @@
#include "nvim/debugger.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
@@ -45,20 +45,19 @@
// there or even outside the try conditional. Try conditionals may be nested.
// Configuration whether an exception is thrown on error or interrupt. When
-// the preprocessor macros below evaluate to FALSE, an error (did_emsg) or
+// the preprocessor macros below evaluate to false, an error (did_emsg) or
// interrupt (got_int) under an active try conditional terminates the script
// after the non-active finally clauses of all active try conditionals have been
// executed. Otherwise, errors and/or interrupts are converted into catchable
-// exceptions, which terminate the script only if not caught. For user
-// exceptions, only current_exception is set. (Note: got_int can be set
-// asynchronously afterwards by a SIGINT, so current_exception && got_int is not
+// exceptions (did_throw additionally set), which terminate the script only if
+// not caught. For user exceptions, only did_throw is set. (Note: got_int can
+// be set asynchronously afterwards by a SIGINT, so did_throw && got_int is not
// a reliant test that the exception currently being thrown is an interrupt
// exception. Similarly, did_emsg can be set afterwards on an error in an
-// (unskipped) conditional command inside an inactive conditional, so
-// current_exception && did_emsg is not a reliant test that the exception
-// currently being thrown is an error exception.) - The macros can be defined
-// as expressions checking for a variable that is allowed to be changed during
-// execution of a script.
+// (unskipped) conditional command inside an inactive conditional, so did_throw
+// && did_emsg is not a reliant test that the exception currently being thrown
+// is an error exception.) - The macros can be defined as expressions checking
+// for a variable that is allowed to be changed during execution of a script.
// Values used for the Vim release.
#define THROW_ON_ERROR true
@@ -71,7 +70,7 @@
#define CHECK_SKIP \
(did_emsg \
|| got_int \
- || current_exception \
+ || did_throw \
|| (cstack->cs_idx > 0 \
&& !(cstack->cs_flags[cstack->cs_idx - 1] & CSF_ACTIVE)))
@@ -82,14 +81,14 @@ static void discard_pending_return(typval_T *p)
/*
* When several errors appear in a row, setting "force_abort" is delayed until
- * the failing command returned. "cause_abort" is set to TRUE meanwhile, in
+ * the failing command returned. "cause_abort" is set to true meanwhile, in
* order to indicate that situation. This is useful when "force_abort" was set
* during execution of a function call from an expression: the aborting of the
* expression evaluation is done without producing any error messages, but all
* error messages on parsing errors during the expression evaluation are given
* (even if a try conditional is active).
*/
-static int cause_abort = FALSE;
+static int cause_abort = false;
/// @return true when immediately aborting on error, or when an interrupt
/// occurred or an exception was thrown but not caught.
@@ -104,7 +103,7 @@ static int cause_abort = FALSE;
/// value. "got_int" is also set by calling interrupt().
int aborting(void)
{
- return (did_emsg && force_abort) || got_int || current_exception;
+ return (did_emsg && force_abort) || got_int || did_throw;
}
/// The value of "force_abort" is temporarily reset by the first emsg() call
@@ -114,11 +113,11 @@ int aborting(void)
void update_force_abort(void)
{
if (cause_abort) {
- force_abort = TRUE;
+ force_abort = true;
}
}
-/// @return TRUE if a command with a subcommand resulting in "retcode" should
+/// @return true if a command with a subcommand resulting in "retcode" should
/// abort the script processing. Can be used to suppress an autocommand after
/// execution of a failing subcommand as long as the error message has not been
/// displayed and actually caused the abortion.
@@ -127,7 +126,7 @@ int should_abort(int retcode)
return (retcode == FAIL && trylevel != 0 && !emsg_silent) || aborting();
}
-/// @return TRUE if a function with the "abort" flag should not be considered
+/// @return true if a function with the "abort" flag should not be considered
/// ended on an error. This means that parsing commands is continued in order
/// to find finally clauses to be executed, and that some errors in skipped
/// commands are still reported.
@@ -151,8 +150,8 @@ int aborted_in_try(void)
bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
FUNC_ATTR_NONNULL_ALL
{
- struct msglist *elem;
- struct msglist **plist;
+ msglist_T *elem;
+ msglist_T **plist;
/*
* Do nothing when displaying the interrupt message or reporting an
@@ -175,7 +174,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
*/
if (!did_emsg) {
cause_abort = force_abort;
- force_abort = FALSE;
+ force_abort = false;
}
/*
@@ -186,7 +185,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
* currently throwing an exception, do nothing. The message text will
* then be stored to v:errmsg by emsg() without displaying it.
*/
- if (((trylevel == 0 && !cause_abort) || emsg_silent) && !current_exception) {
+ if (((trylevel == 0 && !cause_abort) || emsg_silent) && !did_throw) {
return false;
}
@@ -206,7 +205,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
* Ensure that all commands in nested function calls and sourced files
* are aborted immediately.
*/
- cause_abort = TRUE;
+ cause_abort = true;
/*
* When an exception is being thrown, some commands (like conditionals) are
@@ -217,7 +216,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
* exception currently being thrown to prevent it from being caught. Just
* execute finally clauses and terminate.
*/
- if (current_exception) {
+ if (did_throw) {
// When discarding an interrupt exception, reset got_int to prevent the
// same interrupt being converted to an exception again and discarding
// the error exception we are about to throw here.
@@ -254,7 +253,7 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
plist = &(*plist)->next;
}
- elem = xmalloc(sizeof(struct msglist));
+ elem = xmalloc(sizeof(msglist_T));
elem->msg = xstrdup(mesg);
elem->next = NULL;
elem->throw_msg = NULL;
@@ -275,20 +274,26 @@ bool cause_errthrow(const char *mesg, bool severe, bool *ignore)
(*msg_list)->throw_msg = tmsg;
}
}
+
+ // Get the source name and lnum now, it may change before
+ // reaching do_errthrow().
+ elem->sfile = estack_sfile(ESTACK_NONE);
+ elem->slnum = SOURCING_LNUM;
}
return true;
}
}
/// Free a "msg_list" and the messages it contains.
-static void free_msglist(struct msglist *l)
+static void free_msglist(msglist_T *l)
{
- struct msglist *messages, *next;
+ msglist_T *messages, *next;
messages = l;
while (messages != NULL) {
next = messages->next;
xfree(messages->msg);
+ xfree(messages->sfile);
xfree(messages);
messages = next;
}
@@ -312,8 +317,8 @@ void do_errthrow(cstack_T *cstack, char *cmdname)
* are aborted immediately.
*/
if (cause_abort) {
- cause_abort = FALSE;
- force_abort = TRUE;
+ cause_abort = false;
+ force_abort = true;
}
// If no exception is to be thrown or the conversion should be done after
@@ -328,7 +333,7 @@ void do_errthrow(cstack_T *cstack, char *cmdname)
if (cstack != NULL) {
do_throw(cstack);
} else {
- need_rethrow = TRUE;
+ need_rethrow = true;
}
}
*msg_list = NULL;
@@ -337,13 +342,13 @@ void do_errthrow(cstack_T *cstack, char *cmdname)
/// do_intthrow(): Replace the current exception by an interrupt or interrupt
/// exception if appropriate.
///
-/// @return TRUE if the current exception is discarded or,
-/// FALSE otherwise.
+/// @return true if the current exception is discarded or,
+/// false otherwise.
int do_intthrow(cstack_T *cstack)
{
// If no interrupt occurred or no try conditional is active and no exception
// is being thrown, do nothing (for compatibility of non-EH scripts).
- if (!got_int || (trylevel == 0 && !current_exception)) {
+ if (!got_int || (trylevel == 0 && !did_throw)) {
return false;
}
@@ -352,7 +357,7 @@ int do_intthrow(cstack_T *cstack)
// The interrupt aborts everything except for executing finally clauses.
// Discard any user or error or interrupt exception currently being
// thrown.
- if (current_exception) {
+ if (did_throw) {
discard_current_exception();
}
} else {
@@ -363,7 +368,7 @@ int do_intthrow(cstack_T *cstack)
// will be terminated then. - If an interrupt exception is already
// being thrown, do nothing.
- if (current_exception) {
+ if (did_throw) {
if (current_exception->type == ET_INTERRUPT) {
return false;
}
@@ -389,7 +394,7 @@ char *get_exception_string(void *value, except_type_T type, char *cmdname, int *
if (type == ET_ERROR) {
*should_free = true;
- mesg = ((struct msglist *)value)->throw_msg;
+ mesg = ((msglist_T *)value)->throw_msg;
if (cmdname != NULL && *cmdname != NUL) {
size_t cmdlen = STRLEN(cmdname);
ret = xstrnsave("Vim(", 4 + cmdlen + 2 + STRLEN(mesg));
@@ -469,7 +474,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
if (type == ET_ERROR) {
// Store the original message and prefix the exception value with
// "Vim:" or, if a command name is given, "Vim(cmdname):".
- excp->messages = (struct msglist *)value;
+ excp->messages = (msglist_T *)value;
}
excp->value = get_exception_string(value, type, cmdname, &should_free);
@@ -478,20 +483,30 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
}
excp->type = type;
- excp->throw_name = xstrdup(sourcing_name == NULL ? "" : sourcing_name);
- excp->throw_lnum = sourcing_lnum;
+ if (type == ET_ERROR && ((msglist_T *)value)->sfile != NULL) {
+ msglist_T *entry = (msglist_T *)value;
+ excp->throw_name = entry->sfile;
+ entry->sfile = NULL;
+ excp->throw_lnum = entry->slnum;
+ } else {
+ excp->throw_name = estack_sfile(ESTACK_NONE);
+ if (excp->throw_name == NULL) {
+ excp->throw_name = xstrdup("");
+ }
+ excp->throw_lnum = SOURCING_LNUM;
+ }
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
} else {
verbose_enter();
}
- ++no_wait_return;
+ no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ msg_scroll = true; // always scroll up, don't overwrite
}
smsg(_("Exception thrown: %s"), excp->value);
msg_puts("\n"); // don't overwrite this either
@@ -499,7 +514,7 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
if (debug_break_level > 0 || *p_vfile == NUL) {
cmdline_row = msg_row;
}
- --no_wait_return;
+ no_wait_return--;
if (debug_break_level > 0) {
msg_silent = save_msg_silent;
} else {
@@ -538,13 +553,13 @@ static void discard_exception(except_T *excp, bool was_finished)
saved_IObuff = (char *)vim_strsave(IObuff);
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
} else {
verbose_enter();
}
- ++no_wait_return;
+ no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ msg_scroll = true; // always scroll up, don't overwrite
}
smsg(was_finished ? _("Exception finished: %s")
: _("Exception discarded: %s"),
@@ -580,6 +595,7 @@ void discard_current_exception(void)
}
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
+ did_throw = false;
need_rethrow = false;
}
@@ -606,13 +622,13 @@ static void catch_exception(except_T *excp)
int save_msg_silent = msg_silent;
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
} else {
verbose_enter();
}
- ++no_wait_return;
+ no_wait_return++;
if (debug_break_level > 0 || *p_vfile == NUL) {
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ msg_scroll = true; // always scroll up, don't overwrite
}
smsg(_("Exception caught: %s"), excp->value);
msg_puts("\n"); // don't overwrite this either
@@ -620,7 +636,7 @@ static void catch_exception(except_T *excp)
if (debug_break_level > 0 || *p_vfile == NUL) {
cmdline_row = msg_row;
}
- --no_wait_return;
+ no_wait_return--;
if (debug_break_level > 0) {
msg_silent = save_msg_silent;
} else {
@@ -710,14 +726,14 @@ static void report_pending(int action, int pending, void *value)
break;
case CSTP_RETURN:
// ":return" command producing value, allocated
- s = (char *)get_return_cmd(value);
+ s = get_return_cmd(value);
break;
default:
if (pending & CSTP_THROW) {
vim_snprintf((char *)IObuff, IOSIZE,
mesg, _("Exception"));
- mesg = (char *)concat_str(IObuff, (char_u *)": %s");
+ mesg = concat_str((char *)IObuff, ": %s");
s = ((except_T *)value)->value;
} else if ((pending & CSTP_ERROR) && (pending & CSTP_INTERRUPT)) {
s = _("Error and interrupt");
@@ -730,14 +746,14 @@ static void report_pending(int action, int pending, void *value)
save_msg_silent = msg_silent;
if (debug_break_level > 0) {
- msg_silent = FALSE; // display messages
+ msg_silent = false; // display messages
}
- ++no_wait_return;
- msg_scroll = TRUE; // always scroll up, don't overwrite
+ no_wait_return++;
+ msg_scroll = true; // always scroll up, don't overwrite
smsg(mesg, s);
msg_puts("\n"); // don't overwrite this either
cmdline_row = msg_row;
- --no_wait_return;
+ no_wait_return--;
if (debug_break_level > 0) {
msg_silent = save_msg_silent;
}
@@ -814,7 +830,7 @@ void ex_if(exarg_T *eap)
if (cstack->cs_idx == CSTACK_LEN - 1) {
eap->errmsg = _("E579: :if nesting too deep");
} else {
- ++cstack->cs_idx;
+ cstack->cs_idx++;
cstack->cs_flags[cstack->cs_idx] = 0;
skip = CHECK_SKIP;
@@ -854,7 +870,7 @@ void ex_endif(exarg_T *eap)
(void)do_intthrow(eap->cstack);
}
- --eap->cstack->cs_idx;
+ eap->cstack->cs_idx--;
}
}
@@ -948,8 +964,8 @@ void ex_while(exarg_T *eap)
* cstack entry.
*/
if ((cstack->cs_lflags & CSL_HAD_LOOP) == 0) {
- ++cstack->cs_idx;
- ++cstack->cs_looplevel;
+ cstack->cs_idx++;
+ cstack->cs_looplevel++;
cstack->cs_line[cstack->cs_idx] = -1;
}
cstack->cs_flags[cstack->cs_idx] =
@@ -971,7 +987,7 @@ void ex_while(exarg_T *eap)
// Jumping here from a ":continue" or ":endfor": use the
// previously evaluated list.
fi = cstack->cs_forinfo[cstack->cs_idx];
- error = FALSE;
+ error = false;
} else {
// Evaluate the argument and get the info in a structure.
fi = eval_for_line(eap->arg, &error, &eap->nextcmd, skip);
@@ -982,7 +998,7 @@ void ex_while(exarg_T *eap)
if (!error && fi != NULL && !skip) {
result = next_for_item(fi, eap->arg);
} else {
- result = FALSE;
+ result = false;
}
if (!result) {
@@ -1102,7 +1118,7 @@ void ex_endwhile(exarg_T *eap)
eap->errmsg = _(e_endtry);
}
// Try to find the matching ":while" and report what's missing.
- for (idx = cstack->cs_idx; idx > 0; --idx) {
+ for (idx = cstack->cs_idx; idx > 0; idx--) {
fl = cstack->cs_flags[idx];
if ((fl & CSF_TRY) && !(fl & CSF_FINALLY)) {
// Give up at a try conditional not in its finally clause.
@@ -1115,7 +1131,7 @@ void ex_endwhile(exarg_T *eap)
}
}
// Cleanup and rewind all contained (and unclosed) conditionals.
- (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, FALSE);
+ (void)cleanup_conditionals(cstack, CSF_WHILE | CSF_FOR, false);
rewind_conditionals(cstack, idx, CSF_TRY, &cstack->cs_trylevel);
} else if (cstack->cs_flags[cstack->cs_idx] & CSF_TRUE
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)
@@ -1170,7 +1186,7 @@ void ex_throw(exarg_T *eap)
void do_throw(cstack_T *cstack)
{
int idx;
- int inactivate_try = FALSE;
+ int inactivate_try = false;
//
// Cleanup and deactivate up to the next surrounding try conditional that
@@ -1183,14 +1199,14 @@ void do_throw(cstack_T *cstack)
//
#ifndef THROW_ON_ERROR_TRUE
if (did_emsg && !THROW_ON_ERROR) {
- inactivate_try = TRUE;
- did_emsg = FALSE;
+ inactivate_try = true;
+ did_emsg = false;
}
#endif
#ifndef THROW_ON_INTERRUPT_TRUE
if (got_int && !THROW_ON_INTERRUPT) {
- inactivate_try = TRUE;
- got_int = FALSE;
+ inactivate_try = true;
+ got_int = false;
}
#endif
idx = cleanup_conditionals(cstack, 0, inactivate_try);
@@ -1221,6 +1237,8 @@ void do_throw(cstack_T *cstack)
cstack->cs_flags[idx] &= ~CSF_ACTIVE;
cstack->cs_exception[idx] = current_exception;
}
+
+ did_throw = true;
}
/// Handle ":try"
@@ -1232,8 +1250,8 @@ void ex_try(exarg_T *eap)
if (cstack->cs_idx == CSTACK_LEN - 1) {
eap->errmsg = _("E601: :try nesting too deep");
} else {
- ++cstack->cs_idx;
- ++cstack->cs_trylevel;
+ cstack->cs_idx++;
+ cstack->cs_trylevel++;
cstack->cs_flags[cstack->cs_idx] = CSF_TRY;
cstack->cs_pending[cstack->cs_idx] = CSTP_NONE;
@@ -1298,7 +1316,7 @@ void ex_catch(exarg_T *eap)
eap->errmsg = get_end_emsg(cstack);
skip = true;
}
- for (idx = cstack->cs_idx; idx > 0; --idx) {
+ for (idx = cstack->cs_idx; idx > 0; idx--) {
if (cstack->cs_flags[idx] & CSF_TRY) {
break;
}
@@ -1317,10 +1335,10 @@ void ex_catch(exarg_T *eap)
if (ends_excmd(*eap->arg)) { // no argument, catch all errors
pat = ".*";
end = NULL;
- eap->nextcmd = (char *)find_nextcmd((char_u *)eap->arg);
+ eap->nextcmd = find_nextcmd(eap->arg);
} else {
pat = eap->arg + 1;
- end = (char *)skip_regexp((char_u *)pat, *eap->arg, true, NULL);
+ end = skip_regexp(pat, *eap->arg, true, NULL);
}
if (!give_up) {
@@ -1329,7 +1347,7 @@ void ex_catch(exarg_T *eap)
* corresponding try block never got active (because of an inactive
* surrounding conditional or after an error or interrupt or throw).
*/
- if (!current_exception || !(cstack->cs_flags[idx] & CSF_TRUE)) {
+ if (!did_throw || !(cstack->cs_flags[idx] & CSF_TRUE)) {
skip = true;
}
@@ -1360,7 +1378,7 @@ void ex_catch(exarg_T *eap)
*end = NUL;
}
save_cpo = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
// Disable error messages, it will make current exception
// invalid
emsg_off++;
@@ -1390,10 +1408,10 @@ void ex_catch(exarg_T *eap)
}
if (caught) {
- // Make this ":catch" clause active and reset did_emsg and got_int.
- // Put the exception on the caught stack.
+ // Make this ":catch" clause active and reset did_emsg, got_int,
+ // and did_throw. Put the exception on the caught stack.
cstack->cs_flags[idx] |= CSF_ACTIVE | CSF_CAUGHT;
- did_emsg = got_int = false;
+ did_emsg = got_int = did_throw = false;
catch_exception((except_T *)cstack->cs_exception[idx]);
// It's mandatory that the current exception is stored in the cstack
// so that it can be discarded at the next ":catch", ":finally", or
@@ -1403,10 +1421,6 @@ void ex_catch(exarg_T *eap)
if (cstack->cs_exception[cstack->cs_idx] != current_exception) {
internal_error("ex_catch()");
}
- // Discarding current_exceptions happens based on what is stored in
- // cstack->cs_exception, *all* calls to discard_current_exception() are
- // (and must be) guarded by current_exception check.
- current_exception = NULL;
} else {
/*
* If there is a preceding catch clause and it caught the exception,
@@ -1418,12 +1432,12 @@ void ex_catch(exarg_T *eap)
* a ":continue", ":break", ":return", or ":finish", discard the
* pending action.
*/
- cleanup_conditionals(cstack, CSF_TRY, TRUE);
+ cleanup_conditionals(cstack, CSF_TRY, true);
}
}
if (end != NULL) {
- eap->nextcmd = (char *)find_nextcmd((char_u *)end);
+ eap->nextcmd = find_nextcmd(end);
}
}
@@ -1431,7 +1445,7 @@ void ex_catch(exarg_T *eap)
void ex_finally(exarg_T *eap)
{
int idx;
- int skip = FALSE;
+ int skip = false;
int pending = CSTP_NONE;
cstack_T *const cstack = eap->cstack;
@@ -1440,7 +1454,7 @@ void ex_finally(exarg_T *eap)
} else {
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
eap->errmsg = get_end_emsg(cstack);
- for (idx = cstack->cs_idx - 1; idx > 0; --idx) {
+ for (idx = cstack->cs_idx - 1; idx > 0; idx--) {
if (cstack->cs_flags[idx] & CSF_TRY) {
break;
}
@@ -1461,14 +1475,12 @@ void ex_finally(exarg_T *eap)
rewind_conditionals(cstack, idx, CSF_WHILE | CSF_FOR,
&cstack->cs_looplevel);
- /*
- * Don't do something when the corresponding try block never got active
- * (because of an inactive surrounding conditional or after an error or
- * interrupt or throw) or for a ":finally" without ":try" or a multiple
- * ":finally". After every other error (did_emsg or the conditional
- * errors detected above) or after an interrupt (got_int) or an
- * exception (current_exception), the finally clause must be executed.
- */
+ // Don't do something when the corresponding try block never got active
+ // (because of an inactive surrounding conditional or after an error or
+ // interrupt or throw) or for a ":finally" without ":try" or a multiple
+ // ":finally". After every other error (did_emsg or the conditional
+ // errors detected above) or after an interrupt (got_int) or an
+ // exception (did_throw), the finally clause must be executed.
skip = !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
if (!skip) {
@@ -1491,24 +1503,22 @@ void ex_finally(exarg_T *eap)
* ":continue", ":break", ":finish", or ":return" from the preceding
* try block or catch clause.
*/
- cleanup_conditionals(cstack, CSF_TRY, FALSE);
-
- /*
- * Make did_emsg, got_int, current_exception pending. If set, they
- * overrule a pending ":continue", ":break", ":return", or ":finish".
- * Then we have particularly to discard a pending return value (as done
- * by the call to cleanup_conditionals() above when did_emsg or
- * got_int is set). The pending values are restored by the
- * ":endtry", except if there is a new error, interrupt, exception,
- * ":continue", ":break", ":return", or ":finish" in the following
- * finally clause. A missing ":endwhile", ":endfor" or ":endif"
- * detected here is treated as if did_emsg and current_exception had
- * already been set, respectively in case that the error is not
- * converted to an exception, current_exception had already been unset.
- * We must not set did_emsg here since that would suppress the
- * error message.
- */
- if (pending == CSTP_ERROR || did_emsg || got_int || current_exception) {
+ cleanup_conditionals(cstack, CSF_TRY, false);
+
+ // Make did_emsg, got_int, did_throw pending. If set, they overrule
+ // a pending ":continue", ":break", ":return", or ":finish". Then
+ // we have particularly to discard a pending return value (as done
+ // by the call to cleanup_conditionals() above when did_emsg or
+ // got_int is set). The pending values are restored by the
+ // ":endtry", except if there is a new error, interrupt, exception,
+ // ":continue", ":break", ":return", or ":finish" in the following
+ // finally clause. A missing ":endwhile", ":endfor" or ":endif"
+ // detected here is treated as if did_emsg and did_throw had
+ // already been set, respectively in case that the error is not
+ // converted to an exception, did_throw had already been unset.
+ // We must not set did_emsg here since that would suppress the
+ // error message.
+ if (pending == CSTP_ERROR || did_emsg || got_int || did_throw) {
if (cstack->cs_pending[cstack->cs_idx] == CSTP_RETURN) {
report_discard_pending(CSTP_RETURN,
cstack->cs_rettv[cstack->cs_idx]);
@@ -1517,7 +1527,7 @@ void ex_finally(exarg_T *eap)
if (pending == CSTP_ERROR && !did_emsg) {
pending |= (THROW_ON_ERROR ? CSTP_THROW : 0);
} else {
- pending |= (current_exception ? CSTP_THROW : 0);
+ pending |= (did_throw ? CSTP_THROW : 0);
}
pending |= did_emsg ? CSTP_ERROR : 0;
pending |= got_int ? CSTP_INTERRUPT : 0;
@@ -1531,19 +1541,16 @@ void ex_finally(exarg_T *eap)
// exception. When emsg() is called for a missing ":endif" or
// a missing ":endwhile"/":endfor" detected here, the
// exception will be discarded.
- if (current_exception
- && cstack->cs_exception[cstack->cs_idx] != current_exception) {
+ if (did_throw && cstack->cs_exception[cstack->cs_idx] != current_exception) {
internal_error("ex_finally()");
}
}
- /*
- * Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
- * got_int, and current_exception and make the finally clause active.
- * This will happen after emsg() has been called for a missing
- * ":endif" or a missing ":endwhile"/":endfor" detected here, so
- * that the following finally clause will be executed even then.
- */
+ // Set CSL_HAD_FINA, so do_cmdline() will reset did_emsg,
+ // got_int, and did_throw and make the finally clause active.
+ // This will happen after emsg() has been called for a missing
+ // ":endif" or a missing ":endwhile"/":endfor" detected here, so
+ // that the following finally clause will be executed even then.
cstack->cs_lflags |= CSL_HAD_FINA;
}
}
@@ -1571,8 +1578,7 @@ void ex_endtry(exarg_T *eap)
// made inactive by a ":continue", ":break", ":return", or ":finish" in
// the finally clause. The latter case need not be tested since then
// anything pending has already been discarded.
- bool skip = did_emsg || got_int || current_exception
- || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
+ bool skip = did_emsg || got_int || did_throw || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE);
if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
eap->errmsg = get_end_emsg(cstack);
@@ -1586,14 +1592,12 @@ void ex_endtry(exarg_T *eap)
&cstack->cs_looplevel);
skip = true;
- /*
- * If an exception is being thrown, discard it to prevent it from
- * being rethrown at the end of this function. It would be
- * discarded by the error message, anyway. Resets current_exception.
- * This does not affect the script termination due to the error
- * since "trylevel" is decremented after emsg() has been called.
- */
- if (current_exception) {
+ // If an exception is being thrown, discard it to prevent it from
+ // being rethrown at the end of this function. It would be
+ // discarded by the error message, anyway. Resets did_throw.
+ // This does not affect the script termination due to the error
+ // since "trylevel" is decremented after emsg() has been called.
+ if (did_throw) {
discard_current_exception();
}
@@ -1608,7 +1612,7 @@ void ex_endtry(exarg_T *eap)
* a finally clause, we need to rethrow it after closing the try
* conditional.
*/
- if (current_exception
+ if (did_throw
&& (cstack->cs_flags[idx] & CSF_TRUE)
&& !(cstack->cs_flags[idx] & CSF_FINALLY)) {
rethrow = true;
@@ -1632,10 +1636,10 @@ void ex_endtry(exarg_T *eap)
if (got_int) {
skip = true;
(void)do_intthrow(cstack);
- // The do_intthrow() call may have reset current_exception or
+ // The do_intthrow() call may have reset did_throw or
// cstack->cs_pending[idx].
rethrow = false;
- if (current_exception && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
+ if (did_throw && !(cstack->cs_flags[idx] & CSF_FINALLY)) {
rethrow = true;
}
}
@@ -1667,7 +1671,7 @@ void ex_endtry(exarg_T *eap)
* after errors except when this ":endtry" is not within a ":try".
* Restore "emsg_silent" if it has been reset by this try conditional.
*/
- (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, TRUE);
+ (void)cleanup_conditionals(cstack, CSF_TRY | CSF_SILENT, true);
if (cstack->cs_idx >= 0 && (cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) {
cstack->cs_idx--;
@@ -1696,16 +1700,16 @@ void ex_endtry(exarg_T *eap)
ex_break(eap);
break;
case CSTP_RETURN:
- do_return(eap, FALSE, FALSE, rettv);
+ do_return(eap, false, false, rettv);
break;
case CSTP_FINISH:
- do_finish(eap, FALSE);
+ do_finish(eap, false);
break;
// When the finally clause was entered due to an error,
// interrupt or throw (as opposed to a ":continue", ":break",
// ":return", or ":finish"), restore the pending values of
- // did_emsg, got_int, and current_exception. This is skipped, if there
+ // did_emsg, got_int, and did_throw. This is skipped, if there
// was a new error, interrupt, throw, ":continue", ":break",
// ":return", or ":finish". in the finally clause.
default:
@@ -1752,35 +1756,32 @@ void enter_cleanup(cleanup_T *csp)
{
int pending = CSTP_NONE;
- /*
- * Postpone did_emsg, got_int, current_exception. The pending values will be
- * restored by leave_cleanup() except if there was an aborting error,
- * interrupt, or uncaught exception after this function ends.
- */
- if (did_emsg || got_int || current_exception || need_rethrow) {
+ // Postpone did_emsg, got_int, did_throw. The pending values will be
+ // restored by leave_cleanup() except if there was an aborting error,
+ // interrupt, or uncaught exception after this function ends.
+ if (did_emsg || got_int || did_throw || need_rethrow) {
csp->pending = (did_emsg ? CSTP_ERROR : 0)
| (got_int ? CSTP_INTERRUPT : 0)
- | (current_exception ? CSTP_THROW : 0)
+ | (did_throw ? CSTP_THROW : 0)
| (need_rethrow ? CSTP_THROW : 0);
- // If we are currently throwing an exception, save it as well. On an error
- // not yet converted to an exception, update "force_abort" and reset
- // "cause_abort" (as do_errthrow() would do). This is needed for the
- // do_cmdline() call that is going to be made for autocommand execution. We
- // need not save *msg_list because there is an extra instance for every call
- // of do_cmdline(), anyway.
- if (current_exception || need_rethrow) {
+ // If we are currently throwing an exception (did_throw), save it as
+ // well. On an error not yet converted to an exception, update
+ // "force_abort" and reset "cause_abort" (as do_errthrow() would do).
+ // This is needed for the do_cmdline() call that is going to be made
+ // for autocommand execution. We need not save *msg_list because
+ // there is an extra instance for every call of do_cmdline(), anyway.
+ if (did_throw || need_rethrow) {
csp->exception = current_exception;
current_exception = NULL;
} else {
csp->exception = NULL;
if (did_emsg) {
force_abort |= cause_abort;
- cause_abort = FALSE;
+ cause_abort = false;
}
}
- did_emsg = got_int = need_rethrow = false;
- current_exception = NULL;
+ did_emsg = got_int = did_throw = need_rethrow = false;
// Report if required by the 'verbose' option or when debugging.
report_make_pending(pending, csp->exception);
@@ -1847,10 +1848,10 @@ void leave_cleanup(cleanup_T *csp)
// enter_cleanup() was called, let "cause_abort" take the part of
// "force_abort" (as done by cause_errthrow()).
cause_abort = force_abort;
- force_abort = FALSE;
+ force_abort = false;
}
- // Restore the pending values of did_emsg, got_int, and current_exception.
+ // Restore the pending values of did_emsg, got_int, and did_throw.
if (pending & CSTP_ERROR) {
did_emsg = true;
}
@@ -1858,7 +1859,7 @@ void leave_cleanup(cleanup_T *csp)
got_int = true;
}
if (pending & CSTP_THROW) {
- need_rethrow = true; // current_exception will be set by do_one_cmd()
+ need_rethrow = true; // did_throw will be set by do_one_cmd()
}
// Report if required by the 'verbose' option or when debugging.
@@ -1879,7 +1880,7 @@ void leave_cleanup(cleanup_T *csp)
/// inactive itself (a try conditional not in its finally
/// clause possibly find before is always made inactive).
///
-/// If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT, the saved
+/// If "inclusive" is true and "searched_cond" is CSF_TRY|CSF_SILENT, the saved
/// former value of "emsg_silent", if reset when the try conditional finally
/// reached was entered, is restored (used by ex_endtry()). This is normally
/// done only when such a try conditional is left.
@@ -1888,9 +1889,9 @@ void leave_cleanup(cleanup_T *csp)
int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
{
int idx;
- int stop = FALSE;
+ int stop = false;
- for (idx = cstack->cs_idx; idx >= 0; --idx) {
+ for (idx = cstack->cs_idx; idx >= 0; idx--) {
if (cstack->cs_flags[idx] & CSF_TRY) {
/*
* Discard anything pending in a finally clause and continue the
@@ -1952,20 +1953,20 @@ int cleanup_conditionals(cstack_T *cstack, int searched_cond, int inclusive)
if (searched_cond == 0 && !inclusive) {
break;
}
- stop = TRUE;
+ stop = true;
}
}
}
// Stop on the searched conditional type (even when the surrounding
// conditional is not active or something has been made pending).
- // If "inclusive" is TRUE and "searched_cond" is CSF_TRY|CSF_SILENT,
+ // If "inclusive" is true and "searched_cond" is CSF_TRY|CSF_SILENT,
// check first whether "emsg_silent" needs to be restored.
if (cstack->cs_flags[idx] & searched_cond) {
if (!inclusive) {
break;
}
- stop = TRUE;
+ stop = true;
}
cstack->cs_flags[idx] &= ~CSF_ACTIVE;
if (stop && searched_cond != (CSF_TRY | CSF_SILENT)) {
@@ -2020,7 +2021,7 @@ void rewind_conditionals(cstack_T *cstack, int idx, int cond_type, int *cond_lev
if (cstack->cs_flags[cstack->cs_idx] & CSF_FOR) {
free_for_info(cstack->cs_forinfo[cstack->cs_idx]);
}
- --cstack->cs_idx;
+ cstack->cs_idx--;
}
}
@@ -2030,7 +2031,7 @@ void ex_endfunction(exarg_T *eap)
emsg(_("E193: :endfunction not inside a function"));
}
-/// @return TRUE if the string "p" looks like a ":while" or ":for" command.
+/// @return true if the string "p" looks like a ":while" or ":for" command.
int has_loop_cmd(char *p)
{
int len;
@@ -2038,7 +2039,7 @@ int has_loop_cmd(char *p)
// skip modifiers, white space and ':'
for (;;) {
while (*p == ' ' || *p == '\t' || *p == ':') {
- ++p;
+ p++;
}
len = modifier_len(p);
if (len == 0) {
@@ -2048,7 +2049,7 @@ int has_loop_cmd(char *p)
}
if ((p[0] == 'w' && p[1] == 'h')
|| (p[0] == 'f' && p[1] == 'o' && p[2] == 'r')) {
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
diff --git a/src/nvim/ex_eval.h b/src/nvim/ex_eval.h
index 235875fb91..9e3ac5e9c1 100644
--- a/src/nvim/ex_eval.h
+++ b/src/nvim/ex_eval.h
@@ -2,81 +2,7 @@
#define NVIM_EX_EVAL_H
#include "nvim/ex_cmds_defs.h" // for exarg_T
-#include "nvim/pos.h" // for linenr_T
-
-/* There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if"
- * was used. */
-#define CSF_TRUE 0x0001 // condition was TRUE
-#define CSF_ACTIVE 0x0002 // current state is active
-#define CSF_ELSE 0x0004 // ":else" has been passed
-#define CSF_WHILE 0x0008 // is a ":while"
-#define CSF_FOR 0x0010 // is a ":for"
-
-#define CSF_TRY 0x0100 // is a ":try"
-#define CSF_FINALLY 0x0200 // ":finally" has been passed
-#define CSF_THROWN 0x0800 // exception thrown to this try conditional
-#define CSF_CAUGHT 0x1000 // exception caught by this try conditional
-#define CSF_FINISHED 0x2000 // CSF_CAUGHT was handled by finish_exception()
-#define CSF_SILENT 0x4000 // "emsg_silent" reset by ":try"
-// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset
-// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set.
-
-/*
- * What's pending for being reactivated at the ":endtry" of this try
- * conditional:
- */
-#define CSTP_NONE 0 // nothing pending in ":finally" clause
-#define CSTP_ERROR 1 // an error is pending
-#define CSTP_INTERRUPT 2 // an interrupt is pending
-#define CSTP_THROW 4 // a throw is pending
-#define CSTP_BREAK 8 // ":break" is pending
-#define CSTP_CONTINUE 16 // ":continue" is pending
-#define CSTP_RETURN 24 // ":return" is pending
-#define CSTP_FINISH 32 // ":finish" is pending
-
-/*
- * A list of error messages that can be converted to an exception. "throw_msg"
- * is only set in the first element of the list. Usually, it points to the
- * original message stored in that element, but sometimes it points to a later
- * message in the list. See cause_errthrow() below.
- */
-struct msglist {
- char *msg; // original message
- char *throw_msg; // msg to throw: usually original one
- struct msglist *next; // next of several messages in a row
-};
-
-// The exception types.
-typedef enum {
- ET_USER, // exception caused by ":throw" command
- ET_ERROR, // error exception
- ET_INTERRUPT, // interrupt exception triggered by Ctrl-C
-} except_type_T;
-
-/*
- * Structure describing an exception.
- * (don't use "struct exception", it's used by the math library).
- */
-typedef struct vim_exception except_T;
-struct vim_exception {
- except_type_T type; // exception type
- char *value; // exception value
- struct msglist *messages; // message(s) causing error exception
- char *throw_name; // name of the throw point
- linenr_T throw_lnum; // line number of the throw point
- except_T *caught; // next exception on the caught stack
-};
-
-/*
- * Structure to save the error/interrupt/exception state between calls to
- * enter_cleanup() and leave_cleanup(). Must be allocated as an automatic
- * variable by the (common) caller of these functions.
- */
-typedef struct cleanup_stuff cleanup_T;
-struct cleanup_stuff {
- int pending; // error/interrupt/exception state
- except_T *exception; // exception value
-};
+#include "nvim/ex_eval_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_eval.h.generated.h"
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
new file mode 100644
index 0000000000..9da0c9ad12
--- /dev/null
+++ b/src/nvim/ex_eval_defs.h
@@ -0,0 +1,79 @@
+#ifndef NVIM_EX_EVAL_DEFS_H
+#define NVIM_EX_EVAL_DEFS_H
+
+#include "nvim/pos.h" // for linenr_T
+
+/// There is no CSF_IF, the lack of CSF_WHILE, CSF_FOR and CSF_TRY means ":if"
+/// was used.
+enum {
+ CSF_TRUE = 0x0001, ///< condition was TRUE
+ CSF_ACTIVE = 0x0002, ///< current state is active
+ CSF_ELSE = 0x0004, ///< ":else" has been passed
+ CSF_WHILE = 0x0008, ///< is a ":while"
+ CSF_FOR = 0x0010, ///< is a ":for"
+
+ CSF_TRY = 0x0100, ///< is a ":try"
+ CSF_FINALLY = 0x0200, ///< ":finally" has been passed
+ CSF_THROWN = 0x0800, ///< exception thrown to this try conditional
+ CSF_CAUGHT = 0x1000, ///< exception caught by this try conditional
+ CSF_FINISHED = 0x2000, ///< CSF_CAUGHT was handled by finish_exception()
+ CSF_SILENT = 0x4000, ///< "emsg_silent" reset by ":try"
+};
+// Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset
+// (an ":if"), and CSF_SILENT is only used when CSF_TRY is set.
+
+/// What's pending for being reactivated at the ":endtry" of this try
+/// conditional:
+enum {
+ CSTP_NONE = 0, ///< nothing pending in ":finally" clause
+ CSTP_ERROR = 1, ///< an error is pending
+ CSTP_INTERRUPT = 2, ///< an interrupt is pending
+ CSTP_THROW = 4, ///< a throw is pending
+ CSTP_BREAK = 8, ///< ":break" is pending
+ CSTP_CONTINUE = 16, ///< ":continue" is pending
+ CSTP_RETURN = 24, ///< ":return" is pending
+ CSTP_FINISH = 32, ///< ":finish" is pending
+};
+
+/// A list of error messages that can be converted to an exception. "throw_msg"
+/// is only set in the first element of the list. Usually, it points to the
+/// original message stored in that element, but sometimes it points to a later
+/// message in the list. See cause_errthrow().
+typedef struct msglist msglist_T;
+struct msglist {
+ char *msg; ///< original message, allocated
+ char *throw_msg; ///< msg to throw: usually original one
+ char *sfile; ///< value from estack_sfile(), allocated
+ linenr_T slnum; ///< line number for "sfile"
+ msglist_T *next; ///< next of several messages in a row
+};
+
+/// The exception types.
+typedef enum {
+ ET_USER, ///< exception caused by ":throw" command
+ ET_ERROR, ///< error exception
+ ET_INTERRUPT, ///< interrupt exception triggered by Ctrl-C
+} except_type_T;
+
+/// Structure describing an exception.
+/// (don't use "struct exception", it's used by the math library).
+typedef struct vim_exception except_T;
+struct vim_exception {
+ except_type_T type; ///< exception type
+ char *value; ///< exception value
+ msglist_T *messages; ///< message(s) causing error exception
+ char *throw_name; ///< name of the throw point
+ linenr_T throw_lnum; ///< line number of the throw point
+ except_T *caught; ///< next exception on the caught stack
+};
+
+/// Structure to save the error/interrupt/exception state between calls to
+/// enter_cleanup() and leave_cleanup(). Must be allocated as an automatic
+/// variable by the (common) caller of these functions.
+typedef struct cleanup_stuff cleanup_T;
+struct cleanup_stuff {
+ int pending; ///< error/interrupt/exception state
+ except_T *exception; ///< exception value
+};
+
+#endif // NVIM_EX_EVAL_DEFS_H
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 07a0e68884..0998bdd867 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -12,23 +12,22 @@
#include <string.h>
#include "nvim/api/extmark.h"
-#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
#include "nvim/arabic.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/eval/funcs.h"
-#include "nvim/eval/userfunc.h"
#include "nvim/event/loop.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
@@ -37,113 +36,47 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
-#include "nvim/if_cscope.h"
#include "nvim/indent.h"
#include "nvim/keycodes.h"
#include "nvim/lib/kvec.h"
#include "nvim/log.h"
-#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
-#include "nvim/os/os.h"
#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
-#include "nvim/sign.h"
#include "nvim/state.h"
#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/tag.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
+#include "nvim/usercmd.h"
#include "nvim/vim.h"
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
#include "nvim/window.h"
-/// Command-line colors: one chunk
-///
-/// Defines a region which has the same highlighting.
-typedef struct {
- int start; ///< Colored chunk start.
- int end; ///< Colored chunk end (exclusive, > start).
- int attr; ///< Highlight attr.
-} CmdlineColorChunk;
-
-/// Command-line colors
-///
-/// Holds data about all colors.
-typedef kvec_t(CmdlineColorChunk) CmdlineColors;
-
-/// Command-line coloring
-///
-/// Holds both what are the colors and what have been colored. Latter is used to
-/// suppress unnecessary calls to coloring callbacks.
-typedef struct {
- unsigned prompt_id; ///< ID of the prompt which was colored last.
- char *cmdbuff; ///< What exactly was colored last time or NULL.
- CmdlineColors colors; ///< Last colors.
-} ColoredCmdline;
-
-/// Keeps track how much state must be sent to external ui.
-typedef enum {
- kCmdRedrawNone,
- kCmdRedrawPos,
- kCmdRedrawAll,
-} CmdRedraw;
-
-// Variables shared between getcmdline(), redrawcmdline() and others.
-// These need to be saved when using CTRL-R |, that's why they are in a
-// structure.
-struct cmdline_info {
- char_u *cmdbuff; // pointer to command line buffer
- int cmdbufflen; // length of cmdbuff
- int cmdlen; // number of chars in command line
- int cmdpos; // current cursor position
- int cmdspos; // cursor column on screen
- int cmdfirstc; // ':', '/', '?', '=', '>' or NUL
- int cmdindent; // number of spaces before cmdline
- char_u *cmdprompt; // message in front of cmdline
- int cmdattr; // attributes for prompt
- int overstrike; // Typing mode on the command line. Shared by
- // getcmdline() and put_on_cmdline().
- expand_T *xpc; // struct being used for expansion, xp_pattern
- // may point into cmdbuff
- int xp_context; // type of expansion
- char_u *xp_arg; // user-defined expansion arg
- int input_fn; // when TRUE Invoked for input() function
- unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
- Callback highlight_callback; ///< Callback used for coloring user input.
- ColoredCmdline last_colors; ///< Last cmdline colors
- int level; // current cmdline level
- struct cmdline_info *prev_ccline; ///< pointer to saved cmdline state
- char special_char; ///< last putcmdline char (used for redraws)
- bool special_shift; ///< shift of last putcmdline char
- CmdRedraw redraw_state; ///< needed redraw for external cmdline
-};
-
/// Last value of prompt_id, incremented when doing new prompt
static unsigned last_prompt_id = 0;
-// Struct to store the viewstate during 'incsearch' highlighting.
+// Struct to store the viewstate during 'incsearch' highlighting and 'inccommand' preview.
typedef struct {
colnr_T vs_curswant;
colnr_T vs_leftcol;
@@ -172,8 +105,8 @@ typedef struct command_line_state {
long count;
int indent;
int c;
- int gotesc; // TRUE when <ESC> just typed
- int do_abbr; // when TRUE check for abbr.
+ int gotesc; // true when <ESC> just typed
+ int do_abbr; // when true check for abbr.
char_u *lookfor; // string to match
int hiscnt; // current history line in use
int save_hiscnt; // history line before attempting
@@ -195,44 +128,49 @@ typedef struct command_line_state {
long *b_im_ptr;
} CommandLineState;
-typedef struct cmdline_info CmdlineInfo;
+typedef struct cmdpreview_win_info {
+ win_T *win;
+ pos_T save_w_cursor;
+ viewstate_T save_viewstate;
+ int save_w_p_cul;
+ int save_w_p_cuc;
+} CpWinInfo;
+
+typedef struct cmdpreview_buf_info {
+ buf_T *buf;
+ time_t save_b_u_time_cur;
+ long save_b_u_seq_cur;
+ u_header_T *save_b_u_newhead;
+ long save_b_p_ul;
+ int save_b_changed;
+ varnumber_T save_changedtick;
+} CpBufInfo;
+
+typedef struct cmdpreview_info {
+ kvec_t(CpWinInfo) win_info;
+ kvec_t(CpBufInfo) buf_info;
+ bool save_hls;
+ cmdmod_T save_cmdmod;
+ garray_T save_view;
+} CpInfo;
/// The current cmdline_info. It is initialized in getcmdline() and after that
/// used by other functions. When invoking getcmdline() recursively it needs
/// to be saved with save_cmdline() and restored with restore_cmdline().
-static struct cmdline_info ccline;
-
-static int cmd_showtail; // Only show path tail in lists ?
+static CmdlineInfo ccline;
static int new_cmdpos; // position set by set_cmdline_pos()
/// currently displayed block of context
static Array cmdline_block = ARRAY_DICT_INIT;
-/*
- * Type used by call_user_expand_func
- */
-typedef void *(*user_expand_func_T)(const char_u *, int, typval_T *);
-
-static histentry_T *(history[HIST_COUNT]) = { NULL, NULL, NULL, NULL, NULL };
-static int hisidx[HIST_COUNT] = { -1, -1, -1, -1, -1 }; // lastused entry
-static int hisnum[HIST_COUNT] = { 0, 0, 0, 0, 0 };
-// identifying (unique) number of newest history entry
-static int hislen = 0; // actual length of history tables
-
/// Flag for command_line_handle_key to ignore <C-c>
///
/// Used if it was received while processing highlight function in order for
/// user interrupting highlight function to not interrupt command-line.
static bool getln_interrupted_highlight = false;
-// "compl_match_array" points the currently displayed list of entries in the
-// popup menu. It is NULL when there is no popup menu.
-static pumitem_T *compl_match_array = NULL;
-static int compl_match_arraysize;
-// First column in cmdline of the matched item for completion.
-static int compl_startcol;
-static int compl_selected;
+static int cedit_key = -1; ///< key value of 'cedit' option
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.c.generated.h"
@@ -243,26 +181,26 @@ static long cmdpreview_ns = 0;
static int cmd_hkmap = 0; // Hebrew mapping during command line
-static void save_viewstate(viewstate_T *vs)
+static void save_viewstate(win_T *wp, viewstate_T *vs)
FUNC_ATTR_NONNULL_ALL
{
- vs->vs_curswant = curwin->w_curswant;
- vs->vs_leftcol = curwin->w_leftcol;
- vs->vs_topline = curwin->w_topline;
- vs->vs_topfill = curwin->w_topfill;
- vs->vs_botline = curwin->w_botline;
- vs->vs_empty_rows = curwin->w_empty_rows;
+ vs->vs_curswant = wp->w_curswant;
+ vs->vs_leftcol = wp->w_leftcol;
+ vs->vs_topline = wp->w_topline;
+ vs->vs_topfill = wp->w_topfill;
+ vs->vs_botline = wp->w_botline;
+ vs->vs_empty_rows = wp->w_empty_rows;
}
-static void restore_viewstate(viewstate_T *vs)
+static void restore_viewstate(win_T *wp, viewstate_T *vs)
FUNC_ATTR_NONNULL_ALL
{
- curwin->w_curswant = vs->vs_curswant;
- curwin->w_leftcol = vs->vs_leftcol;
- curwin->w_topline = vs->vs_topline;
- curwin->w_topfill = vs->vs_topfill;
- curwin->w_botline = vs->vs_botline;
- curwin->w_empty_rows = vs->vs_empty_rows;
+ wp->w_curswant = vs->vs_curswant;
+ wp->w_leftcol = vs->vs_leftcol;
+ wp->w_topline = vs->vs_topline;
+ wp->w_topfill = vs->vs_topfill;
+ wp->w_botline = vs->vs_botline;
+ wp->w_empty_rows = vs->vs_empty_rows;
}
static void init_incsearch_state(incsearch_state_T *s)
@@ -274,34 +212,8 @@ static void init_incsearch_state(incsearch_state_T *s)
clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
s->search_start = curwin->w_cursor;
- save_viewstate(&s->init_viewstate);
- save_viewstate(&s->old_viewstate);
-}
-
-/// Completion for |:checkhealth| command.
-///
-/// Given to ExpandGeneric() to obtain all available heathcheck names.
-/// @param[in] idx Index of the healthcheck item.
-/// @param[in] xp Not used.
-static char *get_healthcheck_names(expand_T *xp, int idx)
-{
- static Object names = OBJECT_INIT;
- static unsigned last_gen = 0;
-
- if (last_gen != last_prompt_id || last_gen == 0) {
- Array a = ARRAY_DICT_INIT;
- Error err = ERROR_INIT;
- Object res = nlua_exec(STATIC_CSTR_AS_STRING("return vim.health._complete()"), a, &err);
- api_clear_error(&err);
- api_free_object(names);
- names = res;
- last_gen = last_prompt_id;
- }
-
- if (names.type == kObjectTypeArray && idx < (int)names.data.array.size) {
- return names.data.array.items[idx].data.string.data;
- }
- return NULL;
+ save_viewstate(curwin, &s->init_viewstate);
+ save_viewstate(curwin, &s->old_viewstate);
}
// Return true when 'incsearch' highlighting is to be done.
@@ -316,7 +228,6 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
int delim;
char *end;
char *dummy;
- exarg_T ea;
pos_T save_cursor;
bool use_last_pat;
bool retval = false;
@@ -341,11 +252,12 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
}
emsg_off++;
- memset(&ea, 0, sizeof(ea));
- ea.line1 = 1;
- ea.line2 = 1;
- ea.cmd = (char *)ccline.cmdbuff;
- ea.addr_type = ADDR_LINES;
+ exarg_T ea = {
+ .line1 = 1,
+ .line2 = 1,
+ .cmd = (char *)ccline.cmdbuff,
+ .addr_type = ADDR_LINES,
+ };
cmdmod_T dummy_cmdmod;
parse_command_modifiers(&ea, &dummy, &dummy_cmdmod, true);
@@ -403,7 +315,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
p = skipwhite(p);
delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
*search_delim = delim;
- end = (char *)skip_regexp((char_u *)p, delim, p_magic, NULL);
+ end = skip_regexp(p, delim, p_magic, NULL);
use_last_pat = end == p && *end == delim;
if (end == p && !use_last_pat) {
@@ -458,7 +370,6 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
{
pos_T end_pos;
proftime_T tm;
- searchit_arg_T sia;
int skiplen, patlen;
char_u next_char;
char_u use_last_pat;
@@ -506,7 +417,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
if (patlen == 0 && !use_last_pat) {
found = 0;
set_no_hlsearch(true); // turn off previous highlight
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
} else {
int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
ui_busy_start();
@@ -521,8 +432,9 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
search_flags += SEARCH_START;
}
ccline.cmdbuff[skiplen + patlen] = NUL;
- memset(&sia, 0, sizeof(sia));
- sia.sa_tm = &tm;
+ searchit_arg_T sia = {
+ .sa_tm = &tm,
+ };
found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim,
ccline.cmdbuff + skiplen, count,
search_flags, &sia);
@@ -555,7 +467,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
// first restore the old curwin values, so the screen is
// positioned in the same way as the actual search command
- restore_viewstate(&s->old_viewstate);
+ restore_viewstate(curwin, &s->old_viewstate);
changed_cline_bef_curs();
update_topline(curwin);
@@ -578,7 +490,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
next_char = ccline.cmdbuff[skiplen + patlen];
ccline.cmdbuff[skiplen + patlen] = NUL;
if (empty_pattern(ccline.cmdbuff) && !no_hlsearch) {
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(true);
}
ccline.cmdbuff[skiplen + patlen] = next_char;
@@ -590,7 +502,7 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat
curwin->w_redr_status = true;
}
- update_screen(SOME_VALID);
+ update_screen(UPD_SOME_VALID);
highlight_match = false;
restore_last_search_pattern();
@@ -665,7 +577,7 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
}
curwin->w_cursor = s->search_start; // -V519
}
- restore_viewstate(&s->old_viewstate);
+ restore_viewstate(curwin, &s->old_viewstate);
highlight_match = false;
// by default search all lines
@@ -675,9 +587,9 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool
p_magic = s->magic_save;
validate_cursor(); // needed for TAB
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
if (call_update_screen) {
- update_screen(SOME_VALID);
+ update_screen(UPD_SOME_VALID);
}
}
}
@@ -701,7 +613,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
lastwin->w_p_so = 0;
set_option_value("ch", 1L, NULL, 0);
- update_screen(VALID); // redraw the screen NOW
+ update_screen(UPD_VALID); // redraw the screen NOW
made_cmdheight_nonzero = false;
lastwin->w_p_so = save_so;
@@ -722,7 +634,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
.ignore_drag_release = true,
};
CommandLineState *s = &state;
- s->save_p_icm = vim_strsave(p_icm);
+ s->save_p_icm = vim_strsave((char_u *)p_icm);
init_incsearch_state(&s->is_state);
CmdlineInfo save_ccline;
bool did_save_ccline = false;
@@ -736,7 +648,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
save_cmdline(&save_ccline);
did_save_ccline = true;
} else if (init_ccline) {
- memset(&ccline, 0, sizeof(struct cmdline_info));
+ CLEAR_FIELD(ccline);
}
if (s->firstc == -1) {
@@ -869,7 +781,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
may_trigger_modechanged();
init_history();
- s->hiscnt = hislen; // set hiscnt to impossible history value
+ s->hiscnt = get_hislen(); // set hiscnt to impossible history value
s->histype = hist_char2type(s->firstc);
do_digraph(-1); // init digraph typeahead
@@ -942,7 +854,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
s->histype == HIST_SEARCH ? s->firstc : NUL);
if (s->firstc == ':') {
xfree(new_last_cmdline);
- new_last_cmdline = vim_strsave(ccline.cmdbuff);
+ new_last_cmdline = (char *)vim_strsave(ccline.cmdbuff);
}
}
@@ -973,7 +885,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool init
State = s->save_State;
if (cmdpreview != save_cmdpreview) {
cmdpreview = save_cmdpreview; // restore preview state
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
may_trigger_modechanged();
setmouse();
@@ -1006,7 +918,7 @@ theend:
// Restore cmdheight
set_option_value("ch", 0L, NULL, 0);
// Redraw is needed for command line completion
- redraw_all_later(CLEAR);
+ redraw_all_later(UPD_NOT_VALID);
made_cmdheight_nonzero = false;
}
@@ -1111,41 +1023,24 @@ static int command_line_execute(VimState *state, int key)
s->c = Ctrl_P;
}
- // Special translations for 'wildmenu'
- if (s->did_wild_list && p_wmnu) {
- if (s->c == K_LEFT) {
- s->c = Ctrl_P;
- } else if (s->c == K_RIGHT) {
- s->c = Ctrl_N;
- }
+ if (p_wmnu) {
+ s->c = wildmenu_translate_key(&ccline, s->c, &s->xpc, s->did_wild_list);
}
- if (compl_match_array || s->did_wild_list) {
- if (s->c == Ctrl_E) {
- s->res = nextwild(&s->xpc, WILD_CANCEL, WILD_NO_BEEP,
- s->firstc != '@');
- } else if (s->c == Ctrl_Y) {
- s->res = nextwild(&s->xpc, WILD_APPLY, WILD_NO_BEEP,
- s->firstc != '@');
+
+ if (cmdline_pum_active() || s->did_wild_list) {
+ if (s->c == Ctrl_E || s->c == Ctrl_Y) {
+ const int wild_type = (s->c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY;
+ s->res = nextwild(&s->xpc, wild_type, WILD_NO_BEEP, s->firstc != '@');
s->c = Ctrl_E;
}
}
- // Hitting CR after "emenu Name.": complete submenu
- if (s->xpc.xp_context == EXPAND_MENUNAMES && p_wmnu
- && ccline.cmdpos > 1
- && ccline.cmdbuff[ccline.cmdpos - 1] == '.'
- && ccline.cmdbuff[ccline.cmdpos - 2] != '\\'
- && (s->c == '\n' || s->c == '\r' || s->c == K_KENTER)) {
- s->c = K_DOWN;
- }
-
// free expanded names when finished walking through matches
if (!(s->c == p_wc && KeyTyped) && s->c != p_wcm && s->c != Ctrl_Z
&& s->c != Ctrl_N && s->c != Ctrl_P && s->c != Ctrl_A
&& s->c != Ctrl_L) {
- if (compl_match_array) {
- pum_undisplay(true);
- XFREE_CLEAR(compl_match_array);
+ if (cmdline_pum_active()) {
+ cmdline_pum_remove();
}
if (s->xpc.xp_numfiles != -1) {
(void)ExpandOne(&s->xpc, NULL, NULL, 0, WILD_FREE);
@@ -1155,174 +1050,11 @@ static int command_line_execute(VimState *state, int key)
s->xpc.xp_context = EXPAND_NOTHING;
}
s->wim_index = 0;
- if (p_wmnu && wild_menu_showing != 0) {
- const bool skt = KeyTyped;
- int old_RedrawingDisabled = RedrawingDisabled;
-
- if (ccline.input_fn) {
- RedrawingDisabled = 0;
- }
-
- if (wild_menu_showing == WM_SCROLLED) {
- // Entered command line, move it up
- cmdline_row--;
- redrawcmd();
- wild_menu_showing = 0;
- } else if (save_p_ls != -1) {
- // restore 'laststatus' and 'winminheight'
- p_ls = save_p_ls;
- p_wmh = save_p_wmh;
- last_status(false);
- update_screen(VALID); // redraw the screen NOW
- redrawcmd();
- save_p_ls = -1;
- wild_menu_showing = 0;
- // don't redraw statusline if WM_LIST is showing
- } else if (wild_menu_showing != WM_LIST) {
- win_redraw_last_status(topframe);
- wild_menu_showing = 0; // must be before redraw_statuslines #8385
- redraw_statuslines();
- } else {
- wild_menu_showing = 0;
- }
- KeyTyped = skt;
- if (ccline.input_fn) {
- RedrawingDisabled = old_RedrawingDisabled;
- }
- }
+ wildmenu_cleanup(&ccline);
}
- // Special translations for 'wildmenu'
- if (s->xpc.xp_context == EXPAND_MENUNAMES && p_wmnu) {
- // Hitting <Down> after "emenu Name.": complete submenu
- if (s->c == K_DOWN && ccline.cmdpos > 0
- && ccline.cmdbuff[ccline.cmdpos - 1] == '.') {
- s->c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- } else if (s->c == K_UP) {
- // Hitting <Up>: Remove one submenu name in front of the
- // cursor
- int found = false;
-
- int j = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff);
- int i = 0;
- while (--j > 0) {
- // check for start of menu name
- if (ccline.cmdbuff[j] == ' '
- && ccline.cmdbuff[j - 1] != '\\') {
- i = j + 1;
- break;
- }
-
- // check for start of submenu name
- if (ccline.cmdbuff[j] == '.'
- && ccline.cmdbuff[j - 1] != '\\') {
- if (found) {
- i = j + 1;
- break;
- } else {
- found = true;
- }
- }
- }
- if (i > 0) {
- cmdline_del(i);
- }
- s->c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- s->xpc.xp_context = EXPAND_NOTHING;
- }
- }
- if ((s->xpc.xp_context == EXPAND_FILES
- || s->xpc.xp_context == EXPAND_DIRECTORIES
- || s->xpc.xp_context == EXPAND_SHELLCMD) && p_wmnu) {
- char_u upseg[5];
-
- upseg[0] = PATHSEP;
- upseg[1] = '.';
- upseg[2] = '.';
- upseg[3] = PATHSEP;
- upseg[4] = NUL;
-
- if (s->c == K_DOWN
- && ccline.cmdpos > 0
- && ccline.cmdbuff[ccline.cmdpos - 1] == PATHSEP
- && (ccline.cmdpos < 3
- || ccline.cmdbuff[ccline.cmdpos - 2] != '.'
- || ccline.cmdbuff[ccline.cmdpos - 3] != '.')) {
- // go down a directory
- s->c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- } else if (STRNCMP(s->xpc.xp_pattern, upseg + 1, 3) == 0
- && s->c == K_DOWN) {
- // If in a direct ancestor, strip off one ../ to go down
- int found = false;
-
- int j = ccline.cmdpos;
- int i = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff);
- while (--j > i) {
- j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j);
- if (vim_ispathsep(ccline.cmdbuff[j])) {
- found = true;
- break;
- }
- }
- if (found
- && ccline.cmdbuff[j - 1] == '.'
- && ccline.cmdbuff[j - 2] == '.'
- && (vim_ispathsep(ccline.cmdbuff[j - 3]) || j == i + 2)) {
- cmdline_del(j - 2);
- s->c = (int)p_wc;
- KeyTyped = true; // in case the key was mapped
- }
- } else if (s->c == K_UP) {
- // go up a directory
- int found = false;
-
- int j = ccline.cmdpos - 1;
- int i = (int)((char_u *)s->xpc.xp_pattern - ccline.cmdbuff);
- while (--j > i) {
- j -= utf_head_off(ccline.cmdbuff, ccline.cmdbuff + j);
- if (vim_ispathsep(ccline.cmdbuff[j])
-#ifdef BACKSLASH_IN_FILENAME
- && vim_strchr((const char_u *)" *?[{`$%#", ccline.cmdbuff[j + 1])
- == NULL
-#endif
- ) {
- if (found) {
- i = j + 1;
- break;
- } else {
- found = true;
- }
- }
- }
-
- if (!found) {
- j = i;
- } else if (STRNCMP(ccline.cmdbuff + j, upseg, 4) == 0) {
- j += 4;
- } else if (STRNCMP(ccline.cmdbuff + j, upseg + 1, 3) == 0
- && j == i) {
- j += 3;
- } else {
- j = 0;
- }
-
- if (j > 0) {
- // TODO(tarruda): this is only for DOS/Unix systems - need to put in
- // machine-specific stuff here and in upseg init
- cmdline_del(j);
- put_on_cmdline(upseg + 1, 3, false);
- } else if (ccline.cmdpos > i) {
- cmdline_del(i);
- }
-
- // Now complete in the new directory. Set KeyTyped in case the
- // Up key came from a mapping.
- s->c = (int)p_wc;
- KeyTyped = true;
- }
+ if (p_wmnu) {
+ s->c = wildmenu_process_key(&ccline, s->c, &s->xpc);
}
// CTRL-\ CTRL-N goes to Normal mode, CTRL-\ e prompts for an expression.
@@ -1530,7 +1262,7 @@ static int command_line_execute(VimState *state, int key)
}
if (s->wim_index < 3) {
- ++s->wim_index;
+ s->wim_index++;
}
if (s->c == ESC) {
@@ -1547,8 +1279,11 @@ static int command_line_execute(VimState *state, int key)
// <S-Tab> goes to last match, in a clumsy way
if (s->c == K_S_TAB && KeyTyped) {
if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) {
- showmatches(&s->xpc, p_wmnu
- && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ if (s->xpc.xp_numfiles > 1
+ && ((!s->did_wild_list && (wim_flags[s->wim_index] & WIM_LIST)) || p_wmnu)) {
+ // Trigger the popup menu when wildoptions=pum
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ }
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
return command_line_changed(s);
@@ -1664,8 +1399,8 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_
update_topline(curwin);
validate_cursor();
highlight_match = true;
- save_viewstate(&s->old_viewstate);
- update_screen(NOT_VALID);
+ save_viewstate(curwin, &s->old_viewstate);
+ update_screen(UPD_NOT_VALID);
highlight_match = false;
redrawcmdline();
curwin->w_cursor = s->match_end;
@@ -1682,12 +1417,12 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match)
for (;;) {
// one step backwards
if (!next_match) {
- if (s->hiscnt == hislen) {
+ if (s->hiscnt == get_hislen()) {
// first time
- s->hiscnt = hisidx[s->histype];
- } else if (s->hiscnt == 0 && hisidx[s->histype] != hislen - 1) {
- s->hiscnt = hislen - 1;
- } else if (s->hiscnt != hisidx[s->histype] + 1) {
+ s->hiscnt = *get_hisidx(s->histype);
+ } else if (s->hiscnt == 0 && *get_hisidx(s->histype) != get_hislen() - 1) {
+ s->hiscnt = get_hislen() - 1;
+ } else if (s->hiscnt != *get_hisidx(s->histype) + 1) {
s->hiscnt--;
} else {
// at top of list
@@ -1696,17 +1431,17 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match)
}
} else { // one step forwards
// on last entry, clear the line
- if (s->hiscnt == hisidx[s->histype]) {
- s->hiscnt = hislen;
+ if (s->hiscnt == *get_hisidx(s->histype)) {
+ s->hiscnt = get_hislen();
break;
}
// not on a history line, nothing to do
- if (s->hiscnt == hislen) {
+ if (s->hiscnt == get_hislen()) {
break;
}
- if (s->hiscnt == hislen - 1) {
+ if (s->hiscnt == get_hislen() - 1) {
// wrap around
s->hiscnt = 0;
} else {
@@ -1714,14 +1449,14 @@ static void command_line_next_histidx(CommandLineState *s, bool next_match)
}
}
- if (s->hiscnt < 0 || history[s->histype][s->hiscnt].hisstr == NULL) {
+ if (s->hiscnt < 0 || get_histentry(s->histype)[s->hiscnt].hisstr == NULL) {
s->hiscnt = s->save_hiscnt;
break;
}
if ((s->c != K_UP && s->c != K_DOWN)
|| s->hiscnt == s->save_hiscnt
- || STRNCMP(history[s->histype][s->hiscnt].hisstr,
+ || STRNCMP(get_histentry(s->histype)[s->hiscnt].hisstr,
s->lookfor, (size_t)j) == 0) {
break;
}
@@ -1744,7 +1479,7 @@ static int command_line_handle_key(CommandLineState *s)
// delete current character is the same as backspace on next
// character, except at end of line
if (s->c == K_DEL && ccline.cmdpos != ccline.cmdlen) {
- ++ccline.cmdpos;
+ ccline.cmdpos++;
}
if (s->c == K_DEL) {
@@ -2068,9 +1803,17 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_not_changed(s);
case Ctrl_A: // all matches
+ if (cmdline_pum_active()) {
+ // As Ctrl-A completes all the matches, close the popup
+ // menu (if present)
+ cmdline_pum_cleanup(&ccline);
+ }
+
if (nextwild(&s->xpc, WILD_ALL, 0, s->firstc != '@') == FAIL) {
break;
}
+ s->xpc.xp_context = EXPAND_NOTHING;
+ s->did_wild_list = false;
return command_line_changed(s);
case Ctrl_L:
@@ -2103,7 +1846,7 @@ static int command_line_handle_key(CommandLineState *s)
case K_KPAGEUP:
case K_PAGEDOWN:
case K_KPAGEDOWN:
- if (s->histype == HIST_INVALID || hislen == 0 || s->firstc == NUL) {
+ if (s->histype == HIST_INVALID || get_hislen() == 0 || s->firstc == NUL) {
// no history
return command_line_not_changed(s);
}
@@ -2128,10 +1871,10 @@ static int command_line_handle_key(CommandLineState *s)
XFREE_CLEAR(ccline.cmdbuff);
s->xpc.xp_context = EXPAND_NOTHING;
- if (s->hiscnt == hislen) {
+ if (s->hiscnt == get_hislen()) {
p = s->lookfor; // back to the old one
} else {
- p = history[s->histype][s->hiscnt].hisstr;
+ p = get_histentry(s->histype)[s->hiscnt].hisstr;
}
if (s->histype == HIST_SEARCH
@@ -2159,14 +1902,14 @@ static int command_line_handle_key(CommandLineState *s)
if (i > 0) {
ccline.cmdbuff[len] = '\\';
}
- ++len;
+ len++;
}
if (i > 0) {
ccline.cmdbuff[len] = p[j];
}
}
- ++len;
+ len++;
}
if (i == 0) {
@@ -2396,6 +2139,126 @@ static void cmdpreview_close_win(void)
}
}
+/// Save current state and prepare windows and buffers for command preview.
+static void cmdpreview_prepare(CpInfo *cpinfo)
+{
+ kv_init(cpinfo->buf_info);
+ kv_init(cpinfo->win_info);
+
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ buf_T *buf = win->w_buffer;
+
+ // Don't save state of command preview buffer or preview window.
+ if (buf->handle == cmdpreview_bufnr) {
+ continue;
+ }
+
+ CpBufInfo cp_bufinfo;
+ cp_bufinfo.buf = buf;
+
+ cp_bufinfo.save_b_u_time_cur = buf->b_u_time_cur;
+ cp_bufinfo.save_b_u_seq_cur = buf->b_u_seq_cur;
+ cp_bufinfo.save_b_u_newhead = buf->b_u_newhead;
+ cp_bufinfo.save_b_p_ul = buf->b_p_ul;
+ cp_bufinfo.save_b_changed = buf->b_changed;
+ cp_bufinfo.save_changedtick = buf_get_changedtick(buf);
+
+ kv_push(cpinfo->buf_info, cp_bufinfo);
+
+ buf->b_p_ul = LONG_MAX; // Make sure we can undo all changes
+
+ CpWinInfo cp_wininfo;
+ cp_wininfo.win = win;
+
+ // Save window cursor position and viewstate
+ cp_wininfo.save_w_cursor = win->w_cursor;
+ save_viewstate(win, &cp_wininfo.save_viewstate);
+
+ // Save 'cursorline' and 'cursorcolumn'
+ cp_wininfo.save_w_p_cul = win->w_p_cul;
+ cp_wininfo.save_w_p_cuc = win->w_p_cuc;
+
+ kv_push(cpinfo->win_info, cp_wininfo);
+
+ win->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights
+ win->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights
+ }
+
+ cpinfo->save_hls = p_hls;
+ cpinfo->save_cmdmod = cmdmod;
+ win_size_save(&cpinfo->save_view);
+ save_search_patterns();
+
+ p_hls = false; // Don't show search highlighting during live substitution
+ cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers
+ cmdmod.cmod_tab = 0; // Disable :tab modifier
+ cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer
+}
+
+// Restore the state of buffers and windows before command preview.
+static void cmdpreview_restore_state(CpInfo *cpinfo)
+{
+ for (size_t i = 0; i < cpinfo->buf_info.size; i++) {
+ CpBufInfo cp_bufinfo = cpinfo->buf_info.items[i];
+ buf_T *buf = cp_bufinfo.buf;
+
+ buf->b_changed = cp_bufinfo.save_b_changed;
+
+ if (buf->b_u_seq_cur != cp_bufinfo.save_b_u_seq_cur) {
+ int count = 0;
+
+ // Calculate how many undo steps are necessary to restore earlier state.
+ for (u_header_T *uhp = buf->b_u_curhead ? buf->b_u_curhead : buf->b_u_newhead;
+ uhp != NULL && uhp->uh_seq > cp_bufinfo.save_b_u_seq_cur;
+ uhp = uhp->uh_next.ptr, ++count) {}
+
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+ // Undo invisibly. This also moves the cursor!
+ if (!u_undo_and_forget(count)) {
+ abort();
+ }
+ aucmd_restbuf(&aco);
+
+ // Restore newhead. It is meaningless when curhead is valid, but we must
+ // restore it so that undotree() is identical before/after the preview.
+ buf->b_u_newhead = cp_bufinfo.save_b_u_newhead;
+ buf->b_u_time_cur = cp_bufinfo.save_b_u_time_cur;
+ }
+ if (cp_bufinfo.save_changedtick != buf_get_changedtick(buf)) {
+ buf_set_changedtick(buf, cp_bufinfo.save_changedtick);
+ }
+
+ buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels'
+
+ // Clear preview highlights.
+ extmark_clear(buf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
+ }
+ for (size_t i = 0; i < cpinfo->win_info.size; i++) {
+ CpWinInfo cp_wininfo = cpinfo->win_info.items[i];
+ win_T *win = cp_wininfo.win;
+
+ // Restore window cursor position and viewstate
+ win->w_cursor = cp_wininfo.save_w_cursor;
+ restore_viewstate(win, &cp_wininfo.save_viewstate);
+
+ // Restore 'cursorline' and 'cursorcolumn'
+ win->w_p_cul = cp_wininfo.save_w_p_cul;
+ win->w_p_cuc = cp_wininfo.save_w_p_cuc;
+
+ update_topline(win);
+ }
+
+ cmdmod = cpinfo->save_cmdmod; // Restore cmdmod
+ p_hls = cpinfo->save_hls; // Restore 'hlsearch'
+ restore_search_patterns(); // Restore search patterns
+ win_size_restore(&cpinfo->save_view); // Restore window sizes
+
+ ga_clear(&cpinfo->save_view);
+ kv_destroy(cpinfo->win_info);
+ kv_destroy(cpinfo->buf_info);
+}
+
/// Show 'inccommand' preview if command is previewable. It works like this:
/// 1. Store current undo information so we can revert to current state later.
/// 2. Execute the preview callback with the parsed command, preview buffer number and preview
@@ -2440,35 +2303,18 @@ static bool cmdpreview_may_show(CommandLineState *s)
ea.line2 = lnum;
}
- time_t save_b_u_time_cur = curbuf->b_u_time_cur;
- long save_b_u_seq_cur = curbuf->b_u_seq_cur;
- u_header_T *save_b_u_newhead = curbuf->b_u_newhead;
- long save_b_p_ul = curbuf->b_p_ul;
- int save_b_changed = curbuf->b_changed;
- int save_w_p_cul = curwin->w_p_cul;
- int save_w_p_cuc = curwin->w_p_cuc;
- bool save_hls = p_hls;
- varnumber_T save_changedtick = buf_get_changedtick(curbuf);
+ CpInfo cpinfo;
bool icm_split = *p_icm == 's'; // inccommand=split
buf_T *cmdpreview_buf;
win_T *cmdpreview_win;
- cmdmod_T save_cmdmod = cmdmod;
- cmdpreview = true;
emsg_silent++; // Block error reporting as the command may be incomplete,
// but still update v:errmsg
msg_silent++; // Block messages, namely ones that prompt
block_autocmds(); // Block events
- garray_T save_view;
- win_size_save(&save_view); // Save current window sizes
- save_search_patterns(); // Save search patterns
- curbuf->b_p_ul = LONG_MAX; // Make sure we can undo all changes
- curwin->w_p_cul = false; // Disable 'cursorline' so it doesn't mess up the highlights
- curwin->w_p_cuc = false; // Disable 'cursorcolumn' so it doesn't mess up the highlights
- p_hls = false; // Don't show search highlighting during live substitution
- cmdmod.cmod_split = 0; // Disable :leftabove/botright modifiers
- cmdmod.cmod_tab = 0; // Disable :tab modifier
- cmdmod.cmod_flags |= CMOD_NOSWAPFILE; // Disable swap for preview buffer
+
+ // Save current state and prepare for command preview.
+ cmdpreview_prepare(&cpinfo);
// Open preview buffer if inccommand=split.
if (!icm_split) {
@@ -2476,12 +2322,14 @@ static bool cmdpreview_may_show(CommandLineState *s)
} else if ((cmdpreview_buf = cmdpreview_open_buf()) == NULL) {
abort();
}
-
// Setup preview namespace if it's not already set.
if (!cmdpreview_ns) {
cmdpreview_ns = (int)nvim_create_namespace((String)STRING_INIT);
}
+ // Set cmdpreview state.
+ cmdpreview = true;
+
// Execute the preview callback and use its return value to determine whether to show preview or
// open the preview window. The preview callback also handles doing the changes and highlights for
// the preview.
@@ -2500,11 +2348,11 @@ static bool cmdpreview_may_show(CommandLineState *s)
cmdpreview_type = 1;
}
- // If preview callback is nonzero, update screen now.
+ // If preview callback return value is nonzero, update screen now.
if (cmdpreview_type != 0) {
int save_rd = RedrawingDisabled;
RedrawingDisabled = 0;
- update_screen(SOME_VALID);
+ update_screen(UPD_SOME_VALID);
RedrawingDisabled = save_rd;
}
@@ -2512,53 +2360,22 @@ static bool cmdpreview_may_show(CommandLineState *s)
if (icm_split && cmdpreview_type == 2 && cmdpreview_win != NULL) {
cmdpreview_close_win();
}
- // Clear preview highlights.
- extmark_clear(curbuf, (uint32_t)cmdpreview_ns, 0, 0, MAXLNUM, MAXCOL);
-
- curbuf->b_changed = save_b_changed; // Preserve 'modified' during preview
- if (curbuf->b_u_seq_cur != save_b_u_seq_cur) {
- // Undo invisibly. This also moves the cursor!
- while (curbuf->b_u_seq_cur != save_b_u_seq_cur) {
- if (!u_undo_and_forget(1)) {
- abort();
- }
- }
- // Restore newhead. It is meaningless when curhead is valid, but we must
- // restore it so that undotree() is identical before/after the preview.
- curbuf->b_u_newhead = save_b_u_newhead;
- curbuf->b_u_time_cur = save_b_u_time_cur;
- }
- if (save_changedtick != buf_get_changedtick(curbuf)) {
- buf_set_changedtick(curbuf, save_changedtick);
- }
+ // Restore state.
+ cmdpreview_restore_state(&cpinfo);
- cmdmod = save_cmdmod; // Restore cmdmod
- p_hls = save_hls; // Restore 'hlsearch'
- curwin->w_p_cul = save_w_p_cul; // Restore 'cursorline'
- curwin->w_p_cuc = save_w_p_cuc; // Restore 'cursorcolumn'
- curbuf->b_p_ul = save_b_p_ul; // Restore 'undolevels'
- restore_search_patterns(); // Restore search patterns
- win_size_restore(&save_view); // Restore window sizes
- ga_clear(&save_view);
unblock_autocmds(); // Unblock events
msg_silent--; // Unblock messages
emsg_silent--; // Unblock error reporting
-
- // Restore the window "view".
- curwin->w_cursor = s->is_state.save_cursor;
- restore_viewstate(&s->is_state.old_viewstate);
- update_topline(curwin);
-
redrawcmdline();
end:
xfree(cmdline);
return cmdpreview_type != 0;
}
-static int command_line_changed(CommandLineState *s)
+/// Trigger CmdlineChanged autocommands.
+static void do_autocmd_cmdlinechanged(int firstc)
{
- // Trigger CmdlineChanged autocommands.
if (has_event(EVENT_CMDLINECHANGED)) {
TryState tstate;
Error err = ERROR_INIT;
@@ -2566,7 +2383,7 @@ static int command_line_changed(CommandLineState *s)
dict_T *dict = get_v_event(&save_v_event);
char firstcbuf[2];
- firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
+ firstcbuf[0] = (char)firstc;
firstcbuf[1] = 0;
// set v:event to a dictionary with information about the commandline
@@ -2586,6 +2403,12 @@ static int command_line_changed(CommandLineState *s)
redrawcmd();
}
}
+}
+
+static int command_line_changed(CommandLineState *s)
+{
+ // Trigger CmdlineChanged autocommands.
+ do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-');
if (s->firstc == ':'
&& current_sctx.sc_sid == 0 // only if interactive
@@ -2597,7 +2420,7 @@ static int command_line_changed(CommandLineState *s)
// 'inccommand' preview has been shown.
} else if (cmdpreview) {
cmdpreview = false;
- update_screen(SOME_VALID); // Clear 'inccommand' preview.
+ update_screen(UPD_SOME_VALID); // Clear 'inccommand' preview.
} else {
if (s->xpc.xp_context == EXPAND_NOTHING && (KeyTyped || vpeekc() == NUL)) {
may_do_incsearch_highlighting(s->firstc, s->count, &s->is_state);
@@ -2682,7 +2505,7 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int at
save_cmdline(&save_ccline);
did_save_ccline = true;
} else {
- memset(&ccline, 0, sizeof(struct cmdline_info));
+ CLEAR_FIELD(ccline);
}
ccline.prompt_id = last_prompt_id++;
ccline.cmdprompt = (char_u *)prompt;
@@ -2718,6 +2541,58 @@ char_u *get_cmdprompt(void)
return ccline.cmdprompt;
}
+/// Read the 'wildmode' option, fill wim_flags[].
+int check_opt_wim(void)
+{
+ char_u new_wim_flags[4];
+ int i;
+ int idx = 0;
+
+ for (i = 0; i < 4; i++) {
+ new_wim_flags[i] = 0;
+ }
+
+ for (char *p = p_wim; *p; p++) {
+ for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
+ if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
+ return FAIL;
+ }
+ if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
+ new_wim_flags[idx] |= WIM_LONGEST;
+ } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
+ new_wim_flags[idx] |= WIM_FULL;
+ } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
+ new_wim_flags[idx] |= WIM_LIST;
+ } else if (i == 8 && STRNCMP(p, "lastused", 8) == 0) {
+ new_wim_flags[idx] |= WIM_BUFLASTUSED;
+ } else {
+ return FAIL;
+ }
+ p += i;
+ if (*p == NUL) {
+ break;
+ }
+ if (*p == ',') {
+ if (idx == 3) {
+ return FAIL;
+ }
+ idx++;
+ }
+ }
+
+ // fill remaining entries with last flag
+ while (idx < 3) {
+ new_wim_flags[idx + 1] = new_wim_flags[idx];
+ idx++;
+ }
+
+ // only when there are no errors, wim_flags[] is changed
+ for (i = 0; i < 4; i++) {
+ wim_flags[i] = new_wim_flags[i];
+ }
+ return OK;
+}
+
/// Return true when the text must not be changed and we can't switch to
/// another window or buffer. True when editing the command line etc.
bool text_locked(void)
@@ -2795,7 +2670,7 @@ static int cmd_startcol(void)
}
/// Compute the column position for a byte position on the command line.
-static int cmd_screencol(int bytepos)
+int cmd_screencol(int bytepos)
{
int m; // maximum column
@@ -2882,10 +2757,8 @@ static void alloc_cmdbuff(int len)
ccline.cmdbufflen = len;
}
-/*
- * Re-allocate the command line to length len + something extra.
- */
-static void realloc_cmdbuff(int len)
+/// Re-allocate the command line to length len + something extra.
+void realloc_cmdbuff(int len)
{
if (len < ccline.cmdbufflen) {
return; // no need to resize
@@ -3217,7 +3090,7 @@ color_cmdline_error:
/*
* Draw part of the cmdline at the current cursor position. But draw stars
- * when cmdline_star is TRUE.
+ * when cmdline_star is true.
*/
static void draw_cmdline(int start, int len)
{
@@ -3325,7 +3198,7 @@ static void draw_cmdline(int start, int len)
}
}
- msg_outtrans_len((char_u *)arshape_buf, newlen);
+ msg_outtrans_len(arshape_buf, newlen);
} else {
draw_cmdline_no_arabicshape:
if (kv_size(ccline.last_colors.colors)) {
@@ -3340,7 +3213,7 @@ draw_cmdline_no_arabicshape:
chunk.attr);
}
} else {
- msg_outtrans_len(ccline.cmdbuff + start, len);
+ msg_outtrans_len((char *)ccline.cmdbuff + start, len);
}
}
}
@@ -3348,7 +3221,6 @@ draw_cmdline_no_arabicshape:
static void ui_ext_cmdline_show(CmdlineInfo *line)
{
Arena arena = ARENA_EMPTY;
- arena_start(&arena, &ui_ext_fixblk);
Array content;
if (cmdline_star) {
content = arena_array(&arena, 1);
@@ -3393,7 +3265,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
line->special_shift,
line->level);
}
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
}
void ui_ext_cmdline_block_append(size_t indent, const char *line)
@@ -3472,7 +3344,7 @@ void cmdline_ui_flush(void)
/*
* Put a character on the command line. Shifts the following text to the
- * right when "shift" is TRUE. Used for CTRL-V, CTRL-K, etc.
+ * right when "shift" is true. Used for CTRL-V, CTRL-K, etc.
* "c" must be printable (fit in one display cell)!
*/
void putcmdline(char c, int shift)
@@ -3498,7 +3370,7 @@ void putcmdline(char c, int shift)
ui_cursor_shape();
}
-/// Undo a putcmdline(c, FALSE).
+/// Undo a putcmdline(c, false).
void unputcmdline(void)
{
if (cmd_silent) {
@@ -3519,9 +3391,9 @@ void unputcmdline(void)
/*
* Put the given string, of the given length, onto the command line.
* If len is -1, then STRLEN() is used to calculate the length.
- * If 'redraw' is TRUE then the new part of the command line, and the remaining
+ * If 'redraw' is true then the new part of the command line, and the remaining
* part will be redrawn, otherwise it will not. If this function is called
- * twice in a row, then 'redraw' should be FALSE and redrawcmd() should be
+ * twice in a row, then 'redraw' should be false and redrawcmd() should be
* called afterwards.
*/
void put_on_cmdline(char_u *str, int len, int redraw)
@@ -3592,13 +3464,13 @@ void put_on_cmdline(char_u *str, int len, int redraw)
msg_col -= i;
if (msg_col < 0) {
msg_col += Columns;
- --msg_row;
+ msg_row--;
}
}
}
if (redraw && !cmd_silent) {
- msg_no_more = TRUE;
+ msg_no_more = true;
i = cmdline_row;
cursorcmd();
draw_cmdline(ccline.cmdpos, ccline.cmdlen - ccline.cmdpos);
@@ -3606,7 +3478,7 @@ void put_on_cmdline(char_u *str, int len, int redraw)
if (cmdline_row != i || ccline.overstrike) {
msg_clr_eos();
}
- msg_no_more = FALSE;
+ msg_no_more = false;
}
if (KeyTyped) {
m = Columns * Rows;
@@ -3642,16 +3514,16 @@ void put_on_cmdline(char_u *str, int len, int redraw)
/// Save ccline, because obtaining the "=" register may execute "normal :cmd"
/// and overwrite it.
-static void save_cmdline(struct cmdline_info *ccp)
+static void save_cmdline(CmdlineInfo *ccp)
{
*ccp = ccline;
- memset(&ccline, 0, sizeof(struct cmdline_info));
+ CLEAR_FIELD(ccline);
ccline.prev_ccline = ccp;
ccline.cmdbuff = NULL; // signal that ccline is not in use
}
/// Restore ccline after it has been saved with save_cmdline().
-static void restore_cmdline(struct cmdline_info *ccp)
+static void restore_cmdline(CmdlineInfo *ccp)
FUNC_ATTR_NONNULL_ALL
{
ccline = *ccp;
@@ -3669,7 +3541,7 @@ static void restore_cmdline(struct cmdline_info *ccp)
/// @returns FAIL for failure, OK otherwise
static bool cmdline_paste(int regname, bool literally, bool remcr)
{
- char_u *arg;
+ char *arg;
char_u *p;
bool allocated;
@@ -3702,7 +3574,7 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
// When 'incsearch' is set and CTRL-R CTRL-W used: skip the duplicate
// part of the word.
- p = arg;
+ p = (char_u *)arg;
if (p_is && regname == Ctrl_W) {
char_u *w;
int len;
@@ -3733,8 +3605,8 @@ static bool cmdline_paste(int regname, bool literally, bool remcr)
/*
* Put a string on the command line.
- * When "literally" is TRUE, insert literally.
- * When "literally" is FALSE, insert as typed, but don't leave the command
+ * When "literally" is true, insert literally.
+ * When "literally" is false, insert as typed, but don't leave the command
* line.
*/
void cmdline_paste_str(char_u *s, int literally)
@@ -3742,7 +3614,7 @@ void cmdline_paste_str(char_u *s, int literally)
int c, cv;
if (literally) {
- put_on_cmdline(s, -1, TRUE);
+ put_on_cmdline(s, -1, true);
} else {
while (*s != NUL) {
cv = *s;
@@ -3760,16 +3632,6 @@ void cmdline_paste_str(char_u *s, int literally)
}
}
-/// Delete characters on the command line, from "from" to the current position.
-static void cmdline_del(int from)
-{
- assert(ccline.cmdpos <= ccline.cmdlen);
- memmove(ccline.cmdbuff + from, ccline.cmdbuff + ccline.cmdpos,
- (size_t)ccline.cmdlen - (size_t)ccline.cmdpos + 1);
- ccline.cmdlen -= ccline.cmdpos - from;
- ccline.cmdpos = from;
-}
-
// This function is called when the screen size changes and with incremental
// search and in other situations where the command line may have been
// overwritten.
@@ -3841,7 +3703,7 @@ void redrawcmd(void)
redrawcmdprompt();
// Don't use more prompt, truncate the cmdline if it doesn't fit.
- msg_no_more = TRUE;
+ msg_no_more = true;
draw_cmdline(0, ccline.cmdlen);
msg_clr_eos();
msg_no_more = false;
@@ -3856,7 +3718,7 @@ void redrawcmd(void)
* An emsg() before may have set msg_scroll. This is used in normal mode,
* in cmdline mode we can reset them now.
*/
- msg_scroll = FALSE; // next message overwrites cmdline
+ msg_scroll = false; // next message overwrites cmdline
// Typing ':' at the more prompt may set skip_redraw. We don't want this
// in cmdline mode.
@@ -3877,7 +3739,7 @@ void compute_cmdrow(void)
lines_left = cmdline_row;
}
-static void cursorcmd(void)
+void cursorcmd(void)
{
if (cmd_silent) {
return;
@@ -3964,462 +3826,6 @@ static int ccheck_abbr(int c)
return check_abbr(c, ccline.cmdbuff, ccline.cmdpos, spos);
}
-static int sort_func_compare(const void *s1, const void *s2)
-{
- char_u *p1 = *(char_u **)s1;
- char_u *p2 = *(char_u **)s2;
-
- if (*p1 != '<' && *p2 == '<') {
- return -1;
- }
- if (*p1 == '<' && *p2 != '<') {
- return 1;
- }
- return STRCMP(p1, p2);
-}
-
-/// Return FAIL if this is not an appropriate context in which to do
-/// completion of anything, return OK if it is (even if there are no matches).
-/// For the caller, this means that the character is just passed through like a
-/// normal character (instead of being expanded). This allows :s/^I^D etc.
-///
-/// @param options extra options for ExpandOne()
-/// @param escape if TRUE, escape the returned matches
-static int nextwild(expand_T *xp, int type, int options, int escape)
-{
- int i, j;
- char_u *p1;
- char_u *p2;
- int difflen;
-
- if (xp->xp_numfiles == -1) {
- set_expand_context(xp);
- cmd_showtail = expand_showtail(xp);
- }
-
- if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
- beep_flush();
- return OK; // Something illegal on command line
- }
- if (xp->xp_context == EXPAND_NOTHING) {
- // Caller can use the character as a normal char instead
- return FAIL;
- }
-
- if (!(ui_has(kUICmdline) || ui_has(kUIWildmenu))) {
- msg_puts("..."); // show that we are busy
- ui_flush();
- }
-
- i = (int)((char_u *)xp->xp_pattern - ccline.cmdbuff);
- assert(ccline.cmdpos >= i);
- xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i;
-
- if (type == WILD_NEXT || type == WILD_PREV) {
- // Get next/previous match for a previous expanded pattern.
- p2 = ExpandOne(xp, NULL, NULL, 0, type);
- } else {
- // Translate string into pattern and expand it.
- p1 = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
- const int use_options = (
- options
- | WILD_HOME_REPLACE
- | WILD_ADD_SLASH
- | WILD_SILENT
- | (escape ? WILD_ESCAPE : 0)
- | (p_wic ? WILD_ICASE : 0));
- p2 = ExpandOne(xp, p1, vim_strnsave(&ccline.cmdbuff[i], xp->xp_pattern_len),
- use_options, type);
- xfree(p1);
-
- // xp->xp_pattern might have been modified by ExpandOne (for example,
- // in lua completion), so recompute the pattern index and length
- i = (int)((char_u *)xp->xp_pattern - ccline.cmdbuff);
- xp->xp_pattern_len = (size_t)ccline.cmdpos - (size_t)i;
-
- // Longest match: make sure it is not shorter, happens with :help.
- if (p2 != NULL && type == WILD_LONGEST) {
- for (j = 0; (size_t)j < xp->xp_pattern_len; j++) {
- if (ccline.cmdbuff[i + j] == '*'
- || ccline.cmdbuff[i + j] == '?') {
- break;
- }
- }
- if ((int)STRLEN(p2) < j) {
- XFREE_CLEAR(p2);
- }
- }
- }
-
- if (p2 != NULL && !got_int) {
- difflen = (int)STRLEN(p2) - (int)(xp->xp_pattern_len);
- if (ccline.cmdlen + difflen + 4 > ccline.cmdbufflen) {
- realloc_cmdbuff(ccline.cmdlen + difflen + 4);
- xp->xp_pattern = (char *)ccline.cmdbuff + i;
- }
- assert(ccline.cmdpos <= ccline.cmdlen);
- memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
- &ccline.cmdbuff[ccline.cmdpos],
- (size_t)ccline.cmdlen - (size_t)ccline.cmdpos + 1);
- memmove(&ccline.cmdbuff[i], p2, STRLEN(p2));
- ccline.cmdlen += difflen;
- ccline.cmdpos += difflen;
- }
- xfree(p2);
-
- redrawcmd();
- cursorcmd();
-
- /* When expanding a ":map" command and no matches are found, assume that
- * the key is supposed to be inserted literally */
- if (xp->xp_context == EXPAND_MAPPINGS && p2 == NULL) {
- return FAIL;
- }
-
- if (xp->xp_numfiles <= 0 && p2 == NULL) {
- beep_flush();
- } else if (xp->xp_numfiles == 1) {
- // free expanded pattern
- (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE);
- }
-
- return OK;
-}
-
-/// Do wildcard expansion on the string 'str'.
-/// Chars that should not be expanded must be preceded with a backslash.
-/// Return a pointer to allocated memory containing the new string.
-/// Return NULL for failure.
-///
-/// "orig" is the originally expanded string, copied to allocated memory. It
-/// should either be kept in orig_save or freed. When "mode" is WILD_NEXT or
-/// WILD_PREV "orig" should be NULL.
-///
-/// Results are cached in xp->xp_files and xp->xp_numfiles, except when "mode"
-/// is WILD_EXPAND_FREE or WILD_ALL.
-///
-/// mode = WILD_FREE: just free previously expanded matches
-/// mode = WILD_EXPAND_FREE: normal expansion, do not keep matches
-/// mode = WILD_EXPAND_KEEP: normal expansion, keep matches
-/// mode = WILD_NEXT: use next match in multiple match, wrap to first
-/// mode = WILD_PREV: use previous match in multiple match, wrap to first
-/// mode = WILD_ALL: return all matches concatenated
-/// mode = WILD_LONGEST: return longest matched part
-/// mode = WILD_ALL_KEEP: get all matches, keep matches
-///
-/// options = WILD_LIST_NOTFOUND: list entries without a match
-/// options = WILD_HOME_REPLACE: do home_replace() for buffer names
-/// options = WILD_USE_NL: Use '\n' for WILD_ALL
-/// options = WILD_NO_BEEP: Don't beep for multiple matches
-/// options = WILD_ADD_SLASH: add a slash after directory names
-/// options = WILD_KEEP_ALL: don't remove 'wildignore' entries
-/// options = WILD_SILENT: don't print warning messages
-/// options = WILD_ESCAPE: put backslash before special chars
-/// options = WILD_ICASE: ignore case for files
-///
-/// The variables xp->xp_context and xp->xp_backslash must have been set!
-///
-/// @param orig allocated copy of original of expanded string
-char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode)
-{
- char_u *ss = NULL;
- static int findex;
- static char_u *orig_save = NULL; // kept value of orig
- int orig_saved = FALSE;
- int i;
- int non_suf_match; // number without matching suffix
-
- /*
- * first handle the case of using an old match
- */
- if (mode == WILD_NEXT || mode == WILD_PREV) {
- if (xp->xp_numfiles > 0) {
- if (mode == WILD_PREV) {
- if (findex == -1) {
- findex = xp->xp_numfiles;
- }
- --findex;
- } else { // mode == WILD_NEXT
- ++findex;
- }
-
- /*
- * When wrapping around, return the original string, set findex to
- * -1.
- */
- if (findex < 0) {
- if (orig_save == NULL) {
- findex = xp->xp_numfiles - 1;
- } else {
- findex = -1;
- }
- }
- if (findex >= xp->xp_numfiles) {
- if (orig_save == NULL) {
- findex = 0;
- } else {
- findex = -1;
- }
- }
- if (compl_match_array) {
- compl_selected = findex;
- cmdline_pum_display(false);
- } else if (p_wmnu) {
- win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail);
- }
- if (findex == -1) {
- return vim_strsave(orig_save);
- }
- return vim_strsave((char_u *)xp->xp_files[findex]);
- } else {
- return NULL;
- }
- }
-
- if (mode == WILD_CANCEL) {
- ss = vim_strsave(orig_save ? orig_save : (char_u *)"");
- } else if (mode == WILD_APPLY) {
- ss = vim_strsave(findex == -1 ? (orig_save ? orig_save : (char_u *)"") :
- (char_u *)xp->xp_files[findex]);
- }
-
- // free old names
- if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) {
- FreeWild(xp->xp_numfiles, xp->xp_files);
- xp->xp_numfiles = -1;
- XFREE_CLEAR(orig_save);
- }
- findex = 0;
-
- if (mode == WILD_FREE) { // only release file name
- return NULL;
- }
-
- if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL) {
- xfree(orig_save);
- orig_save = orig;
- orig_saved = TRUE;
-
- /*
- * Do the expansion.
- */
- if (ExpandFromContext(xp, str, &xp->xp_numfiles, &xp->xp_files, options) == FAIL) {
-#ifdef FNAME_ILLEGAL
- /* Illegal file name has been silently skipped. But when there
- * are wildcards, the real problem is that there was no match,
- * causing the pattern to be added, which has illegal characters.
- */
- if (!(options & WILD_SILENT) && (options & WILD_LIST_NOTFOUND)) {
- semsg(_(e_nomatch2), str);
- }
-#endif
- } else if (xp->xp_numfiles == 0) {
- if (!(options & WILD_SILENT)) {
- semsg(_(e_nomatch2), str);
- }
- } else {
- // Escape the matches for use on the command line.
- ExpandEscape(xp, str, xp->xp_numfiles, xp->xp_files, options);
-
- /*
- * Check for matching suffixes in file names.
- */
- if (mode != WILD_ALL && mode != WILD_ALL_KEEP
- && mode != WILD_LONGEST) {
- if (xp->xp_numfiles) {
- non_suf_match = xp->xp_numfiles;
- } else {
- non_suf_match = 1;
- }
- if ((xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_DIRECTORIES)
- && xp->xp_numfiles > 1) {
- /*
- * More than one match; check suffix.
- * The files will have been sorted on matching suffix in
- * expand_wildcards, only need to check the first two.
- */
- non_suf_match = 0;
- for (i = 0; i < 2; i++) {
- if (match_suffix((char_u *)xp->xp_files[i])) {
- non_suf_match++;
- }
- }
- }
- if (non_suf_match != 1) {
- /* Can we ever get here unless it's while expanding
- * interactively? If not, we can get rid of this all
- * together. Don't really want to wait for this message
- * (and possibly have to hit return to continue!).
- */
- if (!(options & WILD_SILENT)) {
- emsg(_(e_toomany));
- } else if (!(options & WILD_NO_BEEP)) {
- beep_flush();
- }
- }
- if (!(non_suf_match != 1 && mode == WILD_EXPAND_FREE)) {
- ss = vim_strsave((char_u *)xp->xp_files[0]);
- }
- }
- }
- }
-
- // Find longest common part
- if (mode == WILD_LONGEST && xp->xp_numfiles > 0) {
- size_t len = 0;
-
- for (size_t mb_len; xp->xp_files[0][len]; len += mb_len) {
- mb_len = (size_t)utfc_ptr2len(&xp->xp_files[0][len]);
- int c0 = utf_ptr2char(&xp->xp_files[0][len]);
- for (i = 1; i < xp->xp_numfiles; i++) {
- int ci = utf_ptr2char(&xp->xp_files[i][len]);
-
- if (p_fic && (xp->xp_context == EXPAND_DIRECTORIES
- || xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS)) {
- if (mb_tolower(c0) != mb_tolower(ci)) {
- break;
- }
- } else if (c0 != ci) {
- break;
- }
- }
- if (i < xp->xp_numfiles) {
- if (!(options & WILD_NO_BEEP)) {
- vim_beep(BO_WILD);
- }
- break;
- }
- }
-
- ss = (char_u *)xstrndup(xp->xp_files[0], len);
- findex = -1; // next p_wc gets first one
- }
-
- // Concatenate all matching names
- // TODO(philix): use xstpcpy instead of strcat in a loop (ExpandOne)
- if (mode == WILD_ALL && xp->xp_numfiles > 0) {
- size_t len = 0;
- for (i = 0; i < xp->xp_numfiles; ++i) {
- len += STRLEN(xp->xp_files[i]) + 1;
- }
- ss = xmalloc(len);
- *ss = NUL;
- for (i = 0; i < xp->xp_numfiles; ++i) {
- STRCAT(ss, xp->xp_files[i]);
- if (i != xp->xp_numfiles - 1) {
- STRCAT(ss, (options & WILD_USE_NL) ? "\n" : " ");
- }
- }
- }
-
- if (mode == WILD_EXPAND_FREE || mode == WILD_ALL) {
- ExpandCleanup(xp);
- }
-
- // Free "orig" if it wasn't stored in "orig_save".
- if (!orig_saved) {
- xfree(orig);
- }
-
- return ss;
-}
-
-/*
- * Prepare an expand structure for use.
- */
-void ExpandInit(expand_T *xp)
- FUNC_ATTR_NONNULL_ALL
-{
- CLEAR_POINTER(xp);
- xp->xp_backslash = XP_BS_NONE;
- xp->xp_numfiles = -1;
-}
-
-/*
- * Cleanup an expand structure after use.
- */
-void ExpandCleanup(expand_T *xp)
-{
- if (xp->xp_numfiles >= 0) {
- FreeWild(xp->xp_numfiles, xp->xp_files);
- xp->xp_numfiles = -1;
- }
-}
-
-void ExpandEscape(expand_T *xp, char_u *str, int numfiles, char **files, int options)
-{
- int i;
- char_u *p;
- const int vse_what = xp->xp_context == EXPAND_BUFFERS ? VSE_BUFFER : VSE_NONE;
-
- /*
- * May change home directory back to "~"
- */
- if (options & WILD_HOME_REPLACE) {
- tilde_replace(str, numfiles, files);
- }
-
- if (options & WILD_ESCAPE) {
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_FILES_IN_PATH
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS
- || xp->xp_context == EXPAND_DIRECTORIES) {
- /*
- * Insert a backslash into a file name before a space, \, %, #
- * and wildmatch characters, except '~'.
- */
- for (i = 0; i < numfiles; ++i) {
- // for ":set path=" we need to escape spaces twice
- if (xp->xp_backslash == XP_BS_THREE) {
- p = vim_strsave_escaped((char_u *)files[i], (char_u *)" ");
- xfree(files[i]);
- files[i] = (char *)p;
-#if defined(BACKSLASH_IN_FILENAME)
- p = vim_strsave_escaped(files[i], (char_u *)" ");
- xfree(files[i]);
- files[i] = p;
-#endif
- }
-#ifdef BACKSLASH_IN_FILENAME
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i], vse_what);
-#else
- p = (char_u *)vim_strsave_fnameescape((const char *)files[i],
- xp->xp_shell ? VSE_SHELL : vse_what);
-#endif
- xfree(files[i]);
- files[i] = (char *)p;
-
- /* If 'str' starts with "\~", replace "~" at start of
- * files[i] with "\~". */
- if (str[0] == '\\' && str[1] == '~' && files[i][0] == '~') {
- escape_fname(&files[i]);
- }
- }
- xp->xp_backslash = XP_BS_NONE;
-
- /* If the first file starts with a '+' escape it. Otherwise it
- * could be seen as "+cmd". */
- if (*files[0] == '+') {
- escape_fname(&files[0]);
- }
- } else if (xp->xp_context == EXPAND_TAGS) {
- /*
- * Insert a backslash before characters in a tag name that
- * would terminate the ":tag" command.
- */
- for (i = 0; i < numfiles; i++) {
- p = vim_strsave_escaped((char_u *)files[i], (char_u *)"\\|\"");
- xfree(files[i]);
- files[i] = (char *)p;
- }
- }
- }
-}
-
/// Escape special characters in "fname", depending on "what":
///
/// @param[in] fname File name to escape.
@@ -4476,10 +3882,8 @@ char *vim_strsave_fnameescape(const char *const fname, const int what)
return p;
}
-/*
- * Put a backslash before the file name in "pp", which is in allocated memory.
- */
-static void escape_fname(char **pp)
+/// Put a backslash before the file name in "pp", which is in allocated memory.
+void escape_fname(char **pp)
{
char_u *p = xmalloc(STRLEN(*pp) + 2);
p[0] = '\\';
@@ -4503,1761 +3907,152 @@ void tilde_replace(char_u *orig_pat, int num_files, char **files)
}
}
-void cmdline_pum_display(bool changed_array)
-{
- pum_display(compl_match_array, compl_match_arraysize, compl_selected,
- changed_array, compl_startcol);
-}
-
-/*
- * Show all matches for completion on the command line.
- * Returns EXPAND_NOTHING when the character that triggered expansion should
- * be inserted like a normal character.
- */
-static int showmatches(expand_T *xp, int wildmenu)
-{
-#define L_SHOWFILE(m) (showtail \
- ? sm_gettail(files_found[m], false) : files_found[m])
- int num_files;
- char **files_found;
- int i, j, k;
- int maxlen;
- int lines;
- int columns;
- char_u *p;
- int lastlen;
- int attr;
- int showtail;
-
- if (xp->xp_numfiles == -1) {
- set_expand_context(xp);
- i = expand_cmdline(xp, ccline.cmdbuff, ccline.cmdpos,
- &num_files, &files_found);
- showtail = expand_showtail(xp);
- if (i != EXPAND_OK) {
- return i;
- }
- } else {
- num_files = xp->xp_numfiles;
- files_found = xp->xp_files;
- showtail = cmd_showtail;
- }
-
- bool compl_use_pum = (ui_has(kUICmdline)
- ? ui_has(kUIPopupmenu)
- : wildmenu && (wop_flags & WOP_PUM))
- || ui_has(kUIWildmenu);
-
- if (compl_use_pum) {
- assert(num_files >= 0);
- compl_match_arraysize = num_files;
- compl_match_array = xcalloc((size_t)compl_match_arraysize,
- sizeof(pumitem_T));
- for (i = 0; i < num_files; i++) {
- compl_match_array[i].pum_text = (char_u *)L_SHOWFILE(i);
- }
- char_u *endpos = (char_u *)(showtail ? sm_gettail(xp->xp_pattern, true) : xp->xp_pattern);
- if (ui_has(kUICmdline)) {
- compl_startcol = (int)(endpos - ccline.cmdbuff);
- } else {
- compl_startcol = cmd_screencol((int)(endpos - ccline.cmdbuff));
- }
- compl_selected = -1;
- cmdline_pum_display(true);
- return EXPAND_OK;
- }
-
- if (!wildmenu) {
- msg_didany = false; // lines_left will be set
- msg_start(); // prepare for paging
- msg_putchar('\n');
- ui_flush();
- cmdline_row = msg_row;
- msg_didany = false; // lines_left will be set again
- msg_start(); // prepare for paging
- }
-
- if (got_int) {
- got_int = false; // only int. the completion, not the cmd line
- } else if (wildmenu) {
- win_redr_status_matches(xp, num_files, files_found, -1, showtail);
- } else {
- // find the length of the longest file name
- maxlen = 0;
- for (i = 0; i < num_files; ++i) {
- if (!showtail && (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS)) {
- home_replace(NULL, files_found[i], (char *)NameBuff, MAXPATHL, true);
- j = vim_strsize((char *)NameBuff);
- } else {
- j = vim_strsize(L_SHOWFILE(i));
- }
- if (j > maxlen) {
- maxlen = j;
- }
- }
-
- if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- lines = num_files;
- } else {
- // compute the number of columns and lines for the listing
- maxlen += 2; // two spaces between file names
- columns = (Columns + 2) / maxlen;
- if (columns < 1) {
- columns = 1;
- }
- lines = (num_files + columns - 1) / columns;
- }
-
- attr = HL_ATTR(HLF_D); // find out highlighting for directories
-
- if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- msg_puts_attr(_("tagname"), HL_ATTR(HLF_T));
- msg_clr_eos();
- msg_advance(maxlen - 3);
- msg_puts_attr(_(" kind file\n"), HL_ATTR(HLF_T));
- }
-
- // list the files line by line
- for (i = 0; i < lines; ++i) {
- lastlen = 999;
- for (k = i; k < num_files; k += lines) {
- if (xp->xp_context == EXPAND_TAGS_LISTFILES) {
- msg_outtrans_attr((char_u *)files_found[k], HL_ATTR(HLF_D));
- p = (char_u *)files_found[k] + STRLEN(files_found[k]) + 1;
- msg_advance(maxlen + 1);
- msg_puts((const char *)p);
- msg_advance(maxlen + 3);
- msg_outtrans_long_attr(p + 2, HL_ATTR(HLF_D));
- break;
- }
- for (j = maxlen - lastlen; --j >= 0;) {
- msg_putchar(' ');
- }
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_SHELLCMD
- || xp->xp_context == EXPAND_BUFFERS) {
- // highlight directories
- if (xp->xp_numfiles != -1) {
- // Expansion was done before and special characters
- // were escaped, need to halve backslashes. Also
- // $HOME has been replaced with ~/.
- char_u *exp_path = expand_env_save_opt((char_u *)files_found[k], true);
- char_u *path = exp_path != NULL ? exp_path : (char_u *)files_found[k];
- char_u *halved_slash = backslash_halve_save(path);
- j = os_isdir(halved_slash);
- xfree(exp_path);
- if (halved_slash != path) {
- xfree(halved_slash);
- }
- } else {
- // Expansion was done here, file names are literal.
- j = os_isdir((char_u *)files_found[k]);
- }
- if (showtail) {
- p = (char_u *)L_SHOWFILE(k);
- } else {
- home_replace(NULL, files_found[k], (char *)NameBuff, MAXPATHL, true);
- p = NameBuff;
- }
- } else {
- j = false;
- p = (char_u *)L_SHOWFILE(k);
- }
- lastlen = msg_outtrans_attr(p, j ? attr : 0);
- }
- if (msg_col > 0) { // when not wrapped around
- msg_clr_eos();
- msg_putchar('\n');
- }
- ui_flush(); // show one line at a time
- if (got_int) {
- got_int = FALSE;
- break;
- }
- }
-
- /*
- * we redraw the command below the lines that we have just listed
- * This is a bit tricky, but it saves a lot of screen updating.
- */
- cmdline_row = msg_row; // will put it back later
- }
-
- if (xp->xp_numfiles == -1) {
- FreeWild(num_files, files_found);
- }
-
- return EXPAND_OK;
-}
-
-/// Private path_tail for showmatches() (and win_redr_status_matches()):
-/// Find tail of file name path, but ignore trailing "/".
-char *sm_gettail(char *s, bool eager)
-{
- char_u *p;
- char_u *t = (char_u *)s;
- int had_sep = false;
-
- for (p = (char_u *)s; *p != NUL;) {
- if (vim_ispathsep(*p)
-#ifdef BACKSLASH_IN_FILENAME
- && !rem_backslash(p)
-#endif
- ) {
- if (eager) {
- t = p + 1;
- } else {
- had_sep = true;
- }
- } else if (had_sep) {
- t = p;
- had_sep = FALSE;
- }
- MB_PTR_ADV(p);
- }
- return (char *)t;
-}
-
-/*
- * Return TRUE if we only need to show the tail of completion matches.
- * When not completing file names or there is a wildcard in the path FALSE is
- * returned.
- */
-static int expand_showtail(expand_T *xp)
+/// Get a pointer to the current command line info.
+CmdlineInfo *get_cmdline_info(void)
{
- char_u *s;
- char_u *end;
-
- // When not completing file names a "/" may mean something different.
- if (xp->xp_context != EXPAND_FILES
- && xp->xp_context != EXPAND_SHELLCMD
- && xp->xp_context != EXPAND_DIRECTORIES) {
- return FALSE;
- }
-
- end = (char_u *)path_tail(xp->xp_pattern);
- if (end == (char_u *)xp->xp_pattern) { // there is no path separator
- return false;
- }
-
- for (s = (char_u *)xp->xp_pattern; s < end; s++) {
- // Skip escaped wildcards. Only when the backslash is not a path
- // separator, on DOS the '*' "path\*\file" must not be skipped.
- if (rem_backslash(s)) {
- s++;
- } else if (vim_strchr("*?[", *s) != NULL) {
- return false;
- }
- }
- return TRUE;
-}
-
-/// Prepare a string for expansion.
-///
-/// When expanding file names: The string will be used with expand_wildcards().
-/// Copy "fname[len]" into allocated memory and add a '*' at the end.
-/// When expanding other names: The string will be used with regcomp(). Copy
-/// the name into allocated memory and prepend "^".
-///
-/// @param context EXPAND_FILES etc.
-char_u *addstar(char_u *fname, size_t len, int context)
- FUNC_ATTR_NONNULL_RET
-{
- char_u *retval;
- size_t i, j;
- size_t new_len;
- char_u *tail;
- int ends_in_star;
-
- if (context != EXPAND_FILES
- && context != EXPAND_FILES_IN_PATH
- && context != EXPAND_SHELLCMD
- && context != EXPAND_DIRECTORIES) {
- /*
- * Matching will be done internally (on something other than files).
- * So we convert the file-matching-type wildcards into our kind for
- * use with vim_regcomp(). First work out how long it will be:
- */
-
- // For help tags the translation is done in find_help_tags().
- // For a tag pattern starting with "/" no translation is needed.
- if (context == EXPAND_HELP
- || context == EXPAND_CHECKHEALTH
- || context == EXPAND_COLORS
- || context == EXPAND_COMPILER
- || context == EXPAND_OWNSYNTAX
- || context == EXPAND_FILETYPE
- || context == EXPAND_PACKADD
- || ((context == EXPAND_TAGS_LISTFILES || context == EXPAND_TAGS)
- && fname[0] == '/')) {
- retval = vim_strnsave(fname, len);
- } else {
- new_len = len + 2; // +2 for '^' at start, NUL at end
- for (i = 0; i < len; i++) {
- if (fname[i] == '*' || fname[i] == '~') {
- new_len++; /* '*' needs to be replaced by ".*"
- '~' needs to be replaced by "\~" */
- }
- // Buffer names are like file names. "." should be literal
- if (context == EXPAND_BUFFERS && fname[i] == '.') {
- new_len++; // "." becomes "\."
- }
- /* Custom expansion takes care of special things, match
- * backslashes literally (perhaps also for other types?) */
- if ((context == EXPAND_USER_DEFINED
- || context == EXPAND_USER_LIST) && fname[i] == '\\') {
- new_len++; // '\' becomes "\\"
- }
- }
- retval = xmalloc(new_len);
- {
- retval[0] = '^';
- j = 1;
- for (i = 0; i < len; i++, j++) {
- /* Skip backslash. But why? At least keep it for custom
- * expansion. */
- if (context != EXPAND_USER_DEFINED
- && context != EXPAND_USER_LIST
- && fname[i] == '\\'
- && ++i == len) {
- break;
- }
-
- switch (fname[i]) {
- case '*':
- retval[j++] = '.';
- break;
- case '~':
- retval[j++] = '\\';
- break;
- case '?':
- retval[j] = '.';
- continue;
- case '.':
- if (context == EXPAND_BUFFERS) {
- retval[j++] = '\\';
- }
- break;
- case '\\':
- if (context == EXPAND_USER_DEFINED
- || context == EXPAND_USER_LIST) {
- retval[j++] = '\\';
- }
- break;
- }
- retval[j] = fname[i];
- }
- retval[j] = NUL;
- }
- }
- } else {
- retval = xmalloc(len + 4);
- STRLCPY(retval, fname, len + 1);
-
- /*
- * Don't add a star to *, ~, ~user, $var or `cmd`.
- * * would become **, which walks the whole tree.
- * ~ would be at the start of the file name, but not the tail.
- * $ could be anywhere in the tail.
- * ` could be anywhere in the file name.
- * When the name ends in '$' don't add a star, remove the '$'.
- */
- tail = (char_u *)path_tail((char *)retval);
- ends_in_star = (len > 0 && retval[len - 1] == '*');
-#ifndef BACKSLASH_IN_FILENAME
- for (ssize_t k = (ssize_t)len - 2; k >= 0; k--) {
- if (retval[k] != '\\') {
- break;
- }
- ends_in_star = !ends_in_star;
- }
-#endif
- if ((*retval != '~' || tail != retval)
- && !ends_in_star
- && vim_strchr((char *)tail, '$') == NULL
- && vim_strchr((char *)retval, '`') == NULL) {
- retval[len++] = '*';
- } else if (len > 0 && retval[len - 1] == '$') {
- --len;
- }
- retval[len] = NUL;
- }
- return retval;
-}
-
-/*
- * Must parse the command line so far to work out what context we are in.
- * Completion can then be done based on that context.
- * This routine sets the variables:
- * xp->xp_pattern The start of the pattern to be expanded within
- * the command line (ends at the cursor).
- * xp->xp_context The type of thing to expand. Will be one of:
- *
- * EXPAND_UNSUCCESSFUL Used sometimes when there is something illegal on
- * the command line, like an unknown command. Caller
- * should beep.
- * EXPAND_NOTHING Unrecognised context for completion, use char like
- * a normal char, rather than for completion. eg
- * :s/^I/
- * EXPAND_COMMANDS Cursor is still touching the command, so complete
- * it.
- * EXPAND_BUFFERS Complete file names for :buf and :sbuf commands.
- * EXPAND_FILES After command with EX_XFILE set, or after setting
- * with P_EXPAND set. eg :e ^I, :w>>^I
- * EXPAND_DIRECTORIES In some cases this is used instead of the latter
- * when we know only directories are of interest. eg
- * :set dir=^I
- * EXPAND_SHELLCMD After ":!cmd", ":r !cmd" or ":w !cmd".
- * EXPAND_SETTINGS Complete variable names. eg :set d^I
- * EXPAND_BOOL_SETTINGS Complete boolean variables only, eg :set no^I
- * EXPAND_TAGS Complete tags from the files in p_tags. eg :ta a^I
- * EXPAND_TAGS_LISTFILES As above, but list filenames on ^D, after :tselect
- * EXPAND_HELP Complete tags from the file 'helpfile'/tags
- * EXPAND_EVENTS Complete event names
- * EXPAND_SYNTAX Complete :syntax command arguments
- * EXPAND_HIGHLIGHT Complete highlight (syntax) group names
- * EXPAND_AUGROUP Complete autocommand group names
- * EXPAND_USER_VARS Complete user defined variable names, eg :unlet a^I
- * EXPAND_MAPPINGS Complete mapping and abbreviation names,
- * eg :unmap a^I , :cunab x^I
- * EXPAND_FUNCTIONS Complete internal or user defined function names,
- * eg :call sub^I
- * EXPAND_USER_FUNC Complete user defined function names, eg :delf F^I
- * EXPAND_EXPRESSION Complete internal or user defined function/variable
- * names in expressions, eg :while s^I
- * EXPAND_ENV_VARS Complete environment variable names
- * EXPAND_USER Complete user names
- */
-void set_expand_context(expand_T *xp)
-{
- // only expansion for ':', '>' and '=' command-lines
- if (ccline.cmdfirstc != ':'
- && ccline.cmdfirstc != '>' && ccline.cmdfirstc != '='
- && !ccline.input_fn) {
- xp->xp_context = EXPAND_NOTHING;
- return;
- }
- set_cmd_context(xp, ccline.cmdbuff, ccline.cmdlen, ccline.cmdpos, true);
-}
-
-/// @param str start of command line
-/// @param len length of command line (excl. NUL)
-/// @param col position of cursor
-/// @param use_ccline use ccline for info
-void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline)
-{
- char_u old_char = NUL;
-
- /*
- * Avoid a UMR warning from Purify, only save the character if it has been
- * written before.
- */
- if (col < len) {
- old_char = str[col];
- }
- str[col] = NUL;
- const char *nextcomm = (const char *)str;
-
- if (use_ccline && ccline.cmdfirstc == '=') {
- // pass CMD_SIZE because there is no real command
- set_context_for_expression(xp, (char *)str, CMD_SIZE);
- } else if (use_ccline && ccline.input_fn) {
- xp->xp_context = ccline.xp_context;
- xp->xp_pattern = (char *)ccline.cmdbuff;
- xp->xp_arg = (char *)ccline.xp_arg;
- } else {
- while (nextcomm != NULL) {
- nextcomm = set_one_cmd_context(xp, nextcomm);
- }
- }
-
- /* Store the string here so that call_user_expand_func() can get to them
- * easily. */
- xp->xp_line = (char *)str;
- xp->xp_col = col;
-
- str[col] = old_char;
-}
-
-/// Expand the command line "str" from context "xp".
-/// "xp" must have been set by set_cmd_context().
-/// xp->xp_pattern points into "str", to where the text that is to be expanded
-/// starts.
-/// Returns EXPAND_UNSUCCESSFUL when there is something illegal before the
-/// cursor.
-/// Returns EXPAND_NOTHING when there is nothing to expand, might insert the
-/// key that triggered expansion literally.
-/// Returns EXPAND_OK otherwise.
-///
-/// @param str start of command line
-/// @param col position of cursor
-/// @param matchcount return: nr of matches
-/// @param matches return: array of pointers to matches
-int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char ***matches)
-{
- char_u *file_str = NULL;
- int options = WILD_ADD_SLASH|WILD_SILENT;
-
- if (xp->xp_context == EXPAND_UNSUCCESSFUL) {
- beep_flush();
- return EXPAND_UNSUCCESSFUL; // Something illegal on command line
- }
- if (xp->xp_context == EXPAND_NOTHING) {
- // Caller can use the character as a normal char instead
- return EXPAND_NOTHING;
- }
-
- // add star to file name, or convert to regexp if not exp. files.
- assert((str + col) - (char_u *)xp->xp_pattern >= 0);
- xp->xp_pattern_len = (size_t)((str + col) - (char_u *)xp->xp_pattern);
- file_str = addstar((char_u *)xp->xp_pattern, xp->xp_pattern_len, xp->xp_context);
-
- if (p_wic) {
- options += WILD_ICASE;
- }
-
- // find all files that match the description
- if (ExpandFromContext(xp, file_str, matchcount, matches, options) == FAIL) {
- *matchcount = 0;
- *matches = NULL;
- }
- xfree(file_str);
-
- return EXPAND_OK;
-}
-
-// Cleanup matches for help tags:
-// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
-// tag matches it. Otherwise remove "@en" if "en" is the only language.
-static void cleanup_help_tags(int num_file, char **file)
-{
- char_u buf[4];
- char_u *p = buf;
-
- if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
- *p++ = '@';
- *p++ = p_hlg[0];
- *p++ = p_hlg[1];
- }
- *p = NUL;
-
- for (int i = 0; i < num_file; i++) {
- int len = (int)STRLEN(file[i]) - 3;
- if (len <= 0) {
- continue;
- }
- if (STRCMP(file[i] + len, "@en") == 0) {
- // Sorting on priority means the same item in another language may
- // be anywhere. Search all items for a match up to the "@en".
- int j;
- for (j = 0; j < num_file; j++) {
- if (j != i
- && (int)STRLEN(file[j]) == len + 3
- && STRNCMP(file[i], file[j], len + 1) == 0) {
- break;
- }
- }
- if (j == num_file) {
- // item only exists with @en, remove it
- file[i][len] = NUL;
- }
- }
- }
-
- if (*buf != NUL) {
- for (int i = 0; i < num_file; i++) {
- int len = (int)STRLEN(file[i]) - 3;
- if (len <= 0) {
- continue;
- }
- if (STRCMP(file[i] + len, buf) == 0) {
- // remove the default language
- file[i][len] = NUL;
- }
- }
- }
-}
-
-typedef char *(*ExpandFunc)(expand_T *, int);
-
-/// Do the expansion based on xp->xp_context and "pat".
-///
-/// @param options WILD_ flags
-static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char ***file, int options)
-{
- regmatch_T regmatch;
- int ret;
- int flags;
-
- flags = EW_DIR; // include directories
- if (options & WILD_LIST_NOTFOUND) {
- flags |= EW_NOTFOUND;
- }
- if (options & WILD_ADD_SLASH) {
- flags |= EW_ADDSLASH;
- }
- if (options & WILD_KEEP_ALL) {
- flags |= EW_KEEPALL;
- }
- if (options & WILD_SILENT) {
- flags |= EW_SILENT;
- }
- if (options & WILD_NOERROR) {
- flags |= EW_NOERROR;
- }
- if (options & WILD_ALLLINKS) {
- flags |= EW_ALLLINKS;
- }
-
- if (xp->xp_context == EXPAND_FILES
- || xp->xp_context == EXPAND_DIRECTORIES
- || xp->xp_context == EXPAND_FILES_IN_PATH) {
- /*
- * Expand file or directory names.
- */
- int free_pat = FALSE;
- int i;
-
- // for ":set path=" and ":set tags=" halve backslashes for escaped space
- if (xp->xp_backslash != XP_BS_NONE) {
- free_pat = TRUE;
- pat = vim_strsave(pat);
- for (i = 0; pat[i]; ++i) {
- if (pat[i] == '\\') {
- if (xp->xp_backslash == XP_BS_THREE
- && pat[i + 1] == '\\'
- && pat[i + 2] == '\\'
- && pat[i + 3] == ' ') {
- STRMOVE(pat + i, pat + i + 3);
- }
- if (xp->xp_backslash == XP_BS_ONE
- && pat[i + 1] == ' ') {
- STRMOVE(pat + i, pat + i + 1);
- }
- }
- }
- }
-
- if (xp->xp_context == EXPAND_FILES) {
- flags |= EW_FILE;
- } else if (xp->xp_context == EXPAND_FILES_IN_PATH) {
- flags |= (EW_FILE | EW_PATH);
- } else {
- flags = (flags | EW_DIR) & ~EW_FILE;
- }
- if (options & WILD_ICASE) {
- flags |= EW_ICASE;
- }
-
- // Expand wildcards, supporting %:h and the like.
- ret = expand_wildcards_eval(&pat, num_file, file, flags);
- if (free_pat) {
- xfree(pat);
- }
-#ifdef BACKSLASH_IN_FILENAME
- if (p_csl[0] != NUL && (options & WILD_IGNORE_COMPLETESLASH) == 0) {
- for (int i = 0; i < *num_file; i++) {
- char_u *ptr = (*file)[i];
- while (*ptr != NUL) {
- if (p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (p_csl[0] == 'b' && *ptr == '/') {
- *ptr = '\\';
- }
- ptr += utfc_ptr2len(ptr);
- }
- }
- }
-#endif
- return ret;
- }
-
- *file = NULL;
- *num_file = 0;
- if (xp->xp_context == EXPAND_HELP) {
- /* With an empty argument we would get all the help tags, which is
- * very slow. Get matches for "help" instead. */
- if (find_help_tags(*pat == NUL ? "help" : (char *)pat,
- num_file, file, false) == OK) {
- cleanup_help_tags(*num_file, *file);
- return OK;
- }
- return FAIL;
- }
-
- if (xp->xp_context == EXPAND_SHELLCMD) {
- *file = NULL;
- expand_shellcmd(pat, num_file, file, flags);
- return OK;
- }
- if (xp->xp_context == EXPAND_OLD_SETTING) {
- ExpandOldSetting(num_file, file);
- return OK;
- }
- if (xp->xp_context == EXPAND_BUFFERS) {
- return ExpandBufnames((char *)pat, num_file, file, options);
- }
- if (xp->xp_context == EXPAND_DIFF_BUFFERS) {
- return ExpandBufnames((char *)pat, num_file, file, options | BUF_DIFF_FILTER);
- }
- if (xp->xp_context == EXPAND_TAGS
- || xp->xp_context == EXPAND_TAGS_LISTFILES) {
- return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
- }
- if (xp->xp_context == EXPAND_COLORS) {
- char *directories[] = { "colors", NULL };
- return ExpandRTDir(pat, DIP_START + DIP_OPT + DIP_LUA, num_file, file, directories);
- }
- if (xp->xp_context == EXPAND_COMPILER) {
- char *directories[] = { "compiler", NULL };
- return ExpandRTDir(pat, DIP_LUA, num_file, file, directories);
- }
- if (xp->xp_context == EXPAND_OWNSYNTAX) {
- char *directories[] = { "syntax", NULL };
- return ExpandRTDir(pat, 0, num_file, file, directories);
- }
- if (xp->xp_context == EXPAND_FILETYPE) {
- char *directories[] = { "syntax", "indent", "ftplugin", NULL };
- return ExpandRTDir(pat, DIP_LUA, num_file, file, directories);
- }
- if (xp->xp_context == EXPAND_USER_LIST) {
- return ExpandUserList(xp, num_file, file);
- }
- if (xp->xp_context == EXPAND_USER_LUA) {
- return ExpandUserLua(xp, num_file, file);
- }
- if (xp->xp_context == EXPAND_PACKADD) {
- return ExpandPackAddDir(pat, num_file, file);
- }
-
- // When expanding a function name starting with s:, match the <SNR>nr_
- // prefix.
- char *tofree = NULL;
- if (xp->xp_context == EXPAND_USER_FUNC && STRNCMP(pat, "^s:", 3) == 0) {
- const size_t len = STRLEN(pat) + 20;
-
- tofree = xmalloc(len);
- snprintf(tofree, len, "^<SNR>\\d\\+_%s", pat + 3);
- pat = (char_u *)tofree;
- }
-
- if (xp->xp_context == EXPAND_LUA) {
- ILOG("PAT %s", pat);
- return nlua_expand_pat(xp, pat, num_file, file);
- }
-
- regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0);
- if (regmatch.regprog == NULL) {
- return FAIL;
- }
-
- // set ignore-case according to p_ic, p_scs and pat
- regmatch.rm_ic = ignorecase(pat);
-
- if (xp->xp_context == EXPAND_SETTINGS
- || xp->xp_context == EXPAND_BOOL_SETTINGS) {
- ret = ExpandSettings(xp, &regmatch, num_file, file);
- } else if (xp->xp_context == EXPAND_MAPPINGS) {
- ret = ExpandMappings(&regmatch, num_file, file);
- } else if (xp->xp_context == EXPAND_USER_DEFINED) {
- ret = ExpandUserDefined(xp, &regmatch, num_file, file);
- } else {
- static struct expgen {
- int context;
- ExpandFunc func;
- int ic;
- int escaped;
- } tab[] = {
- { EXPAND_COMMANDS, get_command_name, false, true },
- { EXPAND_BEHAVE, get_behave_arg, true, true },
- { EXPAND_MAPCLEAR, get_mapclear_arg, true, true },
- { EXPAND_MESSAGES, get_messages_arg, true, true },
- { EXPAND_HISTORY, get_history_arg, true, true },
- { EXPAND_USER_COMMANDS, get_user_commands, false, true },
- { EXPAND_USER_ADDR_TYPE, get_user_cmd_addr_type, false, true },
- { EXPAND_USER_CMD_FLAGS, get_user_cmd_flags, false, true },
- { EXPAND_USER_NARGS, get_user_cmd_nargs, false, true },
- { EXPAND_USER_COMPLETE, get_user_cmd_complete, false, true },
- { EXPAND_USER_VARS, get_user_var_name, false, true },
- { EXPAND_FUNCTIONS, get_function_name, false, true },
- { EXPAND_USER_FUNC, get_user_func_name, false, true },
- { EXPAND_EXPRESSION, get_expr_name, false, true },
- { EXPAND_MENUS, get_menu_name, false, true },
- { EXPAND_MENUNAMES, get_menu_names, false, true },
- { EXPAND_SYNTAX, get_syntax_name, true, true },
- { EXPAND_SYNTIME, get_syntime_arg, true, true },
- { EXPAND_HIGHLIGHT, (ExpandFunc)get_highlight_name, true, true },
- { EXPAND_EVENTS, expand_get_event_name, true, true },
- { EXPAND_AUGROUP, expand_get_augroup_name, true, true },
- { EXPAND_CSCOPE, get_cscope_name, true, true },
- { EXPAND_SIGN, get_sign_name, true, true },
- { EXPAND_PROFILE, get_profile_name, true, true },
-#ifdef HAVE_WORKING_LIBINTL
- { EXPAND_LANGUAGE, get_lang_arg, true, false },
- { EXPAND_LOCALES, get_locales, true, false },
-#endif
- { EXPAND_ENV_VARS, get_env_name, true, true },
- { EXPAND_USER, get_users, true, false },
- { EXPAND_ARGLIST, get_arglist_name, true, false },
- { EXPAND_CHECKHEALTH, get_healthcheck_names, true, false },
- };
- int i;
-
- /*
- * Find a context in the table and call the ExpandGeneric() with the
- * right function to do the expansion.
- */
- ret = FAIL;
- for (i = 0; i < (int)ARRAY_SIZE(tab); ++i) {
- if (xp->xp_context == tab[i].context) {
- if (tab[i].ic) {
- regmatch.rm_ic = TRUE;
- }
- ExpandGeneric(xp, &regmatch, num_file, file, tab[i].func, tab[i].escaped);
- ret = OK;
- break;
- }
- }
- }
-
- vim_regfree(regmatch.regprog);
- xfree(tofree);
-
- return ret;
+ return &ccline;
}
-/// Expand a list of names.
-///
-/// Generic function for command line completion. It calls a function to
-/// obtain strings, one by one. The strings are matched against a regexp
-/// program. Matching strings are copied into an array, which is returned.
-///
-/// @param func returns a string from the list
-static void ExpandGeneric(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file,
- CompleteListItemGetter func, int escaped)
+unsigned get_cmdline_last_prompt_id(void)
{
- int i;
- size_t count = 0;
- char_u *str;
-
- // count the number of matching names
- for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
- if (str == NULL) { // end of list
- break;
- }
- if (*str == NUL) { // skip empty strings
- continue;
- }
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
- count++;
- }
- }
- if (count == 0) {
- return;
- }
- assert(count < INT_MAX);
- *num_file = (int)count;
- *file = xmalloc(count * sizeof(char_u *));
-
- // copy the matching names into allocated memory
- count = 0;
- for (i = 0;; i++) {
- str = (char_u *)(*func)(xp, i);
- if (str == NULL) { // End of list.
- break;
- }
- if (*str == NUL) { // Skip empty strings.
- continue;
- }
- if (vim_regexec(regmatch, (char *)str, (colnr_T)0)) {
- if (escaped) {
- str = vim_strsave_escaped(str, (char_u *)" \t\\.");
- } else {
- str = vim_strsave(str);
- }
- (*file)[count++] = (char *)str;
- if (func == get_menu_names) {
- // Test for separator added by get_menu_names().
- str += STRLEN(str) - 1;
- if (*str == '\001') {
- *str = '.';
- }
- }
- }
- }
-
- // Sort the results. Keep menu's in the specified order.
- if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS) {
- if (xp->xp_context == EXPAND_EXPRESSION
- || xp->xp_context == EXPAND_FUNCTIONS
- || xp->xp_context == EXPAND_USER_FUNC) {
- // <SNR> functions should be sorted to the end.
- qsort((void *)*file, (size_t)*num_file, sizeof(char_u *),
- sort_func_compare);
- } else {
- sort_strings(*file, *num_file);
- }
- }
-
- /* Reset the variables used for special highlight names expansion, so that
- * they don't show up when getting normal highlight names by ID. */
- reset_expand_highlight();
+ return last_prompt_id;
}
-/// Complete a shell command.
-///
-/// @param filepat is a pattern to match with command names.
-/// @param[out] num_file is pointer to number of matches.
-/// @param[out] file is pointer to array of pointers to matches.
-/// *file will either be set to NULL or point to
-/// allocated memory.
-/// @param flagsarg is a combination of EW_* flags.
-static void expand_shellcmd(char_u *filepat, int *num_file, char ***file, int flagsarg)
- FUNC_ATTR_NONNULL_ALL
+/// Get pointer to the command line info to use. save_cmdline() may clear
+/// ccline and put the previous value in ccline.prev_ccline.
+static CmdlineInfo *get_ccline_ptr(void)
{
- char_u *pat;
- int i;
- char_u *path = NULL;
- garray_T ga;
- char *buf = xmalloc(MAXPATHL);
- size_t l;
- char_u *s, *e;
- int flags = flagsarg;
- int ret;
- bool did_curdir = false;
-
- // for ":set path=" and ":set tags=" halve backslashes for escaped space
- pat = vim_strsave(filepat);
- for (i = 0; pat[i]; ++i) {
- if (pat[i] == '\\' && pat[i + 1] == ' ') {
- STRMOVE(pat + i, pat + i + 1);
- }
- }
-
- flags |= EW_FILE | EW_EXEC | EW_SHELLCMD;
-
- bool mustfree = false; // Track memory allocation for *path.
- if (pat[0] == '.' && (vim_ispathsep(pat[1])
- || (pat[1] == '.' && vim_ispathsep(pat[2])))) {
- path = (char_u *)".";
+ if ((State & MODE_CMDLINE) == 0) {
+ return NULL;
+ } else if (ccline.cmdbuff != NULL) {
+ return &ccline;
+ } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) {
+ return ccline.prev_ccline;
} else {
- // For an absolute name we don't use $PATH.
- if (!path_is_absolute(pat)) {
- path = (char_u *)vim_getenv("PATH");
- }
- if (path == NULL) {
- path = (char_u *)"";
- } else {
- mustfree = true;
- }
- }
-
- /*
- * Go over all directories in $PATH. Expand matches in that directory and
- * collect them in "ga". When "." is not in $PATH also expaned for the
- * current directory, to find "subdir/cmd".
- */
- ga_init(&ga, (int)sizeof(char *), 10);
- hashtab_T found_ht;
- hash_init(&found_ht);
- for (s = path;; s = e) {
- e = (char_u *)vim_strchr((char *)s, ENV_SEPCHAR);
- if (e == NULL) {
- e = s + STRLEN(s);
- }
-
- if (*s == NUL) {
- if (did_curdir) {
- break;
- }
- // Find directories in the current directory, path is empty.
- did_curdir = true;
- flags |= EW_DIR;
- } else if (STRNCMP(s, ".", e - s) == 0) {
- did_curdir = true;
- flags |= EW_DIR;
- } else {
- // Do not match directories inside a $PATH item.
- flags &= ~EW_DIR;
- }
-
- l = (size_t)(e - s);
- if (l > MAXPATHL - 5) {
- break;
- }
- STRLCPY(buf, s, l + 1);
- add_pathsep(buf);
- l = STRLEN(buf);
- STRLCPY(buf + l, pat, MAXPATHL - l);
-
- // Expand matches in one directory of $PATH.
- ret = expand_wildcards(1, &buf, num_file, file, flags);
- if (ret == OK) {
- ga_grow(&ga, *num_file);
- {
- for (i = 0; i < *num_file; i++) {
- char_u *name = (char_u *)(*file)[i];
-
- if (STRLEN(name) > l) {
- // Check if this name was already found.
- hash_T hash = hash_hash(name + l);
- hashitem_T *hi =
- hash_lookup(&found_ht, (const char *)(name + l),
- STRLEN(name + l), hash);
- if (HASHITEM_EMPTY(hi)) {
- // Remove the path that was prepended.
- STRMOVE(name, name + l);
- ((char_u **)ga.ga_data)[ga.ga_len++] = name;
- hash_add_item(&found_ht, hi, name, hash);
- name = NULL;
- }
- }
- xfree(name);
- }
- xfree(*file);
- }
- }
- if (*e != NUL) {
- ++e;
- }
- }
- *file = ga.ga_data;
- *num_file = ga.ga_len;
-
- xfree(buf);
- xfree(pat);
- if (mustfree) {
- xfree(path);
- }
- hash_clear(&found_ht);
-}
-
-/// Call "user_expand_func()" to invoke a user defined Vim script function and
-/// return the result (either a string, a List or NULL).
-static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T *xp, int *num_file,
- char ***file)
- FUNC_ATTR_NONNULL_ALL
-{
- char_u keep = 0;
- typval_T args[4];
- char_u *pat = NULL;
- const sctx_T save_current_sctx = current_sctx;
-
- if (xp->xp_arg == NULL || xp->xp_arg[0] == '\0' || xp->xp_line == NULL) {
return NULL;
}
- *num_file = 0;
- *file = NULL;
-
- if (ccline.cmdbuff != NULL) {
- keep = ccline.cmdbuff[ccline.cmdlen];
- ccline.cmdbuff[ccline.cmdlen] = 0;
- }
-
- pat = vim_strnsave((char_u *)xp->xp_pattern, xp->xp_pattern_len);
- args[0].v_type = VAR_STRING;
- args[1].v_type = VAR_STRING;
- args[2].v_type = VAR_NUMBER;
- args[3].v_type = VAR_UNKNOWN;
- args[0].vval.v_string = (char *)pat;
- args[1].vval.v_string = xp->xp_line;
- args[2].vval.v_number = xp->xp_col;
-
- current_sctx = xp->xp_script_ctx;
-
- void *const ret = user_expand_func((char_u *)xp->xp_arg, 3, args);
-
- current_sctx = save_current_sctx;
- if (ccline.cmdbuff != NULL) {
- ccline.cmdbuff[ccline.cmdlen] = keep;
- }
-
- xfree(pat);
- return ret;
}
-/// Expand names with a function defined by the user.
-static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char ***file)
+/// Get the current command-line type.
+/// Returns ':' or '/' or '?' or '@' or '>' or '-'
+/// Only works when the command line is being edited.
+/// Returns NUL when something is wrong.
+static int get_cmdline_type(void)
{
- char_u *e;
- garray_T ga;
-
- char_u *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file,
- file);
-
- if (retstr == NULL) {
- return FAIL;
- }
-
- ga_init(&ga, (int)sizeof(char *), 3);
- for (char_u *s = retstr; *s != NUL; s = e) {
- e = (char_u *)vim_strchr((char *)s, '\n');
- if (e == NULL) {
- e = s + STRLEN(s);
- }
- const char_u keep = *e;
- *e = NUL;
-
- const bool skip = xp->xp_pattern[0]
- && vim_regexec(regmatch, (char *)s, (colnr_T)0) == 0;
- *e = keep;
- if (!skip) {
- GA_APPEND(char_u *, &ga, vim_strnsave(s, (size_t)(e - s)));
- }
-
- if (*e != NUL) {
- e++;
- }
- }
- xfree(retstr);
- *file = ga.ga_data;
- *num_file = ga.ga_len;
- return OK;
-}
+ CmdlineInfo *p = get_ccline_ptr();
-/// Expand names with a list returned by a function defined by the user.
-static int ExpandUserList(expand_T *xp, int *num_file, char ***file)
-{
- list_T *const retlist = call_user_expand_func((user_expand_func_T)call_func_retlist, xp, num_file,
- file);
- if (retlist == NULL) {
- return FAIL;
+ if (p == NULL) {
+ return NUL;
}
-
- garray_T ga;
- ga_init(&ga, (int)sizeof(char *), 3);
- // Loop over the items in the list.
- TV_LIST_ITER_CONST(retlist, li, {
- if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
- || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
- continue; // Skip non-string items and empty strings.
- }
-
- GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
- });
- tv_list_unref(retlist);
-
- *file = ga.ga_data;
- *num_file = ga.ga_len;
- return OK;
-}
-
-static int ExpandUserLua(expand_T *xp, int *num_file, char ***file)
-{
- typval_T rettv;
- nlua_call_user_expand_func(xp, &rettv);
- if (rettv.v_type != VAR_LIST) {
- tv_clear(&rettv);
- return FAIL;
+ if (p->cmdfirstc == NUL) {
+ return (p->input_fn) ? '@' : '-';
}
-
- list_T *const retlist = rettv.vval.v_list;
-
- garray_T ga;
- ga_init(&ga, (int)sizeof(char *), 3);
- // Loop over the items in the list.
- TV_LIST_ITER_CONST(retlist, li, {
- if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
- || TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
- continue; // Skip non-string items and empty strings.
- }
-
- GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
- });
- tv_list_unref(retlist);
-
- *file = ga.ga_data;
- *num_file = ga.ga_len;
- return OK;
+ return p->cmdfirstc;
}
-/// Expand color scheme, compiler or filetype names.
-/// Search from 'runtimepath':
-/// 'runtimepath'/{dirnames}/{pat}.vim
-/// When "flags" has DIP_START: search also from 'start' of 'packpath':
-/// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim
-/// When "flags" has DIP_OPT: search also from 'opt' of 'packpath':
-/// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim
-/// When "flags" has DIP_LUA: search also performed for .lua files
-/// "dirnames" is an array with one or more directory names.
-static int ExpandRTDir(char_u *pat, int flags, int *num_file, char ***file, char *dirnames[])
+/// Get the current command line in allocated memory.
+/// Only works when the command line is being edited.
+/// Returns NULL when something is wrong.
+static char_u *get_cmdline_str(void)
{
- *num_file = 0;
- *file = NULL;
- size_t pat_len = STRLEN(pat);
-
- garray_T ga;
- ga_init(&ga, (int)sizeof(char *), 10);
-
- // TODO(bfredl): this is bullshit, exandpath should not reinvent path logic.
- for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 7;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "%s/%s*.vim", dirnames[i], pat);
- globpath(p_rtp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "%s/%s*.lua", dirnames[i], pat);
- globpath(p_rtp, s, &ga, 0);
- }
- xfree(s);
- }
-
- if (flags & DIP_START) {
- for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 22;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "pack/*/start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- }
- xfree(s);
- }
-
- for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 22;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- }
- xfree(s);
- }
- }
-
- if (flags & DIP_OPT) {
- for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 20;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- }
- xfree(s);
- }
-
- for (int i = 0; dirnames[i] != NULL; i++) {
- size_t size = STRLEN(dirnames[i]) + pat_len + 20;
- char_u *s = xmalloc(size);
- snprintf((char *)s, size, "opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- if (flags & DIP_LUA) {
- snprintf((char *)s, size, "opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- }
- xfree(s);
- }
- }
-
- for (int i = 0; i < ga.ga_len; i++) {
- char_u *match = ((char_u **)ga.ga_data)[i];
- char_u *s = match;
- char_u *e = s + STRLEN(s);
- if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0
- || ((flags & DIP_LUA)
- && STRNICMP(e - 4, ".lua", 4) == 0))) {
- e -= 4;
- for (s = e; s > match; MB_PTR_BACK(match, s)) {
- if (vim_ispathsep(*s)) {
- break;
- }
- }
- s++;
- *e = NUL;
- assert((e - s) + 1 >= 0);
- memmove(match, s, (size_t)(e - s) + 1);
- }
+ if (cmdline_star > 0) {
+ return NULL;
}
+ CmdlineInfo *p = get_ccline_ptr();
- if (GA_EMPTY(&ga)) {
- return FAIL;
+ if (p == NULL) {
+ return NULL;
}
-
- /* Sort and remove duplicates which can happen when specifying multiple
- * directories in dirnames. */
- ga_remove_duplicate_strings(&ga);
-
- *file = ga.ga_data;
- *num_file = ga.ga_len;
- return OK;
+ return vim_strnsave(p->cmdbuff, (size_t)p->cmdlen);
}
-/// Expand loadplugin names:
-/// 'packpath'/pack/ * /opt/{pat}
-static int ExpandPackAddDir(char_u *pat, int *num_file, char ***file)
+/// Get the current command-line completion type.
+static char_u *get_cmdline_completion(void)
{
- garray_T ga;
-
- *num_file = 0;
- *file = NULL;
- size_t pat_len = STRLEN(pat);
- ga_init(&ga, (int)sizeof(char *), 10);
-
- size_t buflen = pat_len + 26;
- char_u *s = xmalloc(buflen);
- snprintf((char *)s, buflen, "pack/*/opt/%s*", pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- snprintf((char *)s, buflen, "opt/%s*", pat); // NOLINT
- globpath(p_pp, s, &ga, 0);
- xfree(s);
-
- for (int i = 0; i < ga.ga_len; i++) {
- char_u *match = ((char_u **)ga.ga_data)[i];
- s = (char_u *)path_tail((char *)match);
- memmove(match, s, STRLEN(s) + 1);
- }
-
- if (GA_EMPTY(&ga)) {
- return FAIL;
+ if (cmdline_star > 0) {
+ return NULL;
}
+ CmdlineInfo *p = get_ccline_ptr();
- // Sort and remove duplicates which can happen when specifying multiple
- // directories in dirnames.
- ga_remove_duplicate_strings(&ga);
-
- *file = ga.ga_data;
- *num_file = ga.ga_len;
- return OK;
-}
-
-/// Expand `file` for all comma-separated directories in `path`.
-/// Adds matches to `ga`.
-void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options)
-{
- expand_T xpc;
- ExpandInit(&xpc);
- xpc.xp_context = EXPAND_FILES;
-
- char_u *buf = xmalloc(MAXPATHL);
-
- // Loop over all entries in {path}.
- while (*path != NUL) {
- // Copy one item of the path to buf[] and concatenate the file name.
- copy_option_part((char **)&path, (char *)buf, MAXPATHL, ",");
- if (STRLEN(buf) + STRLEN(file) + 2 < MAXPATHL) {
- add_pathsep((char *)buf);
- STRCAT(buf, file); // NOLINT
-
- char **p;
- int num_p = 0;
- (void)ExpandFromContext(&xpc, buf, &num_p, &p,
- WILD_SILENT | expand_options);
- if (num_p > 0) {
- ExpandEscape(&xpc, buf, num_p, p, WILD_SILENT | expand_options);
-
- // Concatenate new results to previous ones.
- ga_grow(ga, num_p);
- // take over the pointers and put them in "ga"
- for (int i = 0; i < num_p; i++) {
- ((char_u **)ga->ga_data)[ga->ga_len] = (char_u *)p[i];
- ga->ga_len++;
- }
- xfree(p);
- }
+ if (p != NULL && p->xpc != NULL) {
+ set_expand_context(p->xpc);
+ char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context);
+ if (cmd_compl != NULL) {
+ return vim_strsave((char_u *)cmd_compl);
}
}
- xfree(buf);
-}
-
-/*********************************
-* Command line history stuff *
-*********************************/
-
-/// Translate a history character to the associated type number
-static HistoryType hist_char2type(const int c)
- FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT
-{
- switch (c) {
- case ':':
- return HIST_CMD;
- case '=':
- return HIST_EXPR;
- case '@':
- return HIST_INPUT;
- case '>':
- return HIST_DEBUG;
- case NUL:
- case '/':
- case '?':
- return HIST_SEARCH;
- default:
- return HIST_INVALID;
- }
- // Silence -Wreturn-type
- return 0;
-}
-
-/*
- * Table of history names.
- * These names are used in :history and various hist...() functions.
- * It is sufficient to give the significant prefix of a history name.
- */
-
-static char *(history_names[]) =
-{
- "cmd",
- "search",
- "expr",
- "input",
- "debug",
- NULL
-};
-
-/*
- * Function given to ExpandGeneric() to obtain the possible first
- * arguments of the ":history command.
- */
-static char *get_history_arg(expand_T *xp, int idx)
-{
- static char_u compl[2] = { NUL, NUL };
- char *short_names = ":=@>?/";
- int short_names_count = (int)STRLEN(short_names);
- int history_name_count = ARRAY_SIZE(history_names) - 1;
-
- if (idx < short_names_count) {
- compl[0] = (char_u)short_names[idx];
- return (char *)compl;
- }
- if (idx < short_names_count + history_name_count) {
- return history_names[idx - short_names_count];
- }
- if (idx == short_names_count + history_name_count) {
- return "all";
- }
return NULL;
}
-/// Initialize command line history.
-/// Also used to re-allocate history tables when size changes.
-void init_history(void)
-{
- assert(p_hi >= 0 && p_hi <= INT_MAX);
- int newlen = (int)p_hi;
- int oldlen = hislen;
-
- // If history tables size changed, reallocate them.
- // Tables are circular arrays (current position marked by hisidx[type]).
- // On copying them to the new arrays, we take the chance to reorder them.
- if (newlen != oldlen) {
- for (int type = 0; type < HIST_COUNT; type++) {
- histentry_T *temp = (newlen
- ? xmalloc((size_t)newlen * sizeof(*temp))
- : NULL);
-
- int j = hisidx[type];
- if (j >= 0) {
- // old array gets partitioned this way:
- // [0 , i1 ) --> newest entries to be deleted
- // [i1 , i1 + l1) --> newest entries to be copied
- // [i1 + l1 , i2 ) --> oldest entries to be deleted
- // [i2 , i2 + l2) --> oldest entries to be copied
- int l1 = MIN(j + 1, newlen); // how many newest to copy
- int l2 = MIN(newlen, oldlen) - l1; // how many oldest to copy
- int i1 = j + 1 - l1; // copy newest from here
- int i2 = MAX(l1, oldlen - newlen + l1); // copy oldest from here
-
- // copy as much entries as they fit to new table, reordering them
- if (newlen) {
- // copy oldest entries
- memcpy(&temp[0], &history[type][i2], (size_t)l2 * sizeof(*temp));
- // copy newest entries
- memcpy(&temp[l2], &history[type][i1], (size_t)l1 * sizeof(*temp));
- }
-
- // delete entries that don't fit in newlen, if any
- for (int i = 0; i < i1; i++) {
- hist_free_entry(history[type] + i);
- }
- for (int i = i1 + l1; i < i2; i++) {
- hist_free_entry(history[type] + i);
- }
- }
-
- // clear remaining space, if any
- int l3 = j < 0 ? 0 : MIN(newlen, oldlen); // number of copied entries
- if (newlen) {
- memset(temp + l3, 0, (size_t)(newlen - l3) * sizeof(*temp));
- }
-
- hisidx[type] = l3 - 1;
- xfree(history[type]);
- history[type] = temp;
- }
- hislen = newlen;
- }
-}
-
-static inline void hist_free_entry(histentry_T *hisptr)
- FUNC_ATTR_NONNULL_ALL
+/// "getcmdcompltype()" function
+void f_getcmdcompltype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- xfree(hisptr->hisstr);
- tv_list_unref(hisptr->additional_elements);
- clear_hist_entry(hisptr);
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char *)get_cmdline_completion();
}
-static inline void clear_hist_entry(histentry_T *hisptr)
- FUNC_ATTR_NONNULL_ALL
+/// "getcmdline()" function
+void f_getcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- memset(hisptr, 0, sizeof(*hisptr));
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char *)get_cmdline_str();
}
-/// Check if command line 'str' is already in history.
-/// If 'move_to_front' is TRUE, matching entry is moved to end of history.
-///
-/// @param move_to_front Move the entry to the front if it exists
-static int in_history(int type, char_u *str, int move_to_front, int sep)
+/// "getcmdpos()" function
+void f_getcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- int i;
- int last_i = -1;
- char_u *p;
-
- if (hisidx[type] < 0) {
- return FALSE;
- }
- i = hisidx[type];
- do {
- if (history[type][i].hisstr == NULL) {
- return FALSE;
- }
-
- /* For search history, check that the separator character matches as
- * well. */
- p = history[type][i].hisstr;
- if (STRCMP(str, p) == 0
- && (type != HIST_SEARCH || sep == p[STRLEN(p) + 1])) {
- if (!move_to_front) {
- return TRUE;
- }
- last_i = i;
- break;
- }
- if (--i < 0) {
- i = hislen - 1;
- }
- } while (i != hisidx[type]);
-
- if (last_i >= 0) {
- list_T *const list = history[type][i].additional_elements;
- str = history[type][i].hisstr;
- while (i != hisidx[type]) {
- if (++i >= hislen) {
- i = 0;
- }
- history[type][last_i] = history[type][i];
- last_i = i;
- }
- tv_list_unref(list);
- history[type][i].hisnum = ++hisnum[type];
- history[type][i].hisstr = str;
- history[type][i].timestamp = os_time();
- history[type][i].additional_elements = NULL;
- return true;
- }
- return false;
+ CmdlineInfo *p = get_ccline_ptr();
+ rettv->vval.v_number = p != NULL ? p->cmdpos + 1 : 0;
}
-/// Convert history name to its HIST_ equivalent
-///
-/// Names are taken from the table above. When `name` is empty returns currently
-/// active history or HIST_DEFAULT, depending on `return_default` argument.
-///
-/// @param[in] name Converted name.
-/// @param[in] len Name length.
-/// @param[in] return_default Determines whether HIST_DEFAULT should be
-/// returned or value based on `ccline.cmdfirstc`.
-///
-/// @return Any value from HistoryType enum, including HIST_INVALID. May not
-/// return HIST_DEFAULT unless return_default is true.
-HistoryType get_histtype(const char *const name, const size_t len, const bool return_default)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+/// "getcmdscreenpos()" function
+void f_getcmdscreenpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- // No argument: use current history.
- if (len == 0) {
- return return_default ? HIST_DEFAULT : hist_char2type(ccline.cmdfirstc);
- }
-
- for (HistoryType i = 0; history_names[i] != NULL; i++) {
- if (STRNICMP(name, history_names[i], len) == 0) {
- return i;
- }
- }
-
- if (vim_strchr(":=@>?/", name[0]) != NULL && len == 1) {
- return hist_char2type(name[0]);
- }
-
- return HIST_INVALID;
+ CmdlineInfo *p = get_ccline_ptr();
+ rettv->vval.v_number = p != NULL ? p->cmdspos + 1 : 0;
}
-static int last_maptick = -1; // last seen maptick
-
-/// Add the given string to the given history. If the string is already in the
-/// history then it is moved to the front. "histype" may be one of the HIST_
-/// values.
-///
-/// @parma in_map consider maptick when inside a mapping
-/// @param sep separator character used (search hist)
-void add_to_history(int histype, char_u *new_entry, int in_map, int sep)
+/// "getcmdtype()" function
+void f_getcmdtype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- histentry_T *hisptr;
-
- if (hislen == 0 || histype == HIST_INVALID) { // no history
- return;
- }
- assert(histype != HIST_DEFAULT);
-
- if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) && histype == HIST_SEARCH) {
- return;
- }
-
- /*
- * Searches inside the same mapping overwrite each other, so that only
- * the last line is kept. Be careful not to remove a line that was moved
- * down, only lines that were added.
- */
- if (histype == HIST_SEARCH && in_map) {
- if (maptick == last_maptick && hisidx[HIST_SEARCH] >= 0) {
- // Current line is from the same mapping, remove it
- hisptr = &history[HIST_SEARCH][hisidx[HIST_SEARCH]];
- hist_free_entry(hisptr);
- --hisnum[histype];
- if (--hisidx[HIST_SEARCH] < 0) {
- hisidx[HIST_SEARCH] = hislen - 1;
- }
- }
- last_maptick = -1;
- }
- if (!in_history(histype, new_entry, true, sep)) {
- if (++hisidx[histype] == hislen) {
- hisidx[histype] = 0;
- }
- hisptr = &history[histype][hisidx[histype]];
- hist_free_entry(hisptr);
-
- // Store the separator after the NUL of the string.
- size_t len = STRLEN(new_entry);
- hisptr->hisstr = vim_strnsave(new_entry, len + 2);
- hisptr->timestamp = os_time();
- hisptr->additional_elements = NULL;
- hisptr->hisstr[len + 1] = (char_u)sep;
-
- hisptr->hisnum = ++hisnum[histype];
- if (histype == HIST_SEARCH && in_map) {
- last_maptick = maptick;
- }
- }
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xmallocz(1);
+ rettv->vval.v_string[0] = (char)get_cmdline_type();
}
-/*
- * Get identifier of newest history entry.
- * "histype" may be one of the HIST_ values.
- */
-int get_history_idx(int histype)
+/// Set the command line str to "str".
+/// @return 1 when failed, 0 when OK.
+static int set_cmdline_str(const char *str, int pos)
{
- if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
- || hisidx[histype] < 0) {
- return -1;
- }
-
- return history[histype][hisidx[histype]].hisnum;
-}
-
-/// Get pointer to the command line info to use. save_cmdline() may clear
-/// ccline and put the previous value in ccline.prev_ccline.
-static struct cmdline_info *get_ccline_ptr(void)
-{
- if ((State & MODE_CMDLINE) == 0) {
- return NULL;
- } else if (ccline.cmdbuff != NULL) {
- return &ccline;
- } else if (ccline.prev_ccline && ccline.prev_ccline->cmdbuff != NULL) {
- return ccline.prev_ccline;
- } else {
- return NULL;
- }
-}
-
-/// Get the current command-line completion type.
-char_u *get_cmdline_completion(void)
-{
- if (cmdline_star > 0) {
- return NULL;
- }
- struct cmdline_info *p = get_ccline_ptr();
-
- if (p != NULL && p->xpc != NULL) {
- set_expand_context(p->xpc);
- char *cmd_compl = get_user_cmd_complete(p->xpc, p->xpc->xp_context);
- if (cmd_compl != NULL) {
- return vim_strsave((char_u *)cmd_compl);
- }
- }
-
- return NULL;
-}
-
-/*
- * Get the current command line in allocated memory.
- * Only works when the command line is being edited.
- * Returns NULL when something is wrong.
- */
-char_u *get_cmdline_str(void)
-{
- if (cmdline_star > 0) {
- return NULL;
- }
- struct cmdline_info *p = get_ccline_ptr();
+ CmdlineInfo *p = get_ccline_ptr();
if (p == NULL) {
- return NULL;
+ return 1;
}
- return vim_strnsave(p->cmdbuff, (size_t)p->cmdlen);
-}
-/*
- * Get the current command line position, counted in bytes.
- * Zero is the first position.
- * Only works when the command line is being edited.
- * Returns -1 when something is wrong.
- */
-int get_cmdline_pos(void)
-{
- struct cmdline_info *p = get_ccline_ptr();
+ int len = (int)STRLEN(str);
+ realloc_cmdbuff(len + 1);
+ p->cmdlen = len;
+ STRCPY(p->cmdbuff, str);
- if (p == NULL) {
- return -1;
- }
- return p->cmdpos;
-}
+ p->cmdpos = pos < 0 || pos > p->cmdlen ? p->cmdlen : pos;
+ new_cmdpos = p->cmdpos;
-/// Get the command line cursor screen position.
-int get_cmdline_screen_pos(void)
-{
- struct cmdline_info *p = get_ccline_ptr();
+ redrawcmd();
- if (p == NULL) {
- return -1;
- }
- return p->cmdspos;
+ // Trigger CmdlineChanged autocommands.
+ do_autocmd_cmdlinechanged(get_cmdline_type());
+
+ return 0;
}
-/*
- * Set the command line byte position to "pos". Zero is the first position.
- * Only works when the command line is being edited.
- * Returns 1 when failed, 0 when OK.
- */
-int set_cmdline_pos(int pos)
+/// Set the command line byte position to "pos". Zero is the first position.
+/// Only works when the command line is being edited.
+/// @return 1 when failed, 0 when OK.
+static int set_cmdline_pos(int pos)
{
- struct cmdline_info *p = get_ccline_ptr();
+ CmdlineInfo *p = get_ccline_ptr();
if (p == NULL) {
return 1;
@@ -6273,187 +4068,45 @@ int set_cmdline_pos(int pos)
return 0;
}
-/*
- * Get the current command-line type.
- * Returns ':' or '/' or '?' or '@' or '>' or '-'
- * Only works when the command line is being edited.
- * Returns NUL when something is wrong.
- */
-int get_cmdline_type(void)
+/// "setcmdline()" function
+void f_setcmdline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- struct cmdline_info *p = get_ccline_ptr();
-
- if (p == NULL) {
- return NUL;
- }
- if (p->cmdfirstc == NUL) {
- return (p->input_fn) ? '@' : '-';
+ if (argvars[0].v_type != VAR_STRING || argvars[0].vval.v_string == NULL) {
+ emsg(_(e_stringreq));
+ return;
}
- return p->cmdfirstc;
-}
-
-/*
- * Calculate history index from a number:
- * num > 0: seen as identifying number of a history entry
- * num < 0: relative position in history wrt newest entry
- * "histype" may be one of the HIST_ values.
- */
-static int calc_hist_idx(int histype, int num)
-{
- int i;
- histentry_T *hist;
- int wrapped = FALSE;
- if (hislen == 0 || histype < 0 || histype >= HIST_COUNT
- || (i = hisidx[histype]) < 0 || num == 0) {
- return -1;
- }
+ int pos = -1;
+ if (argvars[1].v_type != VAR_UNKNOWN) {
+ bool error = false;
- hist = history[histype];
- if (num > 0) {
- while (hist[i].hisnum > num) {
- if (--i < 0) {
- if (wrapped) {
- break;
- }
- i += hislen;
- wrapped = TRUE;
- }
- }
- if (i >= 0 && hist[i].hisnum == num && hist[i].hisstr != NULL) {
- return i;
- }
- } else if (-num <= hislen) {
- i += num + 1;
- if (i < 0) {
- i += hislen;
+ pos = (int)tv_get_number_chk(&argvars[1], &error) - 1;
+ if (error) {
+ return;
}
- if (hist[i].hisstr != NULL) {
- return i;
+ if (pos < 0) {
+ emsg(_(e_positive));
+ return;
}
}
- return -1;
-}
-/*
- * Get a history entry by its index.
- * "histype" may be one of the HIST_ values.
- */
-char_u *get_history_entry(int histype, int idx)
-{
- idx = calc_hist_idx(histype, idx);
- if (idx >= 0) {
- return history[histype][idx].hisstr;
- } else {
- return (char_u *)"";
- }
+ rettv->vval.v_number = set_cmdline_str(argvars[0].vval.v_string, pos);
}
-/// Clear all entries in a history
-///
-/// @param[in] histype One of the HIST_ values.
-///
-/// @return OK if there was something to clean and histype was one of HIST_
-/// values, FAIL otherwise.
-int clr_history(const int histype)
+/// "setcmdpos()" function
+void f_setcmdpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (hislen != 0 && histype >= 0 && histype < HIST_COUNT) {
- histentry_T *hisptr = history[histype];
- for (int i = hislen; i--; hisptr++) {
- hist_free_entry(hisptr);
- }
- hisidx[histype] = -1; // mark history as cleared
- hisnum[histype] = 0; // reset identifier counter
- return OK;
- }
- return FAIL;
-}
+ const int pos = (int)tv_get_number(&argvars[0]) - 1;
-/*
- * Remove all entries matching {str} from a history.
- * "histype" may be one of the HIST_ values.
- */
-int del_history_entry(int histype, char_u *str)
-{
- regmatch_T regmatch;
- histentry_T *hisptr;
- int idx;
- int i;
- int last;
- bool found = false;
-
- regmatch.regprog = NULL;
- regmatch.rm_ic = FALSE; // always match case
- if (hislen != 0
- && histype >= 0
- && histype < HIST_COUNT
- && *str != NUL
- && (idx = hisidx[histype]) >= 0
- && (regmatch.regprog = vim_regcomp((char *)str, RE_MAGIC + RE_STRING))
- != NULL) {
- i = last = idx;
- do {
- hisptr = &history[histype][i];
- if (hisptr->hisstr == NULL) {
- break;
- }
- if (vim_regexec(&regmatch, (char *)hisptr->hisstr, (colnr_T)0)) {
- found = true;
- hist_free_entry(hisptr);
- } else {
- if (i != last) {
- history[histype][last] = *hisptr;
- clear_hist_entry(hisptr);
- }
- if (--last < 0) {
- last += hislen;
- }
- }
- if (--i < 0) {
- i += hislen;
- }
- } while (i != idx);
- if (history[histype][idx].hisstr == NULL) {
- hisidx[histype] = -1;
- }
+ if (pos >= 0) {
+ rettv->vval.v_number = set_cmdline_pos(pos);
}
- vim_regfree(regmatch.regprog);
- return found;
}
-/*
- * Remove an indexed entry from a history.
- * "histype" may be one of the HIST_ values.
- */
-int del_history_idx(int histype, int idx)
+/// Return the first character of the current command line.
+int get_cmdline_firstc(void)
{
- int i, j;
-
- i = calc_hist_idx(histype, idx);
- if (i < 0) {
- return FALSE;
- }
- idx = hisidx[histype];
- hist_free_entry(&history[histype][i]);
-
- /* When deleting the last added search string in a mapping, reset
- * last_maptick, so that the last added search string isn't deleted again.
- */
- if (histype == HIST_SEARCH && maptick == last_maptick && i == idx) {
- last_maptick = -1;
- }
-
- while (i != idx) {
- j = (i + 1) % hislen;
- history[histype][i] = history[histype][j];
- i = j;
- }
- clear_hist_entry(&history[histype][idx]);
- if (--i < 0) {
- i += hislen;
- }
- hisidx[histype] = i;
- return TRUE;
+ return ccline.cmdfirstc;
}
/// Get indices that specify a range within a list (not a range of text lines
@@ -6464,26 +4117,26 @@ int del_history_idx(int histype, int idx)
/// @param num2 to
///
/// @return OK if parsed successfully, otherwise FAIL.
-int get_list_range(char_u **str, int *num1, int *num2)
+int get_list_range(char **str, int *num1, int *num2)
{
int len;
int first = false;
varnumber_T num;
- *str = (char_u *)skipwhite((char *)(*str));
+ *str = skipwhite((*str));
if (**str == '-' || ascii_isdigit(**str)) { // parse "from" part of range
- vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
+ vim_str2nr((char_u *)(*str), NULL, &len, 0, &num, NULL, 0, false);
*str += len;
*num1 = (int)num;
first = true;
}
- *str = (char_u *)skipwhite((char *)(*str));
+ *str = skipwhite((*str));
if (**str == ',') { // parse "to" part of range
- *str = (char_u *)skipwhite((char *)(*str) + 1);
- vim_str2nr(*str, NULL, &len, 0, &num, NULL, 0, false);
+ *str = skipwhite((*str) + 1);
+ vim_str2nr((char_u *)(*str), NULL, &len, 0, &num, NULL, 0, false);
if (len > 0) {
*num2 = (int)num;
- *str = (char_u *)skipwhite((char *)(*str) + len);
+ *str = skipwhite((*str) + len);
} else if (!first) { // no number given at all
return FAIL;
}
@@ -6493,118 +4146,27 @@ int get_list_range(char_u **str, int *num1, int *num2)
return OK;
}
-/*
- * :history command - print a history
- */
-void ex_history(exarg_T *eap)
+void cmdline_init(void)
{
- histentry_T *hist;
- int histype1 = HIST_CMD;
- int histype2 = HIST_CMD;
- int hisidx1 = 1;
- int hisidx2 = -1;
- int idx;
- int i, j, k;
- char_u *end;
- char_u *arg = (char_u *)eap->arg;
-
- if (hislen == 0) {
- msg(_("'history' option is zero"));
- return;
- }
-
- if (!(ascii_isdigit(*arg) || *arg == '-' || *arg == ',')) {
- end = arg;
- while (ASCII_ISALPHA(*end)
- || vim_strchr(":=@>/?", *end) != NULL) {
- end++;
- }
- histype1 = get_histtype((const char *)arg, (size_t)(end - arg), false);
- if (histype1 == HIST_INVALID) {
- if (STRNICMP(arg, "all", end - arg) == 0) {
- histype1 = 0;
- histype2 = HIST_COUNT - 1;
- } else {
- semsg(_(e_trailing_arg), arg);
- return;
- }
- } else {
- histype2 = histype1;
- }
- } else {
- end = arg;
- }
- if (!get_list_range(&end, &hisidx1, &hisidx2) || *end != NUL) {
- semsg(_(e_trailing_arg), end);
- return;
- }
-
- for (; !got_int && histype1 <= histype2; ++histype1) {
- STRCPY(IObuff, "\n # ");
- assert(history_names[histype1] != NULL);
- STRCAT(STRCAT(IObuff, history_names[histype1]), " history");
- msg_puts_title((char *)IObuff);
- idx = hisidx[histype1];
- hist = history[histype1];
- j = hisidx1;
- k = hisidx2;
- if (j < 0) {
- j = (-j > hislen) ? 0 : hist[(hislen + j + idx + 1) % hislen].hisnum;
- }
- if (k < 0) {
- k = (-k > hislen) ? 0 : hist[(hislen + k + idx + 1) % hislen].hisnum;
- }
- if (idx >= 0 && j <= k) {
- for (i = idx + 1; !got_int; ++i) {
- if (i == hislen) {
- i = 0;
- }
- if (hist[i].hisstr != NULL
- && hist[i].hisnum >= j && hist[i].hisnum <= k) {
- msg_putchar('\n');
- snprintf((char *)IObuff, IOSIZE, "%c%6d ", i == idx ? '>' : ' ',
- hist[i].hisnum);
- if (vim_strsize((char *)hist[i].hisstr) > Columns - 10) {
- trunc_string((char *)hist[i].hisstr, (char *)IObuff + STRLEN(IObuff),
- Columns - 10, IOSIZE - (int)STRLEN(IObuff));
- } else {
- STRCAT(IObuff, hist[i].hisstr);
- }
- msg_outtrans((char *)IObuff);
- ui_flush();
- }
- if (i == idx) {
- break;
- }
- }
- }
- }
+ CLEAR_FIELD(ccline);
}
-/// Translate a history type number to the associated character
-int hist_type2char(int type)
- FUNC_ATTR_CONST
+/// Check value of 'cedit' and set cedit_key.
+/// Returns NULL if value is OK, error message otherwise.
+char *check_cedit(void)
{
- switch (type) {
- case HIST_CMD:
- return ':';
- case HIST_SEARCH:
- return '/';
- case HIST_EXPR:
- return '=';
- case HIST_INPUT:
- return '@';
- case HIST_DEBUG:
- return '>';
- default:
- abort();
- }
- return NUL;
-}
+ int n;
-void cmdline_init(void)
-{
- memset(&ccline, 0, sizeof(struct cmdline_info));
+ if (*p_cedit == NUL) {
+ cedit_key = -1;
+ } else {
+ n = string_to_key((char_u *)p_cedit);
+ if (vim_isprintc(n)) {
+ return e_invarg;
+ }
+ cedit_key = n;
+ }
+ return NULL;
}
/// Open a window on the current command line and history. Allow editing in
@@ -6654,17 +4216,27 @@ static int open_cmdwin(void)
ga_clear(&winsizes);
return K_IGNORE;
}
+ // Don't let quitting the More prompt make this fail.
+ got_int = false;
+
+ // Set "cmdwin_type" before any autocommands may mess things up.
cmdwin_type = get_cmdline_type();
cmdwin_level = ccline.level;
// Create empty command-line buffer.
- buf_open_scratch(0, _("[Command Line]"));
+ if (buf_open_scratch(0, _("[Command Line]")) == FAIL) {
+ // Some autocommand messed it up?
+ win_close(curwin, true, false);
+ ga_clear(&winsizes);
+ cmdwin_type = 0;
+ return Ctrl_C;
+ }
// Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
- set_option_value("bh", 0L, "wipe", OPT_LOCAL);
- curwin->w_p_rl = cmdmsg_rl;
- cmdmsg_rl = false;
+ set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL);
curbuf->b_p_ma = true;
curwin->w_p_fen = false;
+ curwin->w_p_rl = cmdmsg_rl;
+ cmdmsg_rl = false;
// Don't allow switching to another buffer.
curbuf->b_ro_locked++;
@@ -6678,7 +4250,7 @@ static int open_cmdwin(void)
add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true);
add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true);
}
- set_option_value("ft", 0L, "vim", OPT_LOCAL);
+ set_option_value_give_err("ft", 0L, "vim", OPT_LOCAL);
}
curbuf->b_ro_locked--;
@@ -6688,18 +4260,18 @@ static int open_cmdwin(void)
// Fill the buffer with the history.
init_history();
- if (hislen > 0 && histtype != HIST_INVALID) {
- i = hisidx[histtype];
+ if (get_hislen() > 0 && histtype != HIST_INVALID) {
+ i = *get_hisidx(histtype);
if (i >= 0) {
lnum = 0;
do {
- if (++i == hislen) {
+ if (++i == get_hislen()) {
i = 0;
}
- if (history[histtype][i].hisstr != NULL) {
- ml_append(lnum++, (char *)history[histtype][i].hisstr, (colnr_T)0, false);
+ if (get_histentry(histtype)[i].hisstr != NULL) {
+ ml_append(lnum++, (char *)get_histentry(histtype)[i].hisstr, (colnr_T)0, false);
}
- } while (i != hisidx[histtype]);
+ } while (i != *get_hisidx(histtype));
}
}
@@ -6714,7 +4286,7 @@ static int open_cmdwin(void)
ccline.redraw_state = kCmdRedrawNone;
ui_call_cmdline_hide(ccline.level);
}
- redraw_later(curwin, SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
// No Ex mode here!
exmode_active = false;
@@ -6904,90 +4476,6 @@ char *script_get(exarg_T *const eap, size_t *const lenp)
return (char *)ga.ga_data;
}
-/// Iterate over history items
-///
-/// @warning No history-editing functions must be run while iteration is in
-/// progress.
-///
-/// @param[in] iter Pointer to the last history entry.
-/// @param[in] history_type Type of the history (HIST_*). Ignored if iter
-/// parameter is not NULL.
-/// @param[in] zero If true then zero (but not free) returned items.
-///
-/// @warning When using this parameter user is
-/// responsible for calling clr_history()
-/// itself after iteration is over. If
-/// clr_history() is not called behaviour is
-/// undefined. No functions that work with
-/// history must be called during iteration
-/// in this case.
-/// @param[out] hist Next history entry.
-///
-/// @return Pointer used in next iteration or NULL to indicate that iteration
-/// was finished.
-const void *hist_iter(const void *const iter, const uint8_t history_type, const bool zero,
- histentry_T *const hist)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(4)
-{
- *hist = (histentry_T) {
- .hisstr = NULL
- };
- if (hisidx[history_type] == -1) {
- return NULL;
- }
- histentry_T *const hstart = &(history[history_type][0]);
- histentry_T *const hlast = (
- &(history[history_type][hisidx[history_type]]));
- const histentry_T *const hend = &(history[history_type][hislen - 1]);
- histentry_T *hiter;
- if (iter == NULL) {
- histentry_T *hfirst = hlast;
- do {
- hfirst++;
- if (hfirst > hend) {
- hfirst = hstart;
- }
- if (hfirst->hisstr != NULL) {
- break;
- }
- } while (hfirst != hlast);
- hiter = hfirst;
- } else {
- hiter = (histentry_T *)iter;
- }
- if (hiter == NULL) {
- return NULL;
- }
- *hist = *hiter;
- if (zero) {
- memset(hiter, 0, sizeof(*hiter));
- }
- if (hiter == hlast) {
- return NULL;
- }
- hiter++;
- return (const void *)((hiter > hend) ? hstart : hiter);
-}
-
-/// Get array of history items
-///
-/// @param[in] history_type Type of the history to get array for.
-/// @param[out] new_hisidx Location where last index in the new array should
-/// be saved.
-/// @param[out] new_hisnum Location where last history number in the new
-/// history should be saved.
-///
-/// @return Pointer to the array or NULL.
-histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx,
- int **const new_hisnum)
- FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
-{
- init_history();
- *new_hisidx = &(hisidx[history_type]);
- *new_hisnum = &(hisnum[history_type]);
- return history[history_type];
-}
-
static void set_search_match(pos_T *t)
{
// First move cursor to end of match, then to the start. This
diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h
index 1cc6faf87c..5e3ad20a31 100644
--- a/src/nvim/ex_getln.h
+++ b/src/nvim/ex_getln.h
@@ -2,66 +2,77 @@
#define NVIM_EX_GETLN_H
#include "nvim/eval/typval.h"
-#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds_defs.h"
-#include "nvim/os/time.h"
-#include "nvim/regexp_defs.h"
+#include "nvim/types.h"
-// Values for nextwild() and ExpandOne(). See ExpandOne() for meaning.
-#define WILD_FREE 1
-#define WILD_EXPAND_FREE 2
-#define WILD_EXPAND_KEEP 3
-#define WILD_NEXT 4
-#define WILD_PREV 5
-#define WILD_ALL 6
-#define WILD_LONGEST 7
-#define WILD_ALL_KEEP 8
-#define WILD_CANCEL 9
-#define WILD_APPLY 10
+/// Command-line colors: one chunk
+///
+/// Defines a region which has the same highlighting.
+typedef struct {
+ int start; ///< Colored chunk start.
+ int end; ///< Colored chunk end (exclusive, > start).
+ int attr; ///< Highlight attr.
+} CmdlineColorChunk;
-#define WILD_LIST_NOTFOUND 0x01
-#define WILD_HOME_REPLACE 0x02
-#define WILD_USE_NL 0x04
-#define WILD_NO_BEEP 0x08
-#define WILD_ADD_SLASH 0x10
-#define WILD_KEEP_ALL 0x20
-#define WILD_SILENT 0x40
-#define WILD_ESCAPE 0x80
-#define WILD_ICASE 0x100
-#define WILD_ALLLINKS 0x200
-#define WILD_IGNORE_COMPLETESLASH 0x400
-#define WILD_NOERROR 0x800 // sets EW_NOERROR
-#define WILD_BUFLASTUSED 0x1000
-#define BUF_DIFF_FILTER 0x2000
+/// Command-line colors
+///
+/// Holds data about all colors.
+typedef kvec_t(CmdlineColorChunk) CmdlineColors;
-// flags used by vim_strsave_fnameescape()
-#define VSE_NONE 0
-#define VSE_SHELL 1 ///< escape for a shell command
-#define VSE_BUFFER 2 ///< escape for a ":buffer" command
+/// Command-line coloring
+///
+/// Holds both what are the colors and what have been colored. Latter is used to
+/// suppress unnecessary calls to coloring callbacks.
+typedef struct {
+ unsigned prompt_id; ///< ID of the prompt which was colored last.
+ char *cmdbuff; ///< What exactly was colored last time or NULL.
+ CmdlineColors colors; ///< Last colors.
+} ColoredCmdline;
-/// Present history tables
+/// Keeps track how much state must be sent to external ui.
typedef enum {
- HIST_DEFAULT = -2, ///< Default (current) history.
- HIST_INVALID = -1, ///< Unknown history.
- HIST_CMD = 0, ///< Colon commands.
- HIST_SEARCH, ///< Search commands.
- HIST_EXPR, ///< Expressions (e.g. from entering = register).
- HIST_INPUT, ///< input() lines.
- HIST_DEBUG, ///< Debug commands.
-} HistoryType;
+ kCmdRedrawNone,
+ kCmdRedrawPos,
+ kCmdRedrawAll,
+} CmdRedraw;
-/// Number of history tables
-#define HIST_COUNT (HIST_DEBUG + 1)
+/// Variables shared between getcmdline(), redrawcmdline() and others.
+/// These need to be saved when using CTRL-R |, that's why they are in a
+/// structure.
+typedef struct cmdline_info CmdlineInfo;
+struct cmdline_info {
+ char_u *cmdbuff; ///< pointer to command line buffer
+ int cmdbufflen; ///< length of cmdbuff
+ int cmdlen; ///< number of chars in command line
+ int cmdpos; ///< current cursor position
+ int cmdspos; ///< cursor column on screen
+ int cmdfirstc; ///< ':', '/', '?', '=', '>' or NUL
+ int cmdindent; ///< number of spaces before cmdline
+ char_u *cmdprompt; ///< message in front of cmdline
+ int cmdattr; ///< attributes for prompt
+ int overstrike; ///< Typing mode on the command line. Shared by
+ ///< getcmdline() and put_on_cmdline().
+ expand_T *xpc; ///< struct being used for expansion, xp_pattern
+ ///< may point into cmdbuff
+ int xp_context; ///< type of expansion
+ char_u *xp_arg; ///< user-defined expansion arg
+ int input_fn; ///< when true Invoked for input() function
+ unsigned prompt_id; ///< Prompt number, used to disable coloring on errors.
+ Callback highlight_callback; ///< Callback used for coloring user input.
+ ColoredCmdline last_colors; ///< Last cmdline colors
+ int level; ///< current cmdline level
+ CmdlineInfo *prev_ccline; ///< pointer to saved cmdline state
+ char special_char; ///< last putcmdline char (used for redraws)
+ bool special_shift; ///< shift of last putcmdline char
+ CmdRedraw redraw_state; ///< needed redraw for external cmdline
+};
-typedef char *(*CompleteListItemGetter)(expand_T *, int);
-
-/// History entry definition
-typedef struct hist_entry {
- int hisnum; ///< Entry identifier number.
- char_u *hisstr; ///< Actual entry, separator char after the NUL.
- Timestamp timestamp; ///< Time when entry was added.
- list_T *additional_elements; ///< Additional entries from ShaDa file.
-} histentry_T;
+/// flags used by vim_strsave_fnameescape()
+enum {
+ VSE_NONE = 0,
+ VSE_SHELL = 1, ///< escape for a shell command
+ VSE_BUFFER = 2, ///< escape for a ":buffer" command
+};
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_getln.h.generated.h"
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 1d670afa6d..e907c45e79 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -13,12 +13,12 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
@@ -34,6 +34,7 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
+#include "nvim/runtime.h"
#include "nvim/vim.h"
#include "nvim/window.h"
@@ -358,7 +359,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// Then ":help" will re-use both the buffer and the window and set
// the options, even when "options" is not in 'sessionoptions'.
if (0 < wp->w_tagstackidx && wp->w_tagstackidx <= wp->w_tagstacklen) {
- curtag = (char *)wp->w_tagstack[wp->w_tagstackidx - 1].tagname;
+ curtag = wp->w_tagstack[wp->w_tagstackidx - 1].tagname;
}
if (put_line(fd, "enew | setl bt=help") == FAIL
@@ -955,7 +956,7 @@ void ex_mkrc(exarg_T *eap)
}
// When using 'viewdir' may have to create the directory.
- if (using_vdir && !os_isdir(p_vdir)) {
+ if (using_vdir && !os_isdir((char *)p_vdir)) {
vim_mkdir_emsg((const char *)p_vdir, 0755);
}
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index 1639f72990..8e780f4aaa 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -510,11 +510,11 @@ void extmark_adjust(buf_T *buf, linenr_T line1, linenr_T line2, linenr_T amount,
bcount_t old_byte = 0, new_byte = 0;
int old_row, new_row;
if (amount == MAXLNUM) {
- old_row = (int)(line2 - line1 + 1);
+ old_row = line2 - line1 + 1;
// TODO(bfredl): ej kasta?
old_byte = (bcount_t)buf->deleted_bytes2;
- new_row = (int)(amount_after + old_row);
+ new_row = amount_after + old_row;
} else {
// A region is either deleted (amount == MAXLNUM) or
// added (line2 == MAXLNUM). The only other case is :move
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index ebefd1157c..bbc6e53aa8 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -50,6 +50,7 @@
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/file_search.h"
@@ -109,16 +110,15 @@ typedef struct ff_stack {
typedef struct ff_visited {
struct ff_visited *ffv_next;
- /* Visited directories are different if the wildcard string are
- * different. So we have to save it.
- */
+ // Visited directories are different if the wildcard string are
+ // different. So we have to save it.
char_u *ffv_wc_path;
+
// use FileID for comparison (needed because of links), else use filename.
bool file_id_valid;
FileID file_id;
- /* The memory for this struct is allocated according to the length of
- * ffv_fname.
- */
+ // The memory for this struct is allocated according to the length of
+ // ffv_fname.
char_u ffv_fname[1]; // actually longer
} ff_visited_T;
@@ -220,8 +220,8 @@ static char_u e_pathtoolong[] = N_("E854: path too long for completion");
///
/// Upward search is only done on the starting dir.
///
-/// If 'free_visited' is TRUE the list of already visited files/directories is
-/// cleared. Set this to FALSE if you just want to search from another
+/// If 'free_visited' is true the list of already visited files/directories is
+/// cleared. Set this to false if you just want to search from another
/// directory, but want to be sure that no directory from a previous search is
/// searched again. This is useful if you search for a file at different places.
/// The list of visited files/dirs can also be cleared with the function
@@ -268,7 +268,7 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
ff_clear(search_ctx);
// clear visited list if wanted
- if (free_visited == TRUE) {
+ if (free_visited == true) {
vim_findfile_free_visited(search_ctx);
} else {
/* Reuse old visited lists. Get the visited list for the given
@@ -301,12 +301,12 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
if (!vim_isAbsName(rel_fname) && len + 1 < MAXPATHL) {
// Make the start dir an absolute path name.
STRLCPY(ff_expand_buffer, rel_fname, len + 1);
- search_ctx->ffsc_start_dir = (char_u *)FullName_save((char *)ff_expand_buffer, FALSE);
+ search_ctx->ffsc_start_dir = (char_u *)FullName_save((char *)ff_expand_buffer, false);
} else {
search_ctx->ffsc_start_dir = vim_strnsave(rel_fname, len);
}
if (*++path != NUL) {
- ++path;
+ path++;
}
} else if (*path == NUL || !vim_isAbsName(path)) {
#ifdef BACKSLASH_IN_FILENAME
@@ -471,7 +471,7 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
STRCPY(buf, ff_expand_buffer);
STRCPY(buf + eb_len, search_ctx->ffsc_fix_path);
- if (os_isdir(buf)) {
+ if (os_isdir((char *)buf)) {
STRCAT(ff_expand_buffer, search_ctx->ffsc_fix_path);
add_pathsep((char *)ff_expand_buffer);
} else {
@@ -509,9 +509,7 @@ void *vim_findfile_init(char_u *path, char_u *filename, char_u *stopdirs, int le
xfree(buf);
}
- sptr = ff_create_stack_element(ff_expand_buffer,
- search_ctx->ffsc_wc_path,
- level, 0);
+ sptr = ff_create_stack_element(ff_expand_buffer, search_ctx->ffsc_wc_path, level, 0);
ff_push(search_ctx, sptr);
search_ctx->ffsc_file_to_search = vim_strsave(filename);
@@ -583,7 +581,7 @@ char_u *vim_findfile(void *search_ctx_arg)
ff_stack_T *stackp = NULL;
size_t len;
char_u *p;
- char_u *suf;
+ char *suf;
ff_search_ctx_T *search_ctx;
if (search_ctx_arg == NULL) {
@@ -640,11 +638,8 @@ char_u *vim_findfile(void *search_ctx_arg)
* first time (hence stackp->ff_filearray == NULL)
*/
if (stackp->ffs_filearray == NULL
- && ff_check_visited(&search_ctx->ffsc_dir_visited_list
- ->ffvl_visited_list,
- stackp->ffs_fix_path,
- stackp->ffs_wc_path
- ) == FAIL) {
+ && ff_check_visited(&search_ctx->ffsc_dir_visited_list->ffvl_visited_list,
+ stackp->ffs_fix_path, stackp->ffs_wc_path) == FAIL) {
#ifdef FF_VERBOSE
if (p_verbose >= 5) {
verbose_enter_scroll();
@@ -789,8 +784,7 @@ char_u *vim_findfile(void *search_ctx_arg)
stackp->ffs_filearray_cur = 0;
stackp->ffs_stage = 0;
} else {
- rest_of_wildcards = &stackp->ffs_wc_path[
- STRLEN(stackp->ffs_wc_path)];
+ rest_of_wildcards = &stackp->ffs_wc_path[STRLEN(stackp->ffs_wc_path)];
}
if (stackp->ffs_stage == 0) {
@@ -802,7 +796,7 @@ char_u *vim_findfile(void *search_ctx_arg)
*/
for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) {
if (!path_with_url(stackp->ffs_filearray[i])
- && !os_isdir((char_u *)stackp->ffs_filearray[i])) {
+ && !os_isdir(stackp->ffs_filearray[i])) {
continue; // not a directory
}
// prepare the filename to be checked for existence below
@@ -824,7 +818,7 @@ char_u *vim_findfile(void *search_ctx_arg)
*/
len = STRLEN(file_path);
if (search_ctx->ffsc_tagfile) {
- suf = (char_u *)"";
+ suf = "";
} else {
suf = curbuf->b_p_sua;
}
@@ -832,23 +826,17 @@ char_u *vim_findfile(void *search_ctx_arg)
// if file exists and we didn't already find it
if ((path_with_url((char *)file_path)
|| (os_path_exists(file_path)
- && (search_ctx->ffsc_find_what
- == FINDFILE_BOTH
- || ((search_ctx->ffsc_find_what
- == FINDFILE_DIR)
- == os_isdir(file_path)))))
+ && (search_ctx->ffsc_find_what == FINDFILE_BOTH
+ || ((search_ctx->ffsc_find_what == FINDFILE_DIR)
+ == os_isdir((char *)file_path)))))
#ifndef FF_VERBOSE
&& (ff_check_visited(&search_ctx->ffsc_visited_list->ffvl_visited_list,
- file_path,
- (char_u *)""
- ) == OK)
+ file_path, (char_u *)"") == OK)
#endif
) {
#ifdef FF_VERBOSE
if (ff_check_visited(&search_ctx->ffsc_visited_list->ffvl_visited_list,
- file_path,
- (char_u *)""
- ) == FAIL) {
+ file_path, (char_u *)"") == FAIL) {
if (p_verbose >= 5) {
verbose_enter_scroll();
smsg("Already: %s", file_path);
@@ -891,13 +879,13 @@ char_u *vim_findfile(void *search_ctx_arg)
break;
}
assert(MAXPATHL >= len);
- copy_option_part((char **)&suf, (char *)file_path + len, MAXPATHL - len, ",");
+ copy_option_part(&suf, (char *)file_path + len, MAXPATHL - len, ",");
}
}
} else {
// still wildcards left, push the directories for further search
for (int i = stackp->ffs_filearray_cur; i < stackp->ffs_filearray_size; i++) {
- if (!os_isdir((char_u *)stackp->ffs_filearray[i])) {
+ if (!os_isdir(stackp->ffs_filearray[i])) {
continue; // not a directory
}
ff_push(search_ctx,
@@ -921,7 +909,7 @@ char_u *vim_findfile(void *search_ctx_arg)
stackp->ffs_fix_path) == 0) {
continue; // don't repush same directory
}
- if (!os_isdir((char_u *)stackp->ffs_filearray[i])) {
+ if (!os_isdir(stackp->ffs_filearray[i])) {
continue; // not a directory
}
ff_push(search_ctx,
@@ -944,7 +932,7 @@ char_u *vim_findfile(void *search_ctx_arg)
// is the last starting directory in the stop list?
if (ff_path_in_stoplist(search_ctx->ffsc_start_dir,
(int)(path_end - search_ctx->ffsc_start_dir),
- search_ctx->ffsc_stopdirs_v) == TRUE) {
+ search_ctx->ffsc_stopdirs_v) == true) {
break;
}
@@ -1288,7 +1276,7 @@ static void ff_clear(ff_search_ctx_T *search_ctx)
/// check if the given path is in the stopdirs
///
-/// @return TRUE if yes else FALSE
+/// @return true if yes else false
static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
{
int i = 0;
@@ -1300,7 +1288,7 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
// if no path consider it as match
if (path_len == 0) {
- return TRUE;
+ return true;
}
for (i = 0; stopdirs_v[i] != NULL; i++) {
@@ -1311,7 +1299,7 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
*/
if (FNAMENCMP(stopdirs_v[i], path, path_len) == 0
&& vim_ispathsep(stopdirs_v[i][path_len])) {
- return TRUE;
+ return true;
}
} else {
if (FNAMECMP(stopdirs_v[i], path) == 0) {
@@ -1319,13 +1307,13 @@ static int ff_path_in_stoplist(char_u *path, int path_len, char_u **stopdirs_v)
}
}
}
- return FALSE;
+ return false;
}
/// Find the file name "ptr[len]" in the path. Also finds directory names.
///
-/// On the first call set the parameter 'first' to TRUE to initialize
-/// the search. For repeating calls to FALSE.
+/// On the first call set the parameter 'first' to true to initialize
+/// the search. For repeating calls to false.
///
/// Repeating calls will return other files called 'ptr[len]' from the path.
///
@@ -1354,8 +1342,8 @@ char_u *find_file_in_path(char_u *ptr, size_t len, int options, int first, char_
return find_file_in_path_option(ptr, len, options, first,
(*curbuf->b_p_path == NUL
? p_path
- : curbuf->b_p_path),
- FINDFILE_BOTH, rel_fname, curbuf->b_p_sua);
+ : (char_u *)curbuf->b_p_path),
+ FINDFILE_BOTH, rel_fname, (char_u *)curbuf->b_p_sua);
}
static char_u *ff_file_to_find = NULL;
@@ -1386,7 +1374,7 @@ void free_findfile(void)
/// @return an allocated string for the file name. NULL for error.
char_u *find_directory_in_path(char_u *ptr, size_t len, int options, char_u *rel_fname)
{
- return find_file_in_path_option(ptr, len, options, TRUE, p_cdpath,
+ return find_file_in_path_option(ptr, len, options, true, p_cdpath,
FINDFILE_DIR, rel_fname, (char_u *)"");
}
@@ -1401,11 +1389,11 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
char_u *path_option, int find_what, char_u *rel_fname,
char_u *suffixes)
{
- static char_u *dir;
- static int did_findfile_init = FALSE;
+ static char *dir;
+ static int did_findfile_init = false;
char_u save_char;
char_u *file_name = NULL;
- char_u *buf = NULL;
+ char *buf = NULL;
int rel_to_curdir;
if (rel_fname != NULL && path_with_url((const char *)rel_fname)) {
@@ -1421,14 +1409,14 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
// copy file name into NameBuff, expanding environment variables
save_char = ptr[len];
ptr[len] = NUL;
- expand_env_esc(ptr, NameBuff, MAXPATHL, false, true, NULL);
+ expand_env_esc(ptr, (char_u *)NameBuff, MAXPATHL, false, true, NULL);
ptr[len] = save_char;
xfree(ff_file_to_find);
- ff_file_to_find = vim_strsave(NameBuff);
+ ff_file_to_find = vim_strsave((char_u *)NameBuff);
if (options & FNAME_UNESC) {
// Change all "\ " to " ".
- for (ptr = ff_file_to_find; *ptr != NUL; ++ptr) {
+ for (ptr = ff_file_to_find; *ptr != NUL; ptr++) {
if (ptr[0] == '\\' && ptr[1] == ' ') {
memmove(ptr, ptr + 1, STRLEN(ptr));
}
@@ -1457,7 +1445,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
* If this is not a first call, return NULL. We already returned a
* filename on the first call.
*/
- if (first == TRUE) {
+ if (first == true) {
if (path_with_url((char *)ff_file_to_find)) {
file_name = vim_strsave(ff_file_to_find);
goto theend;
@@ -1465,7 +1453,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
/* When FNAME_REL flag given first use the directory of the file.
* Otherwise or when this fails use the current directory. */
- for (int run = 1; run <= 2; ++run) {
+ for (int run = 1; run <= 2; run++) {
size_t l = STRLEN(ff_file_to_find);
if (run == 1
&& rel_to_curdir
@@ -1482,21 +1470,21 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
/* When the file doesn't exist, try adding parts of
* 'suffixesadd'. */
- buf = suffixes;
+ buf = (char *)suffixes;
for (;;) {
if (
- (os_path_exists(NameBuff)
+ (os_path_exists((char_u *)NameBuff)
&& (find_what == FINDFILE_BOTH
|| ((find_what == FINDFILE_DIR)
== os_isdir(NameBuff))))) {
- file_name = vim_strsave(NameBuff);
+ file_name = vim_strsave((char_u *)NameBuff);
goto theend;
}
if (*buf == NUL) {
break;
}
assert(MAXPATHL >= l);
- copy_option_part((char **)&buf, (char *)NameBuff + l, MAXPATHL - l, ",");
+ copy_option_part(&buf, (char *)NameBuff + l, MAXPATHL - l, ",");
}
}
}
@@ -1506,11 +1494,11 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
* When "first" is set, first setup to the start of the option.
* Otherwise continue to find the next match.
*/
- if (first == TRUE) {
+ if (first == true) {
// vim_findfile_free_visited can handle a possible NULL pointer
vim_findfile_free_visited(fdip_search_ctx);
- dir = path_option;
- did_findfile_init = FALSE;
+ dir = (char *)path_option;
+ did_findfile_init = false;
}
for (;;) {
@@ -1520,7 +1508,7 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
break;
}
- did_findfile_init = FALSE;
+ did_findfile_init = false;
} else {
char_u *r_ptr;
@@ -1536,22 +1524,22 @@ char_u *find_file_in_path_option(char_u *ptr, size_t len, int options, int first
// copy next path
buf[0] = 0;
- copy_option_part((char **)&dir, (char *)buf, MAXPATHL, " ,");
+ copy_option_part(&dir, buf, MAXPATHL, " ,");
// get the stopdir string
- r_ptr = vim_findfile_stopdir(buf);
- fdip_search_ctx = vim_findfile_init(buf, ff_file_to_find,
- r_ptr, 100, FALSE, find_what,
- fdip_search_ctx, FALSE, rel_fname);
+ r_ptr = vim_findfile_stopdir((char_u *)buf);
+ fdip_search_ctx = vim_findfile_init((char_u *)buf, ff_file_to_find,
+ r_ptr, 100, false, find_what,
+ fdip_search_ctx, false, rel_fname);
if (fdip_search_ctx != NULL) {
- did_findfile_init = TRUE;
+ did_findfile_init = true;
}
xfree(buf);
}
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
- if (first == TRUE) {
+ if (first == true) {
if (find_what == FINDFILE_DIR) {
semsg(_("E344: Can't find directory \"%s\" in cdpath"),
ff_file_to_find);
@@ -1653,7 +1641,7 @@ int vim_chdirfile(char *fname, CdCause cause)
STRLCPY(dir, fname, MAXPATHL);
*path_tail_with_sep(dir) = NUL;
- if (os_dirname(NameBuff, sizeof(NameBuff)) != OK) {
+ if (os_dirname((char_u *)NameBuff, sizeof(NameBuff)) != OK) {
NameBuff[0] = NUL;
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index b98984017b..d67cd36a85 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -18,7 +18,9 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
+#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
@@ -40,6 +42,7 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -48,7 +51,6 @@
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/sha256.h"
#include "nvim/shada.h"
@@ -132,7 +134,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
// calling filemess().
msg_scroll_save = msg_scroll;
if (shortmess(SHM_OVERALL) && !exiting && p_verbose == 0) {
- msg_scroll = FALSE;
+ msg_scroll = false;
}
if (!msg_scroll) { // wait a bit when overwriting an error msg
check_for_delay(false);
@@ -141,7 +143,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
msg_scroll = msg_scroll_save;
msg_scrolled_ign = true;
// may truncate the message to avoid a hit-return prompt
- msg_outtrans_attr(msg_may_trunc(FALSE, IObuff), attr);
+ msg_outtrans_attr((char *)msg_may_trunc(false, IObuff), attr);
msg_clr_eos();
ui_flush();
msg_scrolled_ign = false;
@@ -165,6 +167,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
/// READ_STDIN read from stdin instead of a file
/// READ_BUFFER read from curbuf instead of a file (converting after reading
/// stdin)
+/// READ_NOFILE do not read a file, only trigger BufReadCmd
/// READ_DUMMY read into a dummy buffer (to check if file contents changed)
/// READ_KEEP_UNDO don't clear undo info or read it from a file
/// READ_FIFO read from fifo/socket instead of a file
@@ -332,12 +335,18 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
curbuf->b_op_start = orig_start;
+
+ if (flags & READ_NOFILE) {
+ // Return NOTDONE instead of FAIL so that BufEnter can be triggered
+ // and other operations don't fail.
+ return NOTDONE;
+ }
}
if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) {
- msg_scroll = FALSE; // overwrite previous file message
+ msg_scroll = false; // overwrite previous file message
} else {
- msg_scroll = TRUE; // don't overwrite previous file message
+ msg_scroll = true; // don't overwrite previous file message
}
// If the name is too long we might crash further on, quit here.
if (fname != NULL && *fname != NUL) {
@@ -400,7 +409,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
*/
check_readonly = (newfile && (curbuf->b_flags & BF_CHECK_RO));
if (check_readonly && !readonlymode) {
- curbuf->b_p_ro = FALSE;
+ curbuf->b_p_ro = false;
}
if (newfile && !read_stdin && !read_buffer && !read_fifo) {
@@ -516,18 +525,18 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
* loaded. Help files always get readonly mode
*/
if ((check_readonly && file_readonly) || curbuf->b_help) {
- curbuf->b_p_ro = TRUE;
+ curbuf->b_p_ro = true;
}
if (set_options) {
// Don't change 'eol' if reading from buffer as it will already be
// correctly set when reading stdin.
if (!read_buffer) {
- curbuf->b_p_eol = TRUE;
- curbuf->b_start_eol = TRUE;
+ curbuf->b_p_eol = true;
+ curbuf->b_start_eol = true;
}
- curbuf->b_p_bomb = FALSE;
- curbuf->b_start_bomb = FALSE;
+ curbuf->b_p_bomb = false;
+ curbuf->b_start_bomb = false;
}
// Create a swap file now, so that other Vims are warned that we are
@@ -581,7 +590,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
return FAIL;
}
- ++no_wait_return; // don't wait for return yet
+ no_wait_return++; // don't wait for return yet
// Set '[ mark to the line above where the lines go (line 1 if zero).
orig_start = curbuf->b_op_start;
@@ -631,9 +640,9 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
if (aborting()) { // autocmds may abort script processing
- --no_wait_return;
+ no_wait_return--;
msg_scroll = msg_save;
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ curbuf->b_p_ro = true; // must use "w!" now
return FAIL;
}
/*
@@ -654,7 +663,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
} else {
emsg(_("E201: *ReadPre autocommands must not change current buffer"));
}
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ curbuf->b_p_ro = true; // must use "w!" now
return FAIL;
}
}
@@ -668,7 +677,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
}
}
- msg_scroll = FALSE; // overwrite the file message
+ msg_scroll = false; // overwrite the file message
/*
* Set linecnt now, before the "retry" caused by a wrong guess for
@@ -690,7 +699,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
* Decide which 'encoding' to use or use first.
*/
if (eap != NULL && eap->force_enc != 0) {
- fenc = (char *)enc_canonize((char_u *)eap->cmd + eap->force_enc);
+ fenc = enc_canonize(eap->cmd + eap->force_enc);
fenc_alloced = true;
keep_dest_enc = true;
} else if (curbuf->b_p_bin) {
@@ -706,10 +715,10 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
fenc_alloced = false;
} else if (*p_fencs == NUL) {
- fenc = (char *)curbuf->b_p_fenc; // use format from buffer
+ fenc = curbuf->b_p_fenc; // use format from buffer
fenc_alloced = false;
} else {
- fenc_next = (char *)p_fencs; // try items in 'fileencodings'
+ fenc_next = p_fencs; // try items in 'fileencodings'
fenc = (char *)next_fenc(&fenc_next, &fenc_alloced);
}
@@ -749,8 +758,8 @@ retry:
}
file_rewind = false;
if (set_options) {
- curbuf->b_p_bomb = FALSE;
- curbuf->b_start_bomb = FALSE;
+ curbuf->b_p_bomb = false;
+ curbuf->b_start_bomb = false;
}
conv_error = 0;
}
@@ -764,7 +773,7 @@ retry:
} else {
if (eap != NULL && eap->force_ff != 0) {
fileformat = get_fileformat_force(curbuf, eap);
- try_unix = try_dos = try_mac = FALSE;
+ try_unix = try_dos = try_mac = false;
} else if (curbuf->b_p_bin) {
fileformat = EOL_UNIX; // binary: use Unix format
} else if (*p_ffs ==
@@ -890,7 +899,7 @@ retry:
}
// Set "can_retry" when it's possible to rewind the file and try with
- // another "fenc" value. It's FALSE when no other "fenc" to try, reading
+ // another "fenc" value. It's false when no other "fenc" to try, reading
// stdin or fixed at a specific encoding.
can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo);
@@ -1009,7 +1018,7 @@ retry:
// Change NL to NUL to reverse the effect done
// below.
n = (int)(size - tlen);
- for (ni = 0; ni < n; ++ni) {
+ for (ni = 0; ni < n; ni++) {
if (p[ni] == NL) {
ptr[tlen++] = NUL;
} else {
@@ -1136,8 +1145,8 @@ retry:
size -= blen;
memmove(ptr, ptr + blen, (size_t)size);
if (set_options) {
- curbuf->b_p_bomb = TRUE;
- curbuf->b_start_bomb = TRUE;
+ curbuf->b_p_bomb = true;
+ curbuf->b_start_bomb = true;
}
}
@@ -1196,11 +1205,11 @@ retry:
}
// Deal with a bad byte and continue with the next.
- ++fromp;
- --from_size;
+ fromp++;
+ from_size--;
if (bad_char_behavior == BAD_KEEP) {
*top++ = *(fromp - 1);
- --to_size;
+ to_size--;
} else if (bad_char_behavior != BAD_DROP) {
*top++ = (char)bad_char_behavior;
to_size--;
@@ -1238,7 +1247,7 @@ retry:
// Check for a trailing incomplete UTF-8 sequence
tail = ptr + size - 1;
while (tail > ptr && (*tail & 0xc0) == 0x80) {
- --tail;
+ tail--;
}
if (tail + utf_byte2len(*tail) <= ptr + size) {
tail = NULL;
@@ -1556,7 +1565,7 @@ rewind_retry:
* Keep it fast!
*/
if (fileformat == EOL_MAC) {
- --ptr;
+ ptr--;
while (++ptr, --size >= 0) {
// catch most common case first
if ((c = *ptr) != NUL && c != CAR && c != NL) {
@@ -1577,20 +1586,20 @@ rewind_retry:
if (read_undo_file) {
sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
- ++lnum;
+ lnum++;
if (--read_count == 0) {
error = true; // break loop
line_start = ptr; // nothing left to write
break;
}
} else {
- --skip_count;
+ skip_count--;
}
line_start = ptr + 1;
}
}
} else {
- --ptr;
+ ptr--;
while (++ptr, --size >= 0) {
if ((c = *ptr) != NUL && c != NL) { // catch most common case
continue;
@@ -1633,14 +1642,14 @@ rewind_retry:
if (read_undo_file) {
sha256_update(&sha_ctx, (char_u *)line_start, (size_t)len);
}
- ++lnum;
+ lnum++;
if (--read_count == 0) {
error = true; // break loop
line_start = ptr; // nothing left to write
break;
}
} else {
- --skip_count;
+ skip_count--;
}
line_start = ptr + 1;
}
@@ -1670,7 +1679,7 @@ failed:
&& ptr == line_start + 1)) {
// remember for when writing
if (set_options) {
- curbuf->b_p_eol = FALSE;
+ curbuf->b_p_eol = false;
}
*ptr = NUL;
len = (colnr_T)(ptr - line_start + 1);
@@ -1727,7 +1736,7 @@ failed:
os_remove(tmpname); // delete converted file
xfree(tmpname);
}
- --no_wait_return; // may wait for return now
+ no_wait_return--; // may wait for return now
/*
* In recovery mode everything but autocommands is skipped.
@@ -1747,7 +1756,7 @@ failed:
linecnt = 0;
}
if (newfile || read_buffer) {
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
// After reading the text into the buffer the diff info needs to
// be updated.
diff_invalidate(curbuf);
@@ -1771,7 +1780,7 @@ failed:
if (!(flags & READ_DUMMY)) {
filemess(curbuf, (char_u *)sfname, (char_u *)_(e_interr), 0);
if (newfile) {
- curbuf->b_p_ro = TRUE; // must use "w!" now
+ curbuf->b_p_ro = true; // must use "w!" now
}
}
msg_scroll = msg_save;
@@ -1786,30 +1795,30 @@ failed:
#ifdef UNIX
if (S_ISFIFO(perm)) { // fifo
STRCAT(IObuff, _("[fifo]"));
- c = TRUE;
+ c = true;
}
if (S_ISSOCK(perm)) { // or socket
STRCAT(IObuff, _("[socket]"));
- c = TRUE;
+ c = true;
}
# ifdef OPEN_CHR_FILES
if (S_ISCHR(perm)) { // or character special
STRCAT(IObuff, _("[character special]"));
- c = TRUE;
+ c = true;
}
# endif
#endif
if (curbuf->b_p_ro) {
STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
- c = TRUE;
+ c = true;
}
if (read_no_eol_lnum) {
msg_add_eol();
- c = TRUE;
+ c = true;
}
if (ff_error == EOL_DOS) {
STRCAT(IObuff, _("[CR missing]"));
- c = TRUE;
+ c = true;
}
if (split) {
STRCAT(IObuff, _("[long lines split]"));
@@ -1817,25 +1826,25 @@ failed:
}
if (notconverted) {
STRCAT(IObuff, _("[NOT converted]"));
- c = TRUE;
+ c = true;
} else if (converted) {
STRCAT(IObuff, _("[converted]"));
- c = TRUE;
+ c = true;
}
if (conv_error != 0) {
sprintf((char *)IObuff + STRLEN(IObuff),
_("[CONVERSION ERROR in line %" PRId64 "]"), (int64_t)conv_error);
- c = TRUE;
+ c = true;
} else if (illegal_byte > 0) {
sprintf((char *)IObuff + STRLEN(IObuff),
_("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte);
- c = TRUE;
+ c = true;
} else if (error) {
STRCAT(IObuff, _("[READ ERRORS]"));
- c = TRUE;
+ c = true;
}
if (msg_add_fileformat(fileformat)) {
- c = TRUE;
+ c = true;
}
msg_add_lines(c, (long)linecnt, filesize);
@@ -1863,9 +1872,8 @@ failed:
// with errors writing the file requires ":w!"
if (newfile && (error
|| conv_error != 0
- || (illegal_byte > 0 && bad_char_behavior != BAD_KEEP)
- )) {
- curbuf->b_p_ro = TRUE;
+ || (illegal_byte > 0 && bad_char_behavior != BAD_KEEP))) {
+ curbuf->b_p_ro = true;
}
u_clearline(); // cannot use "U" command after adding lines
@@ -1946,7 +1954,7 @@ failed:
if (!au_did_filetype && *curbuf->b_p_ft != NUL) {
// EVENT_FILETYPE was not triggered but the buffer already has a
// filetype. Trigger EVENT_FILETYPE using the existing filetype.
- apply_autocmds(EVENT_FILETYPE, (char *)curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
+ apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
}
} else {
apply_autocmds_exarg(EVENT_FILEREADPOST, sfname, sfname,
@@ -1997,9 +2005,9 @@ static linenr_T readfile_linenr(linenr_T linecnt, char_u *p, char_u *endp)
linenr_T lnum;
lnum = curbuf->b_ml.ml_line_count - linecnt + 1;
- for (s = p; s < endp; ++s) {
+ for (s = p; s < endp; s++) {
if (*s == '\n') {
- ++lnum;
+ lnum++;
}
}
return lnum;
@@ -2016,11 +2024,11 @@ void prep_exarg(exarg_T *eap, const buf_T *buf)
snprintf(eap->cmd, cmd_len, "e ++enc=%s", buf->b_p_fenc);
eap->force_enc = 8;
eap->bad_char = buf->b_bad_char;
- eap->force_ff = *buf->b_p_ff;
+ eap->force_ff = (unsigned char)(*buf->b_p_ff);
eap->force_bin = buf->b_p_bin ? FORCE_BIN : FORCE_NOBIN;
- eap->read_edit = FALSE;
- eap->forceit = FALSE;
+ eap->read_edit = false;
+ eap->forceit = false;
}
/// Set default or forced 'fileformat' and 'binary'.
@@ -2048,8 +2056,8 @@ void set_file_options(int set_options, exarg_T *eap)
void set_forced_fenc(exarg_T *eap)
{
if (eap->force_enc != 0) {
- char_u *fenc = enc_canonize((char_u *)eap->cmd + eap->force_enc);
- set_string_option_direct("fenc", -1, (char *)fenc, OPT_FREE|OPT_LOCAL, 0);
+ char *fenc = enc_canonize(eap->cmd + eap->force_enc);
+ set_string_option_direct("fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0);
xfree(fenc);
}
}
@@ -2073,12 +2081,12 @@ static char_u *next_fenc(char **pp, bool *alloced)
}
p = (char_u *)vim_strchr((*pp), ',');
if (p == NULL) {
- r = enc_canonize((char_u *)(*pp));
+ r = (char_u *)enc_canonize(*pp);
*pp += STRLEN(*pp);
} else {
r = vim_strnsave((char_u *)(*pp), (size_t)(p - (char_u *)(*pp)));
*pp = (char *)p + 1;
- p = enc_canonize(r);
+ p = (char_u *)enc_canonize((char *)r);
xfree(r);
r = p;
}
@@ -2159,9 +2167,9 @@ char *new_file_message(void)
///
/// If "forceit" is true, we don't care for errors when attempting backups.
/// In case of an error everything possible is done to restore the original
-/// file. But when "forceit" is TRUE, we risk losing it.
+/// file. But when "forceit" is true, we risk losing it.
///
-/// When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and
+/// When "reset_changed" is true and "append" == false and "start" == 1 and
/// "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
///
/// This function must NOT use NameBuff (because it's called by autowrite()).
@@ -2202,9 +2210,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
int bufsize;
long perm; // file permissions
int retval = OK;
- int newfile = false; // TRUE if file doesn't exist yet
+ int newfile = false; // true if file doesn't exist yet
int msg_save = msg_scroll;
- int overwriting; // TRUE if writing over original
+ int overwriting; // true if writing over original
int no_eol = false; // no end-of-line written
int device = false; // writing to a device
int prev_got_int = got_int;
@@ -2213,7 +2221,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
static char *err_readonly =
"is read-only (cannot override: \"W\" in 'cpoptions')";
#if defined(UNIX)
- int made_writable = FALSE; // 'w' bit has been set
+ int made_writable = false; // 'w' bit has been set
#endif
// writing everything
int whole = (start == 1 && end == buf->b_ml.ml_line_count);
@@ -2232,7 +2240,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
vim_acl_T acl = NULL; /* ACL copied from original file to
backup or new file */
#endif
- int write_undo_file = FALSE;
+ int write_undo_file = false;
context_sha256_T sha_ctx;
unsigned int bkc = get_bkc_value(buf);
const pos_T orig_start = buf->b_op_start;
@@ -2264,7 +2272,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
// must init bw_conv_buf and bw_iconv_fd before jumping to "fail"
write_info.bw_conv_buf = NULL;
- write_info.bw_conv_error = FALSE;
+ write_info.bw_conv_error = false;
write_info.bw_conv_error_lnum = 0;
write_info.bw_restlen = 0;
#ifdef HAVE_ICONV
@@ -2312,10 +2320,10 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (buf->b_ffname != NULL && FNAMECMP(ffname, buf->b_ffname) == 0) {
overwriting = true;
} else {
- overwriting = FALSE;
+ overwriting = false;
}
- ++no_wait_return; // don't wait for return yet
+ no_wait_return++; // don't wait for return yet
/*
* Set '[ and '] marks to the lines to be written.
@@ -2327,12 +2335,12 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
{
aco_save_T aco;
- int buf_ffname = FALSE;
- int buf_sfname = FALSE;
- int buf_fname_f = FALSE;
- int buf_fname_s = FALSE;
- int did_cmd = FALSE;
- int nofile_err = FALSE;
+ int buf_ffname = false;
+ int buf_sfname = false;
+ int buf_fname_f = false;
+ int buf_fname_s = false;
+ int did_cmd = false;
+ int nofile_err = false;
int empty_memline = (buf->b_ml.ml_mfp == NULL);
bufref_T bufref;
@@ -2477,7 +2485,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
} else { // less lines
end -= old_line_count - buf->b_ml.ml_line_count;
if (end < start) {
- --no_wait_return;
+ no_wait_return--;
msg_scroll = msg_save;
emsg(_("E204: Autocommand changed number of lines in unexpected way"));
return FAIL;
@@ -2510,9 +2518,9 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
if (shortmess(SHM_OVER) && !exiting) {
- msg_scroll = FALSE; // overwrite previous file message
+ msg_scroll = false; // overwrite previous file message
} else {
- msg_scroll = TRUE; // don't overwrite previous file message
+ msg_scroll = true; // don't overwrite previous file message
}
if (!filtering) {
filemess(buf,
@@ -2523,7 +2531,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
#endif
(char_u *)"", 0); // show that we are busy
}
- msg_scroll = FALSE; // always overwrite the file message now
+ msg_scroll = false; // always overwrite the file message now
buffer = verbose_try_malloc(BUFSIZE);
// can't allocate big buffer, use small one (to be able to write when out of
@@ -2556,8 +2564,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
}
/* It's a device of some kind (or a fifo) which we can write to
* but for which we can't make a backup. */
- device = TRUE;
- newfile = TRUE;
+ device = true;
+ newfile = true;
perm = -1;
}
}
@@ -2569,8 +2577,8 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
goto fail;
}
if (c == NODE_WRITABLE) {
- device = TRUE;
- newfile = TRUE;
+ device = true;
+ newfile = true;
perm = -1;
} else {
perm = os_getperm((const char *)fname);
@@ -2636,7 +2644,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
* abort it.
*/
prev_got_int = got_int;
- got_int = FALSE;
+ got_int = false;
// Mark the buffer as 'being saved' to prevent changed buffer warnings
buf->b_saving = true;
@@ -2654,7 +2662,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
const bool no_prepend_dot = false;
if ((bkc & BKC_YES) || append) { // "yes"
- backup_copy = TRUE;
+ backup_copy = true;
} else if ((bkc & BKC_AUTO)) { // "auto"
int i;
@@ -2667,7 +2675,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (os_fileinfo_hardlinks(&file_info_old) > 1
|| !os_fileinfo_link(fname, &file_info)
|| !os_fileinfo_id_equal(&file_info, &file_info_old)) {
- backup_copy = TRUE;
+ backup_copy = true;
} else {
/*
* Check if we can create a file and set the owner/group to
@@ -2687,7 +2695,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
fd = os_open((char *)IObuff,
O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, (int)perm);
if (fd < 0) { // can't write in directory
- backup_copy = TRUE;
+ backup_copy = true;
} else {
#ifdef UNIX
os_fchown(fd, (uv_uid_t)file_info_old.stat.st_uid, (uv_gid_t)file_info_old.stat.st_gid);
@@ -2695,7 +2703,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
|| file_info.stat.st_uid != file_info_old.stat.st_uid
|| file_info.stat.st_gid != file_info_old.stat.st_gid
|| (long)file_info.stat.st_mode != perm) {
- backup_copy = TRUE;
+ backup_copy = true;
}
#endif
/* Close the file before removing it, on MS-Windows we
@@ -2717,7 +2725,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if ((bkc & BKC_BREAKSYMLINK)
&& file_info_link_ok
&& !os_fileinfo_id_equal(&file_info, &file_info_old)) {
- backup_copy = FALSE;
+ backup_copy = false;
}
// Hardlinks.
@@ -2725,7 +2733,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
&& os_fileinfo_hardlinks(&file_info_old) > 1
&& (!file_info_link_ok
|| os_fileinfo_id_equal(&file_info, &file_info_old))) {
- backup_copy = FALSE;
+ backup_copy = false;
}
#endif
}
@@ -2734,7 +2742,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (*p_bex == NUL) {
backup_ext = ".bak";
} else {
- backup_ext = (char *)p_bex;
+ backup_ext = p_bex;
}
if (backup_copy) {
@@ -2756,7 +2764,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
* For these reasons, the existing writable file must be truncated
* and reused. Creation of a backup COPY will be attempted.
*/
- dirp = (char *)p_bdir;
+ dirp = p_bdir;
while (*dirp) {
/*
* Isolate one directory name, using an entry in 'bdir'.
@@ -2767,7 +2775,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (trailing_pathseps) {
IObuff[dir_len - 2] = NUL;
}
- if (*dirp == NUL && !os_isdir(IObuff)) {
+ if (*dirp == NUL && !os_isdir((char *)IObuff)) {
int ret;
char *failed_dir;
if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) {
@@ -2787,7 +2795,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
rootname = (char *)get_file_in_dir((char_u *)fname, IObuff);
if (rootname == NULL) {
- some_error = TRUE; // out of memory
+ some_error = true; // out of memory
goto nobackup;
}
@@ -2802,7 +2810,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
if (backup == NULL) {
xfree(rootname);
- some_error = TRUE; // out of memory
+ some_error = true; // out of memory
goto nobackup;
}
@@ -2887,7 +2895,7 @@ nobackup:
if (backup == NULL && errmsg == NULL) {
SET_ERRMSG(_("E509: Cannot create backup file (add ! to override)"));
}
- // Ignore errors when forceit is TRUE.
+ // Ignore errors when forceit is true.
if ((some_error || errmsg != NULL) && !forceit) {
retval = FAIL;
goto fail;
@@ -2917,7 +2925,7 @@ nobackup:
* path/fo.o.h.bak Try all directories in 'backupdir', first one
* that works is used.
*/
- dirp = (char *)p_bdir;
+ dirp = p_bdir;
while (*dirp) {
/*
* Isolate one directory name and make the backup file name.
@@ -2928,7 +2936,7 @@ nobackup:
if (trailing_pathseps) {
IObuff[dir_len - 2] = NUL;
}
- if (*dirp == NUL && !os_isdir(IObuff)) {
+ if (*dirp == NUL && !os_isdir((char *)IObuff)) {
int ret;
char *failed_dir;
if ((ret = os_mkdir_recurse((char *)IObuff, 0755, &failed_dir)) != 0) {
@@ -3047,10 +3055,10 @@ nobackup:
// Check for forced 'fileencoding' from "++opt=val" argument.
if (eap != NULL && eap->force_enc != 0) {
fenc = eap->cmd + eap->force_enc;
- fenc = (char *)enc_canonize((char_u *)fenc);
+ fenc = enc_canonize(fenc);
fenc_tofree = fenc;
} else {
- fenc = (char *)buf->b_p_fenc;
+ fenc = buf->b_p_fenc;
}
// Check if the file needs to be converted.
@@ -3087,8 +3095,8 @@ nobackup:
if (!write_info.bw_conv_buf) {
end = 0;
}
- write_info.bw_first = TRUE;
- } else
+ write_info.bw_first = true;
+ } else {
#endif
/*
@@ -3104,6 +3112,11 @@ nobackup:
}
}
}
+
+#ifdef HAVE_ICONV
+}
+#endif
+
if (converted && wb_flags == 0
#ifdef HAVE_ICONV
&& write_info.bw_iconv_fd == (iconv_t)-1
@@ -3113,7 +3126,7 @@ nobackup:
SET_ERRMSG(_("E213: Cannot convert (add ! to write without conversion)"));
goto restore_backup;
}
- notconverted = TRUE;
+ notconverted = true;
}
// If conversion is taking place, we may first pretend to write and check
@@ -3135,12 +3148,12 @@ nobackup:
} else {
// Open the file "wfname" for writing.
// We may try to open the file twice: If we can't write to the file
- // and forceit is TRUE we delete the existing file and try to
+ // and forceit is true we delete the existing file and try to
// create a new one. If this still fails we may have lost the
// original file! (this may happen when the user reached his
// quotum for number of files).
// Appending will fail if the file does not exist and forceit is
- // FALSE.
+ // false.
while ((fd = os_open(wfname,
O_WRONLY |
(append ?
@@ -3364,7 +3377,7 @@ restore_backup:
// been written to disk and we don't lose it.
// For a device do try the fsync() but don't complain if it does not work
// (could be a pipe).
- // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops.
+ // If the 'fsync' option is false, don't fsync(). Useful for laptops.
int error;
if (p_fs && (error = os_fsync(fd)) != 0 && !device
// fsync not supported on this storage.
@@ -3483,7 +3496,7 @@ restore_backup:
}
lnum -= start; // compute number of written lines
- --no_wait_return; // may wait for return now
+ no_wait_return--; // may wait for return now
#if !defined(UNIX)
fname = sfname; // use shortname now, for the messages
@@ -3493,32 +3506,32 @@ restore_backup:
c = false;
if (write_info.bw_conv_error) {
STRCAT(IObuff, _(" CONVERSION ERROR"));
- c = TRUE;
+ c = true;
if (write_info.bw_conv_error_lnum != 0) {
vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %" PRId64 ";"),
(int64_t)write_info.bw_conv_error_lnum);
}
} else if (notconverted) {
STRCAT(IObuff, _("[NOT converted]"));
- c = TRUE;
+ c = true;
} else if (converted) {
STRCAT(IObuff, _("[converted]"));
- c = TRUE;
+ c = true;
}
if (device) {
STRCAT(IObuff, _("[Device]"));
- c = TRUE;
+ c = true;
} else if (newfile) {
STRCAT(IObuff, new_file_message());
c = true;
}
if (no_eol) {
msg_add_eol();
- c = TRUE;
+ c = true;
}
// may add [unix/dos/mac]
if (msg_add_fileformat(fileformat)) {
- c = TRUE;
+ c = true;
}
msg_add_lines(c, (long)lnum, nchars); // add line/char count
if (!shortmess(SHM_WRITE)) {
@@ -3566,7 +3579,7 @@ restore_backup:
* the backup file our 'original' file.
*/
if (*p_pm && dobackup) {
- char *const org = modname(fname, (char *)p_pm, false);
+ char *const org = modname(fname, p_pm, false);
if (backup != NULL) {
/*
@@ -3622,7 +3635,7 @@ restore_backup:
* Finish up. We get here either after failure or success.
*/
fail:
- --no_wait_return; // may wait for return now
+ no_wait_return--; // may wait for return now
nofail:
// Done saving, we accept changed buffer warnings again
@@ -3709,23 +3722,23 @@ nofail:
if (append) {
apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
} else if (filtering) {
apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
} else if (reset_changed && whole) {
apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
} else {
apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname,
- FALSE, curbuf, eap);
+ false, curbuf, eap);
}
// restore curwin/curbuf and a few other things
aucmd_restbuf(&aco);
if (aborting()) { // autocmds may abort script processing
- retval = FALSE;
+ retval = false;
}
}
@@ -3992,11 +4005,11 @@ static int buf_write_bytes(struct bw_info *ip)
}
if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) {
- ip->bw_conv_error = TRUE;
+ ip->bw_conv_error = true;
ip->bw_conv_error_lnum = ip->bw_start_lnum;
}
if (c == NL) {
- ++ip->bw_start_lnum;
+ ip->bw_start_lnum++;
}
}
if (flags & FIO_LATIN1) {
@@ -4047,7 +4060,7 @@ static int buf_write_bytes(struct bw_info *ip)
to = (char *)ip->bw_conv_buf;
tolen = save_len;
}
- ip->bw_first = FALSE;
+ ip->bw_first = false;
}
/*
@@ -4056,7 +4069,7 @@ static int buf_write_bytes(struct bw_info *ip)
if ((iconv(ip->bw_iconv_fd, (void *)&from, &fromlen, &to, &tolen)
== (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL)
|| fromlen > CONV_RESTLEN) {
- ip->bw_conv_error = TRUE;
+ ip->bw_conv_error = true;
return FAIL;
}
@@ -4161,12 +4174,12 @@ static bool need_conversion(const char_u *fenc)
int fenc_flags;
if (*fenc == NUL || STRCMP(p_enc, fenc) == 0) {
- same_encoding = TRUE;
+ same_encoding = true;
fenc_flags = 0;
} else {
// Ignore difference between "ansi" and "latin1", "ucs-4" and
// "ucs-4be", etc.
- enc_flags = get_fio_flags(p_enc);
+ enc_flags = get_fio_flags((char_u *)p_enc);
fenc_flags = get_fio_flags(fenc);
same_encoding = (enc_flags != 0 && fenc_flags == enc_flags);
}
@@ -4190,7 +4203,7 @@ static int get_fio_flags(const char_u *name)
int prop;
if (*name == NUL) {
- name = p_enc;
+ name = (char_u *)p_enc;
}
prop = enc_canon_props(name);
if (prop & ENC_UNICODE) {
@@ -4294,10 +4307,10 @@ static int make_bom(char_u *buf, char_u *name)
/// Shorten filename of a buffer.
///
-/// @param force when TRUE: Use full path from now on for files currently being
+/// @param force when true: Use full path from now on for files currently being
/// edited, both for file name and swap file name. Try to shorten the file
/// names a bit, if safe to do so.
-/// when FALSE: Only try to shorten absolute file names.
+/// when false: Only try to shorten absolute file names.
///
/// For buffers that have buftype "nofile" or "scratch": never change the file
/// name.
@@ -4384,7 +4397,7 @@ char *modname(const char *fname, const char *ext, bool prepend_dot)
}
add_pathsep(retval);
fnamelen = strlen(retval);
- prepend_dot = FALSE; // nothing to prepend a dot to
+ prepend_dot = false; // nothing to prepend a dot to
} else {
fnamelen = strlen(fname);
retval = xmalloc(fnamelen + extlen + 3);
@@ -4767,7 +4780,7 @@ int vim_rename(const char_u *from, const char_u *to)
return 0;
}
-static int already_warned = FALSE;
+static int already_warned = false;
/// Check if any not hidden buffer has been changed.
/// Postpone the check if there are characters in the stuff buffer, a global
@@ -4776,7 +4789,7 @@ static int already_warned = FALSE;
///
/// @param focus called for GUI focus event
///
-/// @return TRUE if some message was written (screen should be redrawn and cursor positioned).
+/// @return true if some message was written (screen should be redrawn and cursor positioned).
int check_timestamps(int focus)
{
int didit = 0;
@@ -4784,15 +4797,15 @@ int check_timestamps(int focus)
// Don't check timestamps while system() or another low-level function may
// cause us to lose and gain focus.
if (no_check_timestamps > 0) {
- return FALSE;
+ return false;
}
// Avoid doing a check twice. The OK/Reload dialog can cause a focus
// event and we would keep on checking if the file is steadily growing.
// Do check again after typing something.
if (focus && did_check_timestamps) {
- need_check_timestamps = TRUE;
- return FALSE;
+ need_check_timestamps = true;
+ return false;
}
if (!stuff_empty() || global_busy || !typebuf_typed()
@@ -4819,8 +4832,8 @@ int check_timestamps(int focus)
}
}
}
- --no_wait_return;
- need_check_timestamps = FALSE;
+ no_wait_return--;
+ need_check_timestamps = false;
if (need_wait_return && didit == 2) {
// make sure msg isn't overwritten
msg_puts("\n");
@@ -4935,7 +4948,7 @@ int buf_check_timestamp(buf_T *buf)
buf_store_file_info(buf, &file_info);
}
- if (os_isdir((char_u *)buf->b_fname)) {
+ if (os_isdir(buf->b_fname)) {
// Don't do anything for a directory. Might contain the file explorer.
} else if ((buf->b_p_ar >= 0 ? buf->b_p_ar : p_ar)
&& !bufIsChanged(buf) && file_info_ok) {
@@ -5073,7 +5086,7 @@ int buf_check_timestamp(buf_T *buf)
redraw_cmdline = false;
}
}
- already_warned = TRUE;
+ already_warned = true;
}
xfree(path);
@@ -5122,7 +5135,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
// file, not reset the syntax highlighting, clear marks, diff status, etc.
// Force the fileformat and encoding to be the same.
if (reload_options) {
- memset(&ea, 0, sizeof(ea));
+ CLEAR_FIELD(ea);
} else {
prep_exarg(&ea, buf);
}
@@ -5304,8 +5317,8 @@ static void vim_mktempdir(void)
mode_t umask_save = umask(0077);
for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) {
// Expand environment variables, leave room for "/tmp/nvim.<user>/XXXXXX/999999999".
- expand_env((char_u *)temp_dirs[i], (char_u *)tmp, TEMP_FILE_PATH_MAXLEN - 64);
- if (!os_isdir((char_u *)tmp)) {
+ expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64);
+ if (!os_isdir(tmp)) {
continue;
}
@@ -5315,7 +5328,7 @@ static void vim_mktempdir(void)
xstrlcat(tmp, user, sizeof(tmp));
(void)os_mkdir(tmp, 0700); // Always create, to avoid a race.
bool owned = os_file_owned(tmp);
- bool isdir = os_isdir((char_u *)tmp);
+ bool isdir = os_isdir(tmp);
#ifdef UNIX
int perm = os_getperm(tmp); // XDG_RUNTIME_DIR must be owned by the user, mode 0700.
bool valid = isdir && owned && 0700 == (perm & 0777);
@@ -5580,14 +5593,14 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
char_u *regpat;
char allow_dirs;
bool match;
- char_u *p;
+ char *p;
tail = (char_u *)path_tail((char *)sfname);
// try all patterns in 'wildignore'
- p = list;
+ p = (char *)list;
while (*p) {
- copy_option_part((char **)&p, (char *)buf, ARRAY_SIZE(buf), ",");
+ copy_option_part(&p, (char *)buf, ARRAY_SIZE(buf), ",");
regpat = (char_u *)file_pat_to_reg_pat((char *)buf, NULL, &allow_dirs, false);
if (regpat == NULL) {
break;
@@ -5604,8 +5617,8 @@ bool match_file_list(char_u *list, char_u *sfname, char_u *ffname)
/// Convert the given pattern "pat" which has shell style wildcards in it, into
/// a regular expression, and return the result in allocated memory. If there
-/// is a directory path separator to be matched, then TRUE is put in
-/// allow_dirs, otherwise FALSE is put there -- webb.
+/// is a directory path separator to be matched, then true is put in
+/// allow_dirs, otherwise false is put there -- webb.
/// Handle backslashes before special characters, like "\*" and "\ ".
///
/// @param pat_end first char after pattern or NULL
@@ -5623,7 +5636,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
bool add_dollar = true;
if (allow_dirs != NULL) {
- *allow_dirs = FALSE;
+ *allow_dirs = false;
}
if (pat_end == NULL) {
pat_end = pat + STRLEN(pat);
@@ -5680,7 +5693,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
reg_pat[i++] = '.';
reg_pat[i++] = '*';
while (p[1] == '*') { // "**" matches like "*"
- ++p;
+ p++;
}
break;
case '.':
@@ -5709,7 +5722,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
reg_pat[i++] = '/';
reg_pat[i++] = ']';
if (allow_dirs != NULL) {
- *allow_dirs = TRUE;
+ *allow_dirs = true;
}
break;
}
@@ -5743,7 +5756,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
&& (!no_bslash || *p != '\\')
#endif
) {
- *allow_dirs = TRUE;
+ *allow_dirs = true;
}
reg_pat[i++] = '\\';
reg_pat[i++] = *p;
@@ -5756,7 +5769,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
reg_pat[i++] = '/';
reg_pat[i++] = ']';
if (allow_dirs != NULL) {
- *allow_dirs = TRUE;
+ *allow_dirs = true;
}
break;
#endif
@@ -5768,7 +5781,7 @@ char *file_pat_to_reg_pat(const char *pat, const char *pat_end, char *allow_dirs
case '}':
reg_pat[i++] = '\\';
reg_pat[i++] = ')';
- --nested;
+ nested--;
break;
case ',':
if (nested) {
diff --git a/src/nvim/fileio.h b/src/nvim/fileio.h
index 62d8b6142e..ae3c51f1bc 100644
--- a/src/nvim/fileio.h
+++ b/src/nvim/fileio.h
@@ -1,7 +1,6 @@
#ifndef NVIM_FILEIO_H
#define NVIM_FILEIO_H
-#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/garray.h"
@@ -16,6 +15,7 @@
#define READ_KEEP_UNDO 0x20 // keep undo info
#define READ_FIFO 0x40 // read from fifo or socket
#define READ_NOWINENTER 0x80 // do not trigger BufWinEnter
+#define READ_NOFILE 0x100 // do not read a file, do trigger BufReadCmd
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 8f26e03a94..b4ddb3ec08 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -14,6 +14,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_session.h"
@@ -31,7 +32,7 @@
#include "nvim/option.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
+#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/undo.h"
@@ -247,7 +248,7 @@ bool hasFoldingWin(win_T *const win, const linenr_T lnum, linenr_T *const firstp
// foldLevel() {{{2
/// @return fold level at line number "lnum" in the current window.
-int foldLevel(linenr_T lnum)
+static int foldLevel(linenr_T lnum)
{
// While updating the folds lines between invalid_top and invalid_bot have
// an undefined fold level. Otherwise update the folds first.
@@ -393,7 +394,7 @@ void opFoldRange(pos_T firstpos, pos_T lastpos, int opening, int recurse, int ha
}
// Force a redraw to remove the Visual highlighting.
if (had_visual) {
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
}
@@ -720,7 +721,7 @@ void deleteFold(win_T *const wp, const linenr_T start, const linenr_T end, const
emsg(_(e_nofold));
// Force a redraw to remove the Visual highlighting.
if (had_visual) {
- redraw_buf_later(wp->w_buffer, INVERTED);
+ redraw_buf_later(wp->w_buffer, UPD_INVERTED);
}
} else {
// Deleting markers may make cursor column invalid
@@ -756,7 +757,7 @@ void clearFolding(win_T *win)
/// The changes in lines from top to bot (inclusive).
void foldUpdate(win_T *wp, linenr_T top, linenr_T bot)
{
- if (disable_fold_update || compl_busy || State & MODE_INSERT) {
+ if (disable_fold_update || State & MODE_INSERT) {
return;
}
@@ -818,7 +819,7 @@ void foldUpdateAfterInsert(void)
void foldUpdateAll(win_T *win)
{
win->w_foldinvalid = true;
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
}
// foldMoveTo() {{{2
@@ -862,7 +863,7 @@ int foldMoveTo(const bool updown, const int dir, const long count)
if (fp - (fold_T *)gap->ga_data >= gap->ga_len) {
break;
}
- --fp;
+ fp--;
} else {
if (fp == (fold_T *)gap->ga_data) {
break;
@@ -1555,7 +1556,7 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
}
parseMarker(wp);
- foldAddMarker(buf, start, wp->w_p_fmr, foldstartmarkerlen);
+ foldAddMarker(buf, start, (char_u *)wp->w_p_fmr, foldstartmarkerlen);
foldAddMarker(buf, end, foldendmarker, foldendmarkerlen);
// Update both changes here, to avoid all folds after the start are
@@ -1574,9 +1575,9 @@ static void foldCreateMarkers(win_T *wp, pos_T start, pos_T end)
/// Add "marker[markerlen]" in 'commentstring' to position `pos`.
static void foldAddMarker(buf_T *buf, pos_T pos, const char_u *marker, size_t markerlen)
{
- char_u *cms = buf->b_p_cms;
+ char_u *cms = (char_u *)buf->b_p_cms;
char_u *newline;
- char_u *p = (char_u *)strstr((char *)buf->b_p_cms, "%s");
+ char_u *p = (char_u *)strstr(buf->b_p_cms, "%s");
bool line_is_comment = false;
linenr_T lnum = pos.lnum;
@@ -1620,7 +1621,7 @@ static void deleteFoldMarkers(win_T *wp, fold_T *fp, int recursive, linenr_T lnu
lnum_off + fp->fd_top);
}
}
- foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off, wp->w_p_fmr,
+ foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off, (char_u *)wp->w_p_fmr,
foldstartmarkerlen);
foldDelMarker(wp->w_buffer, fp->fd_top + lnum_off + fp->fd_len - 1,
foldendmarker, foldendmarkerlen);
@@ -1638,7 +1639,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char_u *marker, size_t mark
return;
}
- char_u *cms = buf->b_p_cms;
+ char_u *cms = (char_u *)buf->b_p_cms;
char_u *line = ml_get_buf(buf, lnum, false);
for (char_u *p = line; *p != NUL; p++) {
if (STRNCMP(p, marker, markerlen) != 0) {
@@ -1728,7 +1729,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
emsg_silent++; // handle exceptions, but don't display errors
text =
- (char_u *)eval_to_string_safe((char *)wp->w_p_fdt, NULL,
+ (char_u *)eval_to_string_safe(wp->w_p_fdt, NULL,
was_set_insecurely(wp, "foldtext", OPT_LOCAL));
emsg_silent--;
@@ -1786,13 +1787,13 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin
// foldtext_cleanup() {{{2
/// Remove 'foldmarker' and 'commentstring' from "str" (in-place).
-void foldtext_cleanup(char_u *str)
+static void foldtext_cleanup(char_u *str)
{
// Ignore leading and trailing white space in 'commentstring'.
- char_u *cms_start = (char_u *)skipwhite((char *)curbuf->b_p_cms);
+ char_u *cms_start = (char_u *)skipwhite(curbuf->b_p_cms);
size_t cms_slen = STRLEN(cms_start);
while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) {
- --cms_slen;
+ cms_slen--;
}
// locate "%s" in 'commentstring', use the part before and after it.
@@ -1804,7 +1805,7 @@ void foldtext_cleanup(char_u *str)
// exclude white space before "%s"
while (cms_slen > 0 && ascii_iswhite(cms_start[cms_slen - 1])) {
- --cms_slen;
+ cms_slen--;
}
// skip "%s" and white space after it
@@ -2853,7 +2854,7 @@ static void foldlevelIndent(fline_T *flp)
// empty line or lines starting with a character in 'foldignore': level
// depends on surrounding lines
- if (*s == NUL || vim_strchr((char *)flp->wp->w_p_fdi, *s) != NULL) {
+ if (*s == NUL || vim_strchr(flp->wp->w_p_fdi, *s) != NULL) {
// first and last line can't be undefined, use level 0
if (lnum == 1 || lnum == buf->b_ml.ml_line_count) {
flp->lvl = 0;
@@ -2906,7 +2907,7 @@ static void foldlevelExpr(fline_T *flp)
const bool save_keytyped = KeyTyped;
int c;
- const int n = eval_foldexpr((char *)flp->wp->w_p_fde, &c);
+ const int n = eval_foldexpr(flp->wp->w_p_fde, &c);
KeyTyped = save_keytyped;
switch (c) {
@@ -2984,8 +2985,8 @@ static void foldlevelExpr(fline_T *flp)
/// Relies on the option value to have been checked for correctness already.
static void parseMarker(win_T *wp)
{
- foldendmarker = (char_u *)vim_strchr((char *)wp->w_p_fmr, ',');
- foldstartmarkerlen = (size_t)(foldendmarker++ - wp->w_p_fmr);
+ foldendmarker = (char_u *)vim_strchr(wp->w_p_fmr, ',');
+ foldstartmarkerlen = (size_t)(foldendmarker++ - (char_u *)wp->w_p_fmr);
foldendmarkerlen = STRLEN(foldendmarker);
}
@@ -3002,7 +3003,7 @@ static void foldlevelMarker(fline_T *flp)
int start_lvl = flp->lvl;
// cache a few values for speed
- char_u *startmarker = flp->wp->w_p_fmr;
+ char_u *startmarker = (char_u *)flp->wp->w_p_fmr;
int cstart = *startmarker;
startmarker++;
int cend = *foldendmarker;
@@ -3190,3 +3191,119 @@ static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off)
}
// }}}1
+
+/// "foldclosed()" and "foldclosedend()" functions
+static void foldclosed_both(typval_T *argvars, typval_T *rettv, int end)
+{
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ linenr_T first;
+ linenr_T last;
+ if (hasFoldingWin(curwin, lnum, &first, &last, false, NULL)) {
+ if (end) {
+ rettv->vval.v_number = (varnumber_T)last;
+ } else {
+ rettv->vval.v_number = (varnumber_T)first;
+ }
+ return;
+ }
+ }
+ rettv->vval.v_number = -1;
+}
+
+/// "foldclosed()" function
+void f_foldclosed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ foldclosed_both(argvars, rettv, false);
+}
+
+/// "foldclosedend()" function
+void f_foldclosedend(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ foldclosed_both(argvars, rettv, true);
+}
+
+/// "foldlevel()" function
+void f_foldlevel(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 = foldLevel(lnum);
+ }
+}
+
+/// "foldtext()" function
+void f_foldtext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+
+ linenr_T foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART);
+ linenr_T foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND);
+ char_u *dashes = (char_u *)get_vim_var_str(VV_FOLDDASHES);
+ if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count) {
+ // Find first non-empty line in the fold.
+ linenr_T lnum;
+ for (lnum = foldstart; lnum < foldend; lnum++) {
+ if (!linewhite(lnum)) {
+ break;
+ }
+ }
+
+ // Find interesting text in this line.
+ char_u *s = (char_u *)skipwhite((char *)ml_get(lnum));
+ // skip C comment-start
+ if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) {
+ s = (char_u *)skipwhite((char *)s + 2);
+ if (*skipwhite((char *)s) == NUL && lnum + 1 < foldend) {
+ s = (char_u *)skipwhite((char *)ml_get(lnum + 1));
+ if (*s == '*') {
+ s = (char_u *)skipwhite((char *)s + 1);
+ }
+ }
+ }
+ int count = foldend - foldstart + 1;
+ char *txt = NGETTEXT("+-%s%3ld line: ", "+-%s%3ld lines: ", count);
+ size_t len = STRLEN(txt)
+ + STRLEN(dashes) // for %s
+ + 20 // for %3ld
+ + STRLEN(s); // concatenated
+ char_u *r = xmalloc(len);
+ snprintf((char *)r, len, txt, dashes, count);
+ len = STRLEN(r);
+ STRCAT(r, s);
+ // remove 'foldmarker' and 'commentstring'
+ foldtext_cleanup(r + len);
+ rettv->vval.v_string = (char *)r;
+ }
+}
+
+/// "foldtextresult(lnum)" function
+void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ char_u buf[FOLD_TEXT_LEN];
+ static bool entered = false;
+
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = NULL;
+ if (entered) {
+ return; // reject recursive use
+ }
+ entered = true;
+ linenr_T lnum = tv_get_lnum(argvars);
+ // Treat illegal types and illegal string values for {lnum} the same.
+ if (lnum < 0) {
+ lnum = 0;
+ }
+
+ foldinfo_T info = fold_info(curwin, lnum);
+ if (info.fi_lines > 0) {
+ char_u *text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
+ if (text == buf) {
+ text = vim_strsave(text);
+ }
+ rettv->vval.v_string = (char *)text;
+ }
+
+ entered = false;
+}
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 7a3c14b1bb..1afabe4e10 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -131,7 +131,7 @@ void ga_remove_duplicate_strings(garray_T *gap)
fnames[j - 1] = fnames[j];
}
- --gap->ga_len;
+ gap->ga_len--;
}
}
}
diff --git a/src/nvim/garray.h b/src/nvim/garray.h
index 56bd5c9130..0281678925 100644
--- a/src/nvim/garray.h
+++ b/src/nvim/garray.h
@@ -4,7 +4,7 @@
#include <stddef.h> // for size_t
#include "nvim/log.h"
-#include "nvim/types.h" // for char_u
+#include "nvim/types.h"
/// Structure used for growing arrays.
/// This is used to store information that only grows, is deleted all at
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index 70a7be86b5..d872ffd6a9 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -26,6 +26,7 @@ local c_id = (
local c_void = P('void')
local c_param_type = (
((P('Error') * fill * P('*') * fill) * Cc('error')) +
+ ((P('Arena') * fill * P('*') * fill) * Cc('arena')) +
C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) +
(C(c_id) * (ws ^ 1))
)
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index b167767f7a..67b8f5f0f5 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -17,6 +17,7 @@ local nvimdir = arg[1]
package.path = nvimdir .. '/?.lua;' .. package.path
_G.vim = loadfile(nvimdir..'/../../runtime/lua/vim/shared.lua')()
+_G.vim.inspect = loadfile(nvimdir..'/../../runtime/lua/vim/inspect.lua')()
local hashy = require'generators.hashy'
@@ -72,6 +73,11 @@ for i = 6, #arg do
-- for specifying errors
fn.parameters[#fn.parameters] = nil
end
+ if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'arena' then
+ -- return value is allocated in an arena
+ fn.arena_return = true
+ fn.parameters[#fn.parameters] = nil
+ end
end
end
input:close()
@@ -210,7 +216,7 @@ for i = 1, #functions do
if fn.impl_name == nil and fn.remote then
local args = {}
- output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)')
+ output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Arena* arena, Error *error)')
output:write('\n{')
output:write('\n#if MIN_LOG_LEVEL <= LOGLVL_DBG')
output:write('\n logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, true, "ch %" PRIu64 ": invoke '
@@ -319,6 +325,10 @@ for i = 1, #functions do
output:write(call_args)
end
+ if fn.arena_return then
+ output:write(', arena')
+ end
+
if fn.can_fail then
-- if the function can fail, also pass a pointer to the local error object
if #args > 0 then
@@ -355,11 +365,12 @@ local hashorder, hashfun = hashy.hashy_hash("msgpack_rpc_get_handler_for", vim.t
return "method_handlers["..idx.."].name"
end)
-output:write("static const MsgpackRpcRequestHandler method_handlers[] = {\n")
-for _, name in ipairs(hashorder) do
+output:write("const MsgpackRpcRequestHandler method_handlers[] = {\n")
+for n, name in ipairs(hashorder) do
local fn = remote_fns[name]
+ fn.handler_id = n-1
output:write(' { .name = "'..name..'", .fn = handle_'.. (fn.impl_name or fn.name)..
- ', .fast = '..tostring(fn.fast)..'},\n')
+ ', .fast = '..tostring(fn.fast)..', .arena_return = '..tostring(not not fn.arena_return)..'},\n')
end
output:write("};\n\n")
output:write(hashfun)
@@ -400,6 +411,8 @@ output:write([[
#include "nvim/api/private/helpers.h"
#include "nvim/lua/converter.h"
#include "nvim/lua/executor.h"
+#include "nvim/memory.h"
+
]])
include_headers(output, headers)
output:write('\n')
@@ -477,6 +490,13 @@ local function process_function(fn)
if fn.receives_channel_id then
cparams = 'LUA_INTERNAL_CALL, ' .. cparams
end
+ if fn.arena_return then
+ cparams = cparams .. '&arena, '
+ write_shifted_output(output, [[
+ Arena arena = ARENA_EMPTY;
+ ]])
+ end
+
if fn.can_fail then
cparams = cparams .. '&err'
else
@@ -511,15 +531,21 @@ local function process_function(fn)
else
return_type = fn.return_type
end
+ local free_retval
+ if fn.arena_return then
+ free_retval = "arena_mem_free(arena_finish(&arena));"
+ else
+ free_retval = "api_free_"..return_type:lower().."(ret);"
+ end
write_shifted_output(output, string.format([[
const %s ret = %s(%s);
nlua_push_%s(lstate, ret, true);
- api_free_%s(ret);
+ %s
%s
%s
return 1;
- ]], fn.return_type, fn.name, cparams, return_type, return_type:lower(),
- free_at_exit_code, err_throw_code))
+ ]], fn.return_type, fn.name, cparams, return_type,
+ free_retval, free_at_exit_code, err_throw_code))
else
write_shifted_output(output, string.format([[
%s(%s);
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 93bbaab74c..f9e888c20d 100755
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -125,7 +125,7 @@ for i = 1, #events do
local param = ev.parameters[j]
local copy = 'copy_'..param[2]
if param[1] == 'String' then
- send = send..' String copy_'..param[2]..' = copy_string('..param[2]..');\n'
+ send = send..' String copy_'..param[2]..' = copy_string('..param[2]..', NULL);\n'
argv = argv..', '..copy..'.data, INT2PTR('..copy..'.size)'
recv = (recv..' String '..param[2]..
' = (String){.data = argv['..argc..'],'..
@@ -134,7 +134,7 @@ for i = 1, #events do
recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
argc = argc+2
elseif param[1] == 'Array' then
- send = send..' Array '..copy..' = copy_array('..param[2]..');\n'
+ send = send..' Array '..copy..' = copy_array('..param[2]..', NULL);\n'
argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
recv = (recv..' Array '..param[2]..
' = (Array){.items = argv['..argc..'],'..
@@ -144,7 +144,7 @@ for i = 1, #events do
argc = argc+2
elseif param[1] == 'Object' then
send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
- send = send..' *'..copy..' = copy_object('..param[2]..');\n'
+ send = send..' *'..copy..' = copy_object('..param[2]..', NULL);\n'
argv = argv..', '..copy
recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
recv_argv = recv_argv..', '..param[2]
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index c72249161b..8e6d1f2634 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -28,13 +28,20 @@ local hashy = require'generators.hashy'
local hashpipe = io.open(funcsfname, 'wb')
local funcs = require('eval').funcs
+for _, func in pairs(funcs) do
+ if func.float_func then
+ func.func = "float_op_wrapper"
+ func.data = "{ .float_func = &"..func.float_func.." }"
+ end
+end
+
local metadata = mpack.unpack(io.open(metadata_file, 'rb'):read("*all"))
for _,fun in ipairs(metadata) do
if fun.eval then
funcs[fun.name] = {
args=#fun.parameters,
func='api_wrapper',
- data='&handle_'..fun.name,
+ data='{ .api_handler = &method_handlers['..fun.handler_id..'] }'
}
end
end
@@ -60,12 +67,12 @@ for _, name in ipairs(neworder) do
end
local base = def.base or "BASE_NONE"
local func = def.func or ('f_' .. name)
- local data = def.data or "NULL"
+ local data = def.data or "{ .nullptr = NULL }"
local fast = def.fast and 'true' or 'false'
- hashpipe:write((' { "%s", %s, %s, %s, %s, &%s, (FunPtr)%s },\n')
+ hashpipe:write((' { "%s", %s, %s, %s, %s, &%s, %s },\n')
:format(name, args[1], args[2], base, fast, func, data))
end
-hashpipe:write(' { NULL, 0, 0, BASE_NONE, false, NULL, NULL },\n')
+hashpipe:write(' { NULL, 0, 0, BASE_NONE, false, NULL, { .nullptr = NULL } },\n')
hashpipe:write("};\n\n")
hashpipe:write(hashfun)
hashpipe:close()
diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua
index aa96c97bc1..36553f4649 100644
--- a/src/nvim/generators/gen_unicode_tables.lua
+++ b/src/nvim/generators/gen_unicode_tables.lua
@@ -12,8 +12,8 @@
-- 2 then interval applies only to first, third, fifth, … character in range.
-- Fourth value is number that should be added to the codepoint to yield
-- folded/lower/upper codepoint.
--- 4. emoji_width and emoji_all tables: sorted lists of non-overlapping closed
--- intervals of Emoji characters. emoji_width contains all the characters
+-- 4. emoji_wide and emoji_all tables: sorted lists of non-overlapping closed
+-- intervals of Emoji characters. emoji_wide contains all the characters
-- which don't have ambiguous or double width, and emoji_all has all Emojis.
if arg[1] == '--help' then
print('Usage:')
@@ -288,7 +288,7 @@ local build_emoji_table = function(ut_fp, emojiprops, doublewidth, ambiwidth)
end
ut_fp:write('};\n')
- ut_fp:write('static const struct interval emoji_width[] = {\n')
+ ut_fp:write('static const struct interval emoji_wide[] = {\n')
for _, p in ipairs(emojiwidth) do
ut_fp:write(make_range(p[1], p[2]))
end
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 2b2889d4d6..0a9457ef35 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -15,8 +15,12 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
+#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/event/loop.h"
+#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/garray.h"
@@ -40,7 +44,6 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -78,11 +81,9 @@ static buffheader_T readbuf2 = { { NULL, { NUL } }, NULL, 0, 0 };
static int typeahead_char = 0; // typeahead char that's not flushed
-/*
- * when block_redo is TRUE redo buffer will not be changed
- * used by edit() to repeat insertions and 'V' command for redoing
- */
-static int block_redo = FALSE;
+// when block_redo is true redo buffer will not be changed
+// used by edit() to repeat insertions and 'V' command for redoing
+static int block_redo = false;
static int KeyNoremap = 0; // remapping flags
@@ -159,7 +160,7 @@ static char_u *get_buffcont(buffheader_T *buffer, int dozero)
p2 = p;
for (const buffblock_T *bp = buffer->bh_first.b_next;
bp != NULL; bp = bp->b_next) {
- for (const char_u *str = bp->b_str; *str;) {
+ for (const char_u *str = (char_u *)bp->b_str; *str;) {
*p2++ = *str++;
}
}
@@ -176,7 +177,7 @@ char_u *get_recorded(void)
char_u *p;
size_t len;
- p = get_buffcont(&recordbuff, TRUE);
+ p = get_buffcont(&recordbuff, true);
free_buff(&recordbuff);
/*
@@ -204,7 +205,7 @@ char_u *get_recorded(void)
/// K_SPECIAL in the returned string is escaped.
char_u *get_inserted(void)
{
- return get_buffcont(&redobuff, FALSE);
+ return get_buffcont(&redobuff, false);
}
/// Add string after the current block of the given buffer
@@ -316,7 +317,7 @@ static void add_char_buff(buffheader_T *buf, int c)
/// Get one byte from the read buffers. Use readbuf1 one first, use readbuf2
/// if that one is empty.
-/// If advance == TRUE go to the next char.
+/// If advance == true go to the next char.
/// No translation is done K_SPECIAL is escaped.
static int read_readbuffers(int advance)
{
@@ -338,7 +339,7 @@ static int read_readbuf(buffheader_T *buf, int advance)
}
buffblock_T *const curr = buf->bh_first.b_next;
- c = curr->b_str[buf->bh_index];
+ c = (char_u)curr->b_str[buf->bh_index];
if (advance) {
if (curr->b_str[++buf->bh_index] == NUL) {
@@ -365,19 +366,15 @@ static void start_stuff(void)
}
}
-/*
- * Return TRUE if the stuff buffer is empty.
- */
+/// Return true if the stuff buffer is empty.
int stuff_empty(void)
FUNC_ATTR_PURE
{
return (readbuf1.bh_first.b_next == NULL && readbuf2.bh_first.b_next == NULL);
}
-/*
- * Return TRUE if readbuf1 is empty. There may still be redo characters in
- * redbuf2.
- */
+/// Return true if readbuf1 is empty. There may still be redo characters in
+/// redbuf2.
int readbuf1_empty(void)
FUNC_ATTR_PURE
{
@@ -626,6 +623,34 @@ void stuffnumReadbuff(long n)
add_num_buff(&readbuf1, n);
}
+/// Stuff a string into the typeahead buffer, such that edit() will insert it
+/// literally ("literally" true) or interpret is as typed characters.
+void stuffescaped(const char *arg, bool literally)
+{
+ while (*arg != NUL) {
+ // Stuff a sequence of normal ASCII characters, that's fast. Also
+ // stuff K_SPECIAL to get the effect of a special key when "literally"
+ // is true.
+ const char *const start = arg;
+ while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL
+ && !literally)) {
+ arg++;
+ }
+ if (arg > start) {
+ stuffReadbuffLen(start, (arg - start));
+ }
+
+ // stuff a single special character
+ if (*arg != NUL) {
+ const int c = mb_cptr2char_adv((const char_u **)&arg);
+ if (literally && ((c < ' ' && c != TAB) || c == DEL)) {
+ stuffcharReadbuff(Ctrl_V);
+ }
+ stuffcharReadbuff(c);
+ }
+ }
+}
+
/// Read a character from the redo buffer. Translates K_SPECIAL and
/// multibyte characters.
/// The redo buffer is left as it is.
@@ -646,7 +671,7 @@ static int read_redo(bool init, bool old_redo)
if (bp == NULL) {
return FAIL;
}
- p = bp->b_str;
+ p = (char_u *)bp->b_str;
return OK;
}
if ((c = *p) == NUL) {
@@ -667,7 +692,7 @@ static int read_redo(bool init, bool old_redo)
}
if (*++p == NUL && bp->b_next != NULL) {
bp = bp->b_next;
- p = bp->b_str;
+ p = (char_u *)bp->b_str;
}
buf[i] = (char_u)c;
if (i == n - 1) { // last byte of a character
@@ -790,7 +815,7 @@ int start_redo_ins(void)
void stop_redo_ins(void)
{
- block_redo = FALSE;
+ block_redo = false;
}
/*
@@ -929,9 +954,9 @@ int ins_typebuf(char *str, int noremap, int offset, bool nottyped, bool silent)
} else {
nrm = noremap;
}
- for (i = 0; i < addlen; ++i) {
+ for (i = 0; i < addlen; i++) {
typebuf.tb_noremap[typebuf.tb_off + i + offset] =
- (char_u)((--nrm >= 0) ? val : RM_YES);
+ (uint8_t)((--nrm >= 0) ? val : RM_YES);
}
// tb_maplen and tb_silent only remember the length of mapped and/or
@@ -967,7 +992,7 @@ int ins_char_typebuf(int c, int modifiers)
return (int)len;
}
-/// Return TRUE if the typeahead buffer was changed (while waiting for a
+/// Return true if the typeahead buffer was changed (while waiting for a
/// character to arrive). Happens when a message was received from a client or
/// from feedkeys().
/// But check in a more generic way to avoid trouble: When "typebuf.tb_buf"
@@ -983,10 +1008,8 @@ bool typebuf_changed(int tb_change_cnt)
|| typebuf_was_filled);
}
-/*
- * Return TRUE if there are no characters in the typeahead buffer that have
- * not been typed (result from a mapping or come from ":normal").
- */
+/// Return true if there are no characters in the typeahead buffer that have
+/// not been typed (result from a mapping or come from ":normal").
int typebuf_typed(void)
FUNC_ATTR_PURE
{
@@ -1257,7 +1280,7 @@ void restore_typeahead(tasave_T *tp)
/// Open a new script file for the ":source!" command.
///
/// @param directly when true execute directly
-void openscript(char_u *name, bool directly)
+void openscript(char *name, bool directly)
{
if (curscript + 1 == NSCRIPT) {
emsg(_(e_nesting));
@@ -1336,7 +1359,7 @@ static void closescript(void)
file_free(scriptin[curscript], false);
scriptin[curscript] = NULL;
if (curscript > 0) {
- --curscript;
+ curscript--;
}
}
@@ -1350,9 +1373,7 @@ void close_all_scripts(void)
#endif
-/*
- * Return TRUE when reading keys from a script file.
- */
+/// Return true when reading keys from a script file.
int using_script(void)
FUNC_ATTR_PURE
{
@@ -1683,7 +1704,7 @@ int vpeekc_any(void)
/*
* Call vpeekc() without causing anything to be mapped.
- * Return TRUE if a character is available, FALSE otherwise.
+ * Return true if a character is available, false otherwise.
*/
int char_avail(void)
{
@@ -1695,6 +1716,149 @@ int char_avail(void)
return retval != NUL;
}
+/// "getchar()" and "getcharstr()" functions
+static void getchar_common(typval_T *argvars, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ varnumber_T n;
+ bool error = false;
+
+ no_mapping++;
+ allow_keys++;
+ for (;;) {
+ // Position the cursor. Needed after a message that ends in a space,
+ // or if event processing caused a redraw.
+ ui_cursor_goto(msg_row, msg_col);
+
+ if (argvars[0].v_type == VAR_UNKNOWN) {
+ // getchar(): blocking wait.
+ // TODO(bfredl): deduplicate shared logic with state_enter ?
+ if (!char_avail()) {
+ // flush output before waiting
+ ui_flush();
+ (void)os_inchar(NULL, 0, -1, 0, main_loop.events);
+ if (!multiqueue_empty(main_loop.events)) {
+ state_handle_k_event();
+ continue;
+ }
+ }
+ n = safe_vgetc();
+ } else if (tv_get_number_chk(&argvars[0], &error) == 1) {
+ // getchar(1): only check if char avail
+ n = vpeekc_any();
+ } else if (error || vpeekc_any() == NUL) {
+ // illegal argument or getchar(0) and no char avail: return zero
+ n = 0;
+ } else {
+ // getchar(0) and char avail() != NUL: get a character.
+ // Note that vpeekc_any() returns K_SPECIAL for K_IGNORE.
+ n = safe_vgetc();
+ }
+
+ if (n == K_IGNORE
+ || n == K_MOUSEMOVE
+ || n == K_VER_SCROLLBAR
+ || n == K_HOR_SCROLLBAR) {
+ continue;
+ }
+ break;
+ }
+ no_mapping--;
+ allow_keys--;
+
+ if (!ui_has_messages()) {
+ // redraw the screen after getchar()
+ update_screen(UPD_NOT_VALID);
+ }
+
+ set_vim_var_nr(VV_MOUSE_WIN, 0);
+ set_vim_var_nr(VV_MOUSE_WINID, 0);
+ set_vim_var_nr(VV_MOUSE_LNUM, 0);
+ set_vim_var_nr(VV_MOUSE_COL, 0);
+
+ rettv->vval.v_number = n;
+ if (n != 0 && (IS_SPECIAL(n) || mod_mask != 0)) {
+ char_u temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
+ int i = 0;
+
+ // Turn a special key into three bytes, plus modifier.
+ if (mod_mask != 0) {
+ temp[i++] = K_SPECIAL;
+ temp[i++] = KS_MODIFIER;
+ temp[i++] = (char_u)mod_mask;
+ }
+ if (IS_SPECIAL(n)) {
+ temp[i++] = K_SPECIAL;
+ temp[i++] = (char_u)K_SECOND(n);
+ temp[i++] = K_THIRD(n);
+ } else {
+ i += utf_char2bytes((int)n, (char *)temp + i);
+ }
+ assert(i < 10);
+ temp[i++] = NUL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = (char *)vim_strsave(temp);
+
+ if (is_mouse_key((int)n)) {
+ int row = mouse_row;
+ int col = mouse_col;
+ int grid = mouse_grid;
+ linenr_T lnum;
+ win_T *wp;
+ int winnr = 1;
+
+ if (row >= 0 && col >= 0) {
+ // Find the window at the mouse coordinates and compute the
+ // text position.
+ win_T *const win = mouse_find_win(&grid, &row, &col);
+ if (win == NULL) {
+ return;
+ }
+ (void)mouse_comp_pos(win, &row, &col, &lnum);
+ for (wp = firstwin; wp != win; wp = wp->w_next) {
+ winnr++;
+ }
+ set_vim_var_nr(VV_MOUSE_WIN, winnr);
+ set_vim_var_nr(VV_MOUSE_WINID, wp->handle);
+ set_vim_var_nr(VV_MOUSE_LNUM, lnum);
+ set_vim_var_nr(VV_MOUSE_COL, col + 1);
+ }
+ }
+ }
+}
+
+/// "getchar()" function
+void f_getchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ getchar_common(argvars, rettv);
+}
+
+/// "getcharstr()" function
+void f_getcharstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ getchar_common(argvars, rettv);
+
+ if (rettv->v_type == VAR_NUMBER) {
+ char temp[7]; // mbyte-char: 6, NUL: 1
+ const varnumber_T n = rettv->vval.v_number;
+ int i = 0;
+
+ if (n != 0) {
+ i += utf_char2bytes((int)n, (char *)temp);
+ }
+ assert(i < 7);
+ temp[i++] = NUL;
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = xstrdup(temp);
+ }
+}
+
+/// "getcharmod()" function
+void f_getcharmod(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = mod_mask;
+}
+
typedef enum {
map_result_fail, // failed, break loop
map_result_get, // get a character from typeahead
@@ -1735,7 +1899,7 @@ static bool at_ins_compl_key(void)
c = p[3] & 0x1f;
}
return (ctrl_x_mode_not_default() && vim_is_ctrl_x_key(c))
- || ((compl_cont_status & CONT_LOCAL) && (c == Ctrl_N || c == Ctrl_P));
+ || (compl_status_local() && (c == Ctrl_N || c == Ctrl_P));
}
/// Check if typebuf.tb_buf[] contains a modifier plus key that can be changed
@@ -1901,12 +2065,11 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// - Partly match: mlen == typebuf.tb_len
keylen = mp->m_keylen;
if (mlen == keylen || (mlen == typebuf.tb_len && typebuf.tb_len < keylen)) {
- char_u *s;
int n;
// If only script-local mappings are allowed, check if the
// mapping starts with K_SNR.
- s = typebuf.tb_noremap + typebuf.tb_off;
+ uint8_t *s = typebuf.tb_noremap + typebuf.tb_off;
if (*s == RM_SCRIPT
&& (mp->m_keys[0] != K_SPECIAL
|| mp->m_keys[1] != KS_EXTRA
@@ -1957,7 +2120,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
// Check for match with 'pastetoggle'
if (*p_pt != NUL && mp == NULL && (State & (MODE_INSERT | MODE_NORMAL))) {
- bool match = typebuf_match_len(p_pt, &mlen);
+ bool match = typebuf_match_len((char_u *)p_pt, &mlen);
if (match) {
// write chars to script file(s)
if (mlen > typebuf.tb_maplen) {
@@ -1966,7 +2129,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
}
del_typebuf(mlen, 0); // remove the chars
- set_option_value("paste", !p_paste, NULL, 0);
+ set_option_value_give_err("paste", !p_paste, NULL, 0);
if (!(State & MODE_INSERT)) {
msg_col = 0;
msg_row = Rows - 1;
@@ -2102,7 +2265,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
save_m_keys = vim_strsave(mp->m_keys);
if (save_m_luaref == LUA_NOREF) {
- save_m_str = vim_strsave(mp->m_str);
+ save_m_str = vim_strsave((char_u *)mp->m_str);
}
map_str = eval_map_expr(mp, NUL);
@@ -2134,7 +2297,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth)
vgetc_busy = save_vgetc_busy;
may_garbage_collect = save_may_garbage_collect;
} else {
- map_str = mp->m_str;
+ map_str = (char_u *)mp->m_str;
}
// Insert the 'to' part in the typebuf.tb_buf.
@@ -2218,8 +2381,8 @@ void check_end_reg_executing(bool advance)
///
/// if "advance" is true (vgetc()):
/// Really get the character.
-/// KeyTyped is set to TRUE in the case the user typed the key.
-/// KeyStuffed is TRUE if the character comes from the stuff buffer.
+/// KeyTyped is set to true in the case the user typed the key.
+/// KeyStuffed is true if the character comes from the stuff buffer.
/// if "advance" is false (vpeekc()):
/// Just look whether there is a character available.
/// Return NUL if not.
@@ -2251,10 +2414,11 @@ static int vgetorpeek(bool advance)
return NUL;
}
- ++vgetc_busy;
+ vgetc_busy++;
if (advance) {
- KeyStuffed = FALSE;
+ KeyStuffed = false;
+ typebuf_was_empty = false;
}
init_typebuf();
@@ -2305,7 +2469,7 @@ static int vgetorpeek(bool advance)
// flush all input
c = inchar(typebuf.tb_buf, typebuf.tb_buflen - 1, 0L);
- // If inchar() returns TRUE (script file was active) or we
+ // If inchar() returns true (script file was active) or we
// are inside a mapping, get out of Insert mode.
// Otherwise we behave like having gotten a CTRL-C.
// As a result typing CTRL-C in insert mode will
@@ -2354,7 +2518,7 @@ static int vgetorpeek(bool advance)
// write char to script file(s)
gotchars(typebuf.tb_buf + typebuf.tb_off, 1);
}
- KeyNoremap = typebuf.tb_noremap[typebuf.tb_off];
+ KeyNoremap = (unsigned char)typebuf.tb_noremap[typebuf.tb_off];
del_typebuf(1, 0);
}
break; // got character, break the for loop
@@ -2383,7 +2547,7 @@ static int vgetorpeek(bool advance)
&& (State & MODE_INSERT)
&& (p_timeout || (keylen == KEYLEN_PART_KEY && p_ttimeout))
&& (c = inchar(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_len, 3, 25L)) == 0) {
- colnr_T col = 0, vcol;
+ colnr_T col = 0;
char_u *ptr;
if (mode_displayed) {
@@ -2401,22 +2565,27 @@ static int vgetorpeek(bool advance)
// We are expecting to truncate the trailing
// white-space, so find the last non-white
// character -- webb
- col = vcol = curwin->w_wcol = 0;
+ curwin->w_wcol = 0;
ptr = get_cursor_line_ptr();
- while (col < curwin->w_cursor.col) {
- if (!ascii_iswhite(ptr[col])) {
- curwin->w_wcol = vcol;
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin,
+ curwin->w_cursor.lnum, 0, ptr, ptr);
+ while ((char_u *)cts.cts_ptr < ptr + curwin->w_cursor.col) {
+ if (!ascii_iswhite(*cts.cts_ptr)) {
+ curwin->w_wcol = cts.cts_vcol;
}
- vcol += lbr_chartabsize(ptr, ptr + col, vcol);
- col += utfc_ptr2len((char *)ptr + col);
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr += utfc_ptr2len(cts.cts_ptr);
}
+ clear_chartabsize_arg(&cts);
+
curwin->w_wrow = curwin->w_cline_row
+ curwin->w_wcol / curwin->w_width_inner;
curwin->w_wcol %= curwin->w_width_inner;
curwin->w_wcol += curwin_col_off();
col = 0; // no correction needed
} else {
- --curwin->w_wcol;
+ curwin->w_wcol--;
col = curwin->w_cursor.col - 1;
}
} else if (curwin->w_p_wrap && curwin->w_wrow) {
@@ -2480,6 +2649,11 @@ static int vgetorpeek(bool advance)
}
tc = c;
+ // set a flag to indicate this wasn't a normal char
+ if (advance) {
+ typebuf_was_empty = true;
+ }
+
// return 0 in normal_check()
if (pending_exmode_active) {
exmode_active = true;
@@ -2638,7 +2812,7 @@ static int vgetorpeek(bool advance)
gotchars(nop_buf, 3);
}
- --vgetc_busy;
+ vgetc_busy--;
return c;
}
@@ -2714,7 +2888,7 @@ int inchar(char_u *buf, int maxlen, long wait_time)
if (read_size <= 0) { // Did not get a character from script.
// If we got an interrupt, skip all previously typed characters and
- // return TRUE if quit reading script file.
+ // return true if quit reading script file.
// Stop reading typeahead when a single CTRL-C was read,
// fill_input_buf() returns this when not able to read from stdin.
// Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
@@ -2777,7 +2951,7 @@ int fix_input_buffer(char_u *buf, int len)
// Two characters are special: NUL and K_SPECIAL.
// Replace NUL by K_SPECIAL KS_ZERO KE_FILLER
// Replace K_SPECIAL by K_SPECIAL KS_SPECIAL KE_FILLER
- for (i = len; --i >= 0; ++p) {
+ for (i = len; --i >= 0; p++) {
if (p[0] == NUL
|| (p[0] == K_SPECIAL
&& (i < 2 || p[1] != KS_EXTRA))) {
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 317423ffa0..060db3b3b1 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -6,12 +6,14 @@
#include "nvim/ascii.h"
#include "nvim/event/loop.h"
-#include "nvim/ex_eval.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_eval_defs.h"
#include "nvim/iconv.h"
#include "nvim/macros.h"
#include "nvim/mbyte.h"
-#include "nvim/menu.h"
+#include "nvim/menu_defs.h"
#include "nvim/os/os_defs.h"
+#include "nvim/runtime.h"
#include "nvim/syntax_defs.h"
#include "nvim/types.h"
@@ -94,9 +96,6 @@ EXTERN struct nvim_stats_s {
EXTERN int Rows INIT(= DFLT_ROWS); // nr of rows in the screen
EXTERN int Columns INIT(= DFLT_COLS); // nr of columns in the screen
-EXTERN NS ns_hl_active INIT(= 0); // current ns that defines highlights
-EXTERN bool ns_hl_changed INIT(= false); // highlight need update
-
// We use 64-bit file functions here, if available. E.g. ftello() returns
// off_t instead of long, which helps if long is 32 bit and off_t is 64 bit.
// We assume that when fseeko() is available then ftello() is too.
@@ -143,6 +142,7 @@ EXTERN int vgetc_char INIT(= 0);
EXTERN int cmdline_row;
EXTERN bool redraw_cmdline INIT(= false); // cmdline must be redrawn
+EXTERN bool redraw_mode INIT(= false); // mode must be redrawn
EXTERN bool clear_cmdline INIT(= false); // cmdline must be cleared
EXTERN bool mode_displayed INIT(= false); // mode is being displayed
EXTERN int cmdline_star INIT(= false); // cmdline is encrypted
@@ -159,31 +159,10 @@ EXTERN colnr_T dollar_vcol INIT(= -1);
// Variables for Insert mode completion.
-// Length in bytes of the text being completed (this is deleted to be replaced
-// by the match.)
-EXTERN int compl_length INIT(= 0);
-
-// Set when doing something for completion that may call edit() recursively,
-// which is not allowed. Also used to disable folding during completion
-EXTERN bool compl_busy INIT(= false);
-
-// List of flags for method of completion.
-EXTERN int compl_cont_status INIT(= 0);
-#define CONT_ADDING 1 // "normal" or "adding" expansion
-#define CONT_INTRPT (2 + 4) // a ^X interrupted the current expansion
- // it's set only iff N_ADDS is set
-#define CONT_N_ADDS 4 // next ^X<> will add-new or expand-current
-#define CONT_S_IPOS 8 // next ^X<> will set initial_pos?
- // if so, word-wise-expansion will set SOL
-#define CONT_SOL 16 // pattern includes start of line, just for
- // word-wise expansion, not set for ^X^L
-#define CONT_LOCAL 32 // for ctrl_x_mode 0, ^X^P/^X^N do a local
- // expansion, (eg use complete=.)
-
-EXTERN char_u *edit_submode INIT(= NULL); // msg for CTRL-X submode
-EXTERN char_u *edit_submode_pre INIT(= NULL); // prepended to edit_submode
-EXTERN char_u *edit_submode_extra INIT(= NULL); // appended to edit_submode
-EXTERN hlf_T edit_submode_highl; // highl. method for extra info
+EXTERN char *edit_submode INIT(= NULL); // msg for CTRL-X submode
+EXTERN char *edit_submode_pre INIT(= NULL); // prepended to edit_submode
+EXTERN char *edit_submode_extra INIT(= NULL); // appended to edit_submode
+EXTERN hlf_T edit_submode_highl; // highl. method for extra info
// state for putting characters in the message area
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
@@ -198,7 +177,7 @@ EXTERN bool msg_scrolled_ign INIT(= false);
// is reset before the screen is redrawn, so we need to keep track of this.
EXTERN bool msg_did_scroll INIT(= false);
-EXTERN char_u *keep_msg INIT(= NULL); // msg to be shown after redraw
+EXTERN char *keep_msg INIT(= NULL); // msg to be shown after redraw
EXTERN int keep_msg_attr INIT(= 0); // highlight attr for keep_msg
EXTERN bool need_fileinfo INIT(= false); // do fileinfo() after redraw
EXTERN int msg_scroll INIT(= false); // msg_start() will scroll
@@ -248,9 +227,6 @@ EXTERN int lines_left INIT(= -1); // lines left for listing
EXTERN int msg_no_more INIT(= false); // don't use more prompt, truncate
// messages
-EXTERN char *sourcing_name INIT(= NULL); // name of error message source
-EXTERN linenr_T sourcing_lnum INIT(= 0); // line number of the source file
-
EXTERN int ex_nesting_level INIT(= 0); // nesting level
EXTERN int debug_break_level INIT(= -1); // break below this level
EXTERN bool debug_did_msg INIT(= false); // did "debug mode" message
@@ -265,9 +241,13 @@ EXTERN int do_profiling INIT(= PROF_NONE); ///< PROF_ values
/// Exception currently being thrown. Used to pass an exception to a different
/// cstack. Also used for discarding an exception before it is caught or made
-/// pending.
+/// pending. Only valid when did_throw is true.
EXTERN except_T *current_exception;
+/// An exception is being thrown. Reset when the exception is caught or as
+/// long as it is pending in a finally clause.
+EXTERN bool did_throw INIT(= false);
+
/// Set when a throw that cannot be handled in do_cmdline() must be propagated
/// to the cstack of the previously called do_cmdline().
EXTERN bool need_rethrow INIT(= false);
@@ -296,7 +276,7 @@ EXTERN int force_abort INIT(= false);
/// same as the "msg" field of that element, but can be identical to the "msg"
/// field of a later list element, when the "emsg_severe" flag was set when the
/// emsg() call was made.
-EXTERN struct msglist **msg_list INIT(= NULL);
+EXTERN msglist_T **msg_list INIT(= NULL);
/// When set, don't convert an error to an exception. Used when displaying the
/// interrupt message or reporting an exception that is still uncaught at the
@@ -348,8 +328,8 @@ EXTERN bool did_source_packages INIT(= false);
// provider function call
EXTERN struct caller_scope {
sctx_T script_ctx;
- char *sourcing_name, *autocmd_fname, *autocmd_match;
- linenr_T sourcing_lnum;
+ estack_T es_entry;
+ char *autocmd_fname, *autocmd_match;
int autocmd_bufnr;
void *funccalp;
} provider_caller_scope;
@@ -583,6 +563,8 @@ EXTERN bool can_si INIT(= false);
// one indent will be removed.
EXTERN bool can_si_back INIT(= false);
+EXTERN int old_indent INIT(= 0); ///< for ^^D command in insert mode
+
// w_cursor before formatting text.
EXTERN pos_T saved_cursor INIT(= { 0, 0, 0 });
@@ -689,7 +671,7 @@ EXTERN int swap_exists_action INIT(= SEA_NONE); ///< For dialog when swap file
EXTERN bool swap_exists_did_quit INIT(= false); ///< Selected "quit" at the dialog.
EXTERN char_u IObuff[IOSIZE]; ///< Buffer for sprintf, I/O, etc.
-EXTERN char_u NameBuff[MAXPATHL]; ///< Buffer for expanding file names
+EXTERN char NameBuff[MAXPATHL]; ///< Buffer for expanding file names
EXTERN char msg_buf[MSG_BUF_LEN]; ///< Small buffer for messages
EXTERN char os_buf[ ///< Buffer for the os/ layer
#if MAXPATHL > IOSIZE
@@ -708,6 +690,10 @@ EXTERN int recoverymode INIT(= false); // Set to true for "-r" option
// typeahead buffer
EXTERN typebuf_T typebuf INIT(= { NULL, NULL, 0, 0, 0, 0, 0, 0, 0 });
+/// Flag used to indicate that vgetorpeek() returned a char like Esc when the
+/// :normal argument was exhausted.
+EXTERN bool typebuf_was_empty INIT(= false);
+
EXTERN int ex_normal_busy INIT(= 0); // recursiveness of ex_normal()
EXTERN int ex_normal_lock INIT(= 0); // forbid use of ex_normal()
EXTERN int ignore_script INIT(= false); // ignore script input
@@ -749,9 +735,9 @@ EXTERN bool need_start_insertmode INIT(= false); ///< start insert mode soon
// including the terminating NUL
EXTERN char last_mode[MODE_MAX_LENGTH] INIT(= "n");
-EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
-EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
-EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
+EXTERN char *last_cmdline INIT(= NULL); // last command line (for ":)
+EXTERN char *repeat_cmdline INIT(= NULL); // command line for "."
+EXTERN char *new_last_cmdline INIT(= NULL); // new value for last_cmdline
EXTERN char *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline
EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline
EXTERN char *autocmd_match INIT(= NULL); // name for <amatch> on cmdline
@@ -775,7 +761,7 @@ EXTERN int keep_help_flag INIT(= false); // doing :ta from help file
// When a string option is NULL (which only happens in out-of-memory
// situations), it is set to empty_option, to avoid having to check for NULL
// everywhere.
-EXTERN char_u *empty_option INIT(= (char_u *)"");
+EXTERN char *empty_option INIT(= "");
EXTERN bool redir_off INIT(= false); // no redirection for a moment
EXTERN FILE *redir_fd INIT(= NULL); // message redirection file
@@ -815,7 +801,6 @@ EXTERN char *last_chdir_reason INIT(= NULL);
EXTERN bool km_stopsel INIT(= false);
EXTERN bool km_startsel INIT(= false);
-EXTERN int cedit_key INIT(= -1); ///< key value of 'cedit' option
EXTERN int cmdwin_type INIT(= 0); ///< type of cmdline window or 0
EXTERN int cmdwin_result INIT(= 0); ///< result of cmdline window or 0
EXTERN int cmdwin_level INIT(= 0); ///< cmdline recursion level
@@ -939,7 +924,7 @@ EXTERN char e_patnotf2[] INIT(= N_("E486: Pattern not found: %s"));
EXTERN char e_positive[] INIT(= N_("E487: Argument must be positive"));
EXTERN char e_prev_dir[] INIT(= N_("E459: Cannot go back to previous directory"));
-EXTERN char e_quickfix[] INIT(= N_("E42: No Errors"));
+EXTERN char e_no_errors[] INIT(= N_("E42: No Errors"));
EXTERN char e_loclist[] INIT(= N_("E776: No location list"));
EXTERN char e_re_damg[] INIT(= N_("E43: Damaged match string"));
EXTERN char e_re_corr[] INIT(= N_("E44: Corrupted regexp program"));
@@ -1020,6 +1005,8 @@ EXTERN char e_resulting_text_too_long[] INIT(= N_("E1240: Resulting text too lon
EXTERN char e_line_number_out_of_range[] INIT(= N_("E1247: Line number out of range"));
+EXTERN char e_highlight_group_name_invalid_char[] INIT(= N_("E5248: Invalid character in group name"));
+
EXTERN char e_highlight_group_name_too_long[] INIT(= N_("E1249: Highlight group name too long"));
EXTERN char e_undobang_cannot_redo_or_move_branch[]
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index 72e85c425d..329adfda12 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -1,10 +1,19 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+// Most of the routines in this file perform screen (grid) manipulations. The
+// given operation is performed physically on the screen. The corresponding
+// change is also made to the internal screen image. In this way, the editor
+// anticipates the effect of editing changes on the appearance of the screen.
+// That way, when we call update_screen() a complete redraw isn't usually
+// necessary. Another advantage is that we can keep adding code to anticipate
+// screen changes, and in the meantime, everything still works.
+//
+// The grid_*() functions write to the screen and handle updating grid->lines[].
+
#include "nvim/arabic.h"
#include "nvim/grid.h"
#include "nvim/highlight.h"
-#include "nvim/screen.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -116,7 +125,7 @@ void grid_putchar(ScreenGrid *grid, int c, int row, int col, int attr)
char buf[MB_MAXBYTES + 1];
buf[utf_char2bytes(c, buf)] = NUL;
- grid_puts(grid, (char_u *)buf, row, col, attr);
+ grid_puts(grid, buf, row, col, attr);
}
/// get a single character directly from grid.chars into "bytes[]".
@@ -139,7 +148,7 @@ void grid_getbytes(ScreenGrid *grid, int row, int col, char_u *bytes, int *attrp
/// attributes 'attr', and update chars[] and attrs[].
/// Note: only outputs within one row, message is truncated at grid boundary!
/// Note: if grid, row and/or col is invalid, nothing is done.
-void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
+void grid_puts(ScreenGrid *grid, char *text, int row, int col, int attr)
{
grid_puts_len(grid, text, -1, row, col, attr);
}
@@ -178,10 +187,10 @@ void grid_put_schar(ScreenGrid *grid, int row, int col, char_u *schar, int attr)
/// like grid_puts(), but output "text[len]". When "len" is -1 output up to
/// a NUL.
-void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col, int attr)
+void grid_puts_len(ScreenGrid *grid, char *text, int textlen, int row, int col, int attr)
{
size_t off;
- char_u *ptr = text;
+ char *ptr = text;
int len = textlen;
int c;
size_t max_off;
@@ -228,13 +237,13 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
while (col < grid->cols
&& (len < 0 || (int)(ptr - text) < len)
&& *ptr != NUL) {
- c = *ptr;
+ c = (unsigned char)(*ptr);
// check if this is the first byte of a multibyte
mbyte_blen = len > 0
- ? utfc_ptr2len_len(ptr, (int)((text + len) - ptr))
- : utfc_ptr2len((char *)ptr);
+ ? utfc_ptr2len_len((char_u *)ptr, (int)((text + len) - ptr))
+ : utfc_ptr2len(ptr);
u8c = len >= 0
- ? utfc_ptr2char_len(ptr, u8cc, (int)((text + len) - ptr))
+ ? utfc_ptr2char_len((char_u *)ptr, u8cc, (int)((text + len) - ptr))
: utfc_ptr2char(ptr, u8cc);
mbyte_cells = utf_char2cells(u8c);
if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
@@ -245,7 +254,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
nc1 = NUL;
} else {
nc = len >= 0
- ? utfc_ptr2char_len(ptr + mbyte_blen, pcc, (int)((text + len) - ptr - mbyte_blen))
+ ? utfc_ptr2char_len((char_u *)ptr + mbyte_blen, pcc,
+ (int)((text + len) - ptr - mbyte_blen))
: utfc_ptr2char(ptr + mbyte_blen, pcc);
nc1 = pcc[0];
}
@@ -311,7 +321,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col
ptr += mbyte_blen;
if (clear_next_cell) {
// This only happens at the end, display one space next.
- ptr = (char_u *)" ";
+ ptr = " ";
len = -1;
}
}
@@ -384,11 +394,11 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col, int
// double wide-char clear out the right half. Only needed in a
// terminal.
if (start_col > 0 && grid_fix_col(grid, start_col, row) != start_col) {
- grid_puts_len(grid, (char_u *)" ", 1, row, start_col - 1, 0);
+ grid_puts_len(grid, " ", 1, row, start_col - 1, 0);
}
if (end_col < grid->cols
&& grid_fix_col(grid, end_col, row) != end_col) {
- grid_puts_len(grid, (char_u *)" ", 1, row, end_col, 0);
+ grid_puts_len(grid, " ", 1, row, end_col, 0);
}
// if grid was resized (in ext_multigrid mode), the UI has no redraw updates
@@ -466,9 +476,9 @@ static int grid_char_needs_redraw(ScreenGrid *grid, size_t off_from, size_t off_
/// "endcol" gives the columns where valid characters are.
/// "clear_width" is the width of the window. It's > 0 if the rest of the line
/// needs to be cleared, negative otherwise.
-/// "rlflag" is TRUE in a rightleft window:
-/// When TRUE and "clear_width" > 0, clear columns 0 to "endcol"
-/// When FALSE and "clear_width" > 0, clear columns "endcol" to "clear_width"
+/// "rlflag" is true in a rightleft window:
+/// When true and "clear_width" > 0, clear columns 0 to "endcol"
+/// When false and "clear_width" > 0, clear columns "endcol" to "clear_width"
/// If "wrap" is true, then hint to the UI that "row" contains a line
/// which has wrapped into the next row.
void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int clear_width,
@@ -780,3 +790,123 @@ void grid_assign_handle(ScreenGrid *grid)
grid->handle = ++last_grid_handle;
}
}
+
+/// insert lines on the screen and move the existing lines down
+/// 'line_count' is the number of lines to be inserted.
+/// 'end' is the line after the scrolled part. Normally it is Rows.
+/// 'col' is the column from with we start inserting.
+//
+/// 'row', 'col' and 'end' are relative to the start of the region.
+void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
+{
+ int i;
+ int j;
+ unsigned temp;
+
+ int row_off = 0;
+ grid_adjust(&grid, &row_off, &col);
+ row += row_off;
+ end += row_off;
+
+ if (line_count <= 0) {
+ return;
+ }
+
+ // Shift line_offset[] line_count down to reflect the inserted lines.
+ // Clear the inserted lines.
+ for (i = 0; i < line_count; i++) {
+ if (width != grid->cols) {
+ // need to copy part of a line
+ j = end - 1 - i;
+ while ((j -= line_count) >= row) {
+ linecopy(grid, j + line_count, j, col, width);
+ }
+ j += line_count;
+ grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
+ grid->line_wraps[j] = false;
+ } else {
+ j = end - 1 - i;
+ temp = (unsigned)grid->line_offset[j];
+ while ((j -= line_count) >= row) {
+ grid->line_offset[j + line_count] = grid->line_offset[j];
+ grid->line_wraps[j + line_count] = grid->line_wraps[j];
+ }
+ grid->line_offset[j + line_count] = temp;
+ grid->line_wraps[j + line_count] = false;
+ grid_clear_line(grid, temp, grid->cols, false);
+ }
+ }
+
+ if (!grid->throttled) {
+ ui_call_grid_scroll(grid->handle, row, end, col, col + width, -line_count, 0);
+ }
+}
+
+/// delete lines on the screen and move lines up.
+/// 'end' is the line after the scrolled part. Normally it is Rows.
+/// When scrolling region used 'off' is the offset from the top for the region.
+/// 'row' and 'end' are relative to the start of the region.
+void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
+{
+ int j;
+ int i;
+ unsigned temp;
+
+ int row_off = 0;
+ grid_adjust(&grid, &row_off, &col);
+ row += row_off;
+ end += row_off;
+
+ if (line_count <= 0) {
+ return;
+ }
+
+ // Now shift line_offset[] line_count up to reflect the deleted lines.
+ // Clear the inserted lines.
+ for (i = 0; i < line_count; i++) {
+ if (width != grid->cols) {
+ // need to copy part of a line
+ j = row + i;
+ while ((j += line_count) <= end - 1) {
+ linecopy(grid, j - line_count, j, col, width);
+ }
+ j -= line_count;
+ grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
+ grid->line_wraps[j] = false;
+ } else {
+ // whole width, moving the line pointers is faster
+ j = row + i;
+ temp = (unsigned)grid->line_offset[j];
+ while ((j += line_count) <= end - 1) {
+ grid->line_offset[j - line_count] = grid->line_offset[j];
+ grid->line_wraps[j - line_count] = grid->line_wraps[j];
+ }
+ grid->line_offset[j - line_count] = temp;
+ grid->line_wraps[j - line_count] = false;
+ grid_clear_line(grid, temp, grid->cols, false);
+ }
+ }
+
+ if (!grid->throttled) {
+ ui_call_grid_scroll(grid->handle, row, end, col, col + width, line_count, 0);
+ }
+}
+
+static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
+{
+ unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col);
+ unsigned off_from = (unsigned)(grid->line_offset[from] + (size_t)col);
+
+ memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T));
+ memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T));
+}
+
+win_T *get_win_by_grid_handle(handle_T handle)
+{
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_grid_alloc.handle == handle) {
+ return wp;
+ }
+ }
+ return NULL;
+}
diff --git a/src/nvim/grid.h b/src/nvim/grid.h
index c38748940d..6a93bf3d90 100644
--- a/src/nvim/grid.h
+++ b/src/nvim/grid.h
@@ -6,6 +6,7 @@
#include "nvim/ascii.h"
#include "nvim/buffer_defs.h"
#include "nvim/grid_defs.h"
+#include "nvim/mbyte.h"
/// By default, all windows are drawn on a single rectangular grid, represented by
/// this ScreenGrid instance. In multigrid mode each window will have its own
@@ -18,6 +19,9 @@ EXTERN ScreenGrid default_grid INIT(= SCREEN_GRID_INIT);
#define DEFAULT_GRID_HANDLE 1 // handle for the default_grid
+/// While resizing the screen this flag is set.
+EXTERN bool resizing_screen INIT(= 0);
+
EXTERN schar_T *linebuf_char INIT(= NULL);
EXTERN sattr_T *linebuf_attr INIT(= NULL);
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index d7f7b8eb92..602fa8b71d 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -10,19 +10,16 @@
#include <string.h>
#include "nvim/ascii.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/hardcopy.h"
#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -31,11 +28,13 @@
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
#include "nvim/version.h"
+#include "nvim/vim.h"
/*
* To implement printing on a platform, the following functions must be
@@ -49,20 +48,20 @@
*
* int mch_print_begin(prt_settings_T *settings)
* Called to start the print job.
- * Return FALSE to abort.
+ * Return false to abort.
*
* int mch_print_begin_page(char_u *msg)
* Called at the start of each page.
* "msg" indicates the progress of the print job, can be NULL.
- * Return FALSE to abort.
+ * Return false to abort.
*
* int mch_print_end_page()
* Called at the end of each page.
- * Return FALSE to abort.
+ * Return false to abort.
*
* int mch_print_blank_page()
* Called to generate a blank page for collated, duplex, multiple copy
- * document. Return FALSE to abort.
+ * document. Return false to abort.
*
* void mch_print_end(prt_settings_T *psettings)
* Called at normal end of print job.
@@ -85,36 +84,33 @@
*
* mch_print_start_line(int margin, int page_line)
* Sets the current position at the start of line "page_line".
- * If margin is TRUE start in the left margin (for header and line number).
+ * If margin is true start in the left margin (for header and line number).
*
* int mch_print_text_out(char_u *p, size_t len);
* Output one character of text p[len] at the current position.
- * Return TRUE if there is no room for another character in the same line.
+ * Return true if there is no room for another character in the same line.
*
* Note that the generic code has no idea of margins. The machine code should
* simply make the page look smaller! The header and the line numbers are
* printed in the margin.
*/
-static option_table_T printer_opts[OPT_PRINT_NUM_OPTIONS]
- =
- {
- { "top", TRUE, 0, NULL, 0, FALSE },
- { "bottom", TRUE, 0, NULL, 0, FALSE },
- { "left", TRUE, 0, NULL, 0, FALSE },
- { "right", TRUE, 0, NULL, 0, FALSE },
- { "header", TRUE, 0, NULL, 0, FALSE },
- { "syntax", FALSE, 0, NULL, 0, FALSE },
- { "number", FALSE, 0, NULL, 0, FALSE },
- { "wrap", FALSE, 0, NULL, 0, FALSE },
- { "duplex", FALSE, 0, NULL, 0, FALSE },
- { "portrait", FALSE, 0, NULL, 0, FALSE },
- { "paper", FALSE, 0, NULL, 0, FALSE },
- { "collate", FALSE, 0, NULL, 0, FALSE },
- { "jobsplit", FALSE, 0, NULL, 0, FALSE },
- { "formfeed", FALSE, 0, NULL, 0, FALSE },
- }
-;
+static option_table_T printer_opts[OPT_PRINT_NUM_OPTIONS] = {
+ { "top", true, 0, NULL, 0, false },
+ { "bottom", true, 0, NULL, 0, false },
+ { "left", true, 0, NULL, 0, false },
+ { "right", true, 0, NULL, 0, false },
+ { "header", true, 0, NULL, 0, false },
+ { "syntax", false, 0, NULL, 0, false },
+ { "number", false, 0, NULL, 0, false },
+ { "wrap", false, 0, NULL, 0, false },
+ { "duplex", false, 0, NULL, 0, false },
+ { "portrait", false, 0, NULL, 0, false },
+ { "paper", false, 0, NULL, 0, false },
+ { "collate", false, 0, NULL, 0, false },
+ { "jobsplit", false, 0, NULL, 0, false },
+ { "formfeed", false, 0, NULL, 0, false },
+};
static const uint32_t cterm_color_8[8] = {
0x000000, 0xff0000, 0x00ff00, 0xffff00,
@@ -150,12 +146,12 @@ static int page_count;
static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] =
{
- { "c", FALSE, 0, NULL, 0, FALSE },
- { "a", FALSE, 0, NULL, 0, FALSE },
- { "r", FALSE, 0, NULL, 0, FALSE },
- { "b", FALSE, 0, NULL, 0, FALSE },
- { "i", FALSE, 0, NULL, 0, FALSE },
- { "o", FALSE, 0, NULL, 0, FALSE },
+ { "c", false, 0, NULL, 0, false },
+ { "a", false, 0, NULL, 0, false },
+ { "r", false, 0, NULL, 0, false },
+ { "b", false, 0, NULL, 0, false },
+ { "i", false, 0, NULL, 0, false },
+ { "o", false, 0, NULL, 0, false },
};
/*
@@ -265,7 +261,7 @@ struct prt_resfile_buffer_S {
*/
char *parse_printoptions(void)
{
- return parse_list_options(p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS);
+ return parse_list_options((char_u *)p_popt, printer_opts, OPT_PRINT_NUM_OPTIONS);
}
/*
@@ -274,7 +270,7 @@ char *parse_printoptions(void)
*/
char *parse_printmbfont(void)
{
- return parse_list_options(p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS);
+ return parse_list_options((char_u *)p_pmfn, mbfont_opts, OPT_MBFONT_NUM_OPTIONS);
}
/*
@@ -292,8 +288,8 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
char *ret = NULL;
char_u *stringp;
char_u *colonp;
- char_u *commap;
- char_u *p;
+ char *commap;
+ char *p;
size_t idx = 0; // init for GCC
int len;
@@ -315,14 +311,14 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
ret = N_("E550: Missing colon");
break;
}
- commap = (char_u *)vim_strchr((char *)stringp, ',');
+ commap = vim_strchr((char *)stringp, ',');
if (commap == NULL) {
- commap = option_str + STRLEN(option_str);
+ commap = (char *)option_str + STRLEN(option_str);
}
len = (int)(colonp - stringp);
- for (idx = 0; idx < table_size; ++idx) {
+ for (idx = 0; idx < table_size; idx++) {
if (STRNICMP(stringp, table[idx].name, len) == 0) {
break;
}
@@ -333,8 +329,8 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
break;
}
- p = colonp + 1;
- table[idx].present = TRUE;
+ p = (char *)colonp + 1;
+ table[idx].present = true;
if (table[idx].hasnum) {
if (!ascii_isdigit(*p)) {
@@ -342,15 +338,15 @@ static char *parse_list_options(char_u *option_str, option_table_T *table, size_
break;
}
- table[idx].number = getdigits_int((char **)&p, false, 0);
+ table[idx].number = getdigits_int(&p, false, 0);
}
- table[idx].string = p;
+ table[idx].string = (char_u *)p;
table[idx].strlen = (int)(commap - p);
- stringp = commap;
+ stringp = (char_u *)commap;
if (*stringp == ',') {
- ++stringp;
+ stringp++;
}
}
@@ -504,9 +500,7 @@ int prt_header_height(void)
return 2;
}
-/*
- * Return TRUE if using a line number for printing.
- */
+// Return true if using a line number for printing.
int prt_use_number(void)
{
return printer_opts[OPT_PRINT_NUMBER].present
@@ -524,7 +518,7 @@ int prt_get_unit(int idx)
static char *(units[4]) = PRT_UNIT_NAMES;
if (printer_opts[idx].present) {
- for (i = 0; i < 4; ++i) {
+ for (i = 0; i < 4; i++) {
if (STRNICMP(printer_opts[idx].string, units[i], 2) == 0) {
u = i;
break;
@@ -550,7 +544,7 @@ static void prt_header(prt_settings_T *const psettings, const int pagenum, const
if (*p_header != NUL) {
linenr_T tmp_lnum, tmp_topline, tmp_botline;
- int use_sandbox = FALSE;
+ int use_sandbox = false;
/*
* Need to (temporarily) set current line number and first/last line
@@ -619,7 +613,7 @@ static void prt_message(char_u *s)
{
// TODO(bfredl): delete this
grid_fill(&default_grid, Rows - 1, Rows, 0, Columns, ' ', ' ', 0);
- grid_puts(&default_grid, s, Rows - 1, 0, HL_ATTR(HLF_R));
+ grid_puts(&default_grid, (char *)s, Rows - 1, 0, HL_ATTR(HLF_R));
ui_flush();
}
@@ -632,8 +626,8 @@ void ex_hardcopy(exarg_T *eap)
int page_line;
int jobsplit;
- memset(&settings, 0, sizeof(prt_settings_T));
- settings.has_color = TRUE;
+ CLEAR_FIELD(settings);
+ settings.has_color = true;
if (*eap->arg == '>') {
char *errormsg = NULL;
@@ -667,7 +661,7 @@ void ex_hardcopy(exarg_T *eap)
settings.modec = 'c';
if (!syntax_present(curwin)) {
- settings.do_syntax = FALSE;
+ settings.do_syntax = false;
} else if (printer_opts[OPT_PRINT_SYNTAX].present
&& TOLOWER_ASC(printer_opts[OPT_PRINT_SYNTAX].string[0]) != 'a') {
settings.do_syntax =
@@ -734,7 +728,7 @@ void ex_hardcopy(exarg_T *eap)
prt_pos_T page_prtpos; // print position at page start
int side;
- memset(&page_prtpos, 0, sizeof(prt_pos_T));
+ CLEAR_FIELD(page_prtpos);
page_prtpos.file_line = eap->line1;
prtpos = page_prtpos;
@@ -749,11 +743,9 @@ void ex_hardcopy(exarg_T *eap)
/*
* Loop over all pages in the print job: 1 2 3 ...
*/
- for (page_count = 0; prtpos.file_line <= eap->line2; ++page_count) {
- /*
- * Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
- * For duplex: 12 12 12 34 34 34, ...
- */
+ for (page_count = 0; prtpos.file_line <= eap->line2; page_count++) {
+ // Loop over uncollated copies: 1 1 1, 2 2 2, 3 3 3, ...
+ // For duplex: 12 12 12 34 34 34, ...
for (uncollated_copies = 0;
uncollated_copies < settings.n_uncollated_copies;
uncollated_copies++) {
@@ -763,10 +755,8 @@ void ex_hardcopy(exarg_T *eap)
/*
* Do front and rear side of a page.
*/
- for (side = 0; side <= settings.duplex; ++side) {
- /*
- * Print one page.
- */
+ for (side = 0; side <= settings.duplex; side++) {
+ // Print one page.
// Check for interrupt character every page.
os_breakcheck();
@@ -837,7 +827,7 @@ void ex_hardcopy(exarg_T *eap)
}
}
if (settings.duplex && prtpos.file_line <= eap->line2) {
- ++page_count;
+ page_count++;
}
// Remember the position where the next page starts.
@@ -868,7 +858,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
{
colnr_T col;
char_u *line;
- int need_break = FALSE;
+ int need_break = false;
int outputlen;
int tab_spaces;
int print_pos;
@@ -881,7 +871,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
if (!ppos->ff && prt_use_number()) {
prt_line_number(psettings, page_line, ppos->file_line);
}
- ppos->ff = FALSE;
+ ppos->ff = false;
} else {
// left over from wrap halfway through a tab
print_pos = ppos->print_pos;
@@ -900,7 +890,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
}
// syntax highlighting stuff.
if (psettings->do_syntax) {
- id = syn_get_id(curwin, ppos->file_line, col, 1, NULL, FALSE);
+ id = syn_get_id(curwin, ppos->file_line, col, 1, NULL, false);
if (id > 0) {
id = syn_get_final_id(id);
} else {
@@ -944,7 +934,7 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
&& printer_opts[OPT_PRINT_FORMFEED].present
&& TOLOWER_ASC(printer_opts[OPT_PRINT_FORMFEED].string[0])
== 'y') {
- ppos->ff = TRUE;
+ ppos->ff = true;
need_break = 1;
} else {
need_break = mch_print_text_out(line + col, (size_t)outputlen);
@@ -1718,7 +1708,7 @@ static bool prt_open_resource(struct prt_ps_resource_S *resource)
semsg(_("E624: Can't open file \"%s\""), resource->filename);
return false;
}
- memset(prt_resfile.buffer, NUL, PRT_FILE_BUFFER_LEN);
+ CLEAR_FIELD(prt_resfile.buffer);
// Parse first line to ensure valid resource file
prt_resfile.len = (int)fread((char *)prt_resfile.buffer, sizeof(char_u),
@@ -1986,7 +1976,7 @@ void mch_print_cleanup(void)
if (prt_do_conv) {
convert_setup(&prt_conv, NULL, NULL);
- prt_do_conv = FALSE;
+ prt_do_conv = false;
}
if (prt_ps_fd != NULL) {
fclose(prt_ps_fd);
@@ -2118,11 +2108,11 @@ static int prt_match_encoding(char *p_encoding, struct prt_ps_mbfont_S *p_cmap,
for (mbenc = 0; mbenc < p_cmap->num_encodings; mbenc++) {
if (STRNICMP(p_mbenc->encoding, p_encoding, enc_len) == 0) {
*pp_mbenc = p_mbenc;
- return TRUE;
+ return true;
}
p_mbenc++;
}
- return FALSE;
+ return false;
}
static int prt_match_charset(char *p_charset, struct prt_ps_mbfont_S *p_cmap,
@@ -2141,11 +2131,11 @@ static int prt_match_charset(char *p_charset, struct prt_ps_mbfont_S *p_cmap,
for (mbchar = 0; mbchar < p_cmap->num_charsets; mbchar++) {
if (STRNICMP(p_mbchar->charset, p_charset, char_len) == 0) {
*pp_mbchar = p_mbchar;
- return TRUE;
+ return true;
}
p_mbchar++;
}
- return FALSE;
+ return false;
}
int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
@@ -2157,17 +2147,14 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
char_u *p;
int props;
int cmap = 0;
- char_u *p_encoding;
struct prt_ps_encoding_S *p_mbenc;
struct prt_ps_encoding_S *p_mbenc_first;
struct prt_ps_charset_S *p_mbchar = NULL;
- /*
- * Set up font and encoding.
- */
- p_encoding = enc_skip(p_penc);
+ // Set up font and encoding.
+ char_u *p_encoding = (char_u *)enc_skip(p_penc);
if (*p_encoding == NUL) {
- p_encoding = enc_skip(p_enc);
+ p_encoding = (char_u *)enc_skip(p_enc);
}
// Look for a multi-byte font that matches the encoding and character set.
@@ -2186,7 +2173,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
p_mbenc_first = p_mbenc;
effective_cmap = cmap;
}
- if (prt_match_charset((char *)p_pmcs, &prt_ps_mbfonts[cmap], &p_mbchar)) {
+ if (prt_match_charset(p_pmcs, &prt_ps_mbfonts[cmap], &p_mbchar)) {
break;
}
}
@@ -2279,7 +2266,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
prt_ps_font = &prt_ps_mb_font;
} else {
- prt_use_courier = FALSE;
+ prt_use_courier = false;
prt_ps_font = &prt_ps_courier_font;
}
@@ -2296,7 +2283,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
paper_name = "A4";
paper_strlen = 2;
}
- for (i = 0; i < (int)PRT_MEDIASIZE_LEN; ++i) {
+ for (i = 0; i < (int)PRT_MEDIASIZE_LEN; i++) {
if (STRLEN(prt_mediasize[i].name) == (unsigned)paper_strlen
&& STRNICMP(prt_mediasize[i].name, paper_name,
paper_strlen) == 0) {
@@ -2337,7 +2324,7 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
* Set up the font size.
*/
fontsize = PRT_PS_DEFAULT_FONTSIZE;
- for (p = p_pfn; (p = (char_u *)vim_strchr((char *)p, ':')) != NULL; p++) {
+ for (p = (char_u *)p_pfn; (p = (char_u *)vim_strchr((char *)p, ':')) != NULL; p++) {
if (p[1] == 'h' && ascii_isdigit(p[2])) {
fontsize = atoi((char *)p + 2);
}
@@ -2381,16 +2368,16 @@ int mch_print_init(prt_settings_T *psettings, char_u *jobname, int forceit)
* Set up printer duplex and tumble based on Duplex option setting - default
* is long sided duplex printing (i.e. no tumble).
*/
- prt_duplex = TRUE;
- prt_tumble = FALSE;
+ prt_duplex = true;
+ prt_tumble = false;
psettings->duplex = 1;
if (printer_opts[OPT_PRINT_DUPLEX].present) {
if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "off", 3) == 0) {
- prt_duplex = FALSE;
+ prt_duplex = false;
psettings->duplex = 0;
} else if (STRNICMP(printer_opts[OPT_PRINT_DUPLEX].string, "short", 5)
== 0) {
- prt_tumble = TRUE;
+ prt_tumble = true;
}
}
@@ -2601,13 +2588,13 @@ bool mch_print_begin(prt_settings_T *psettings)
// that cannot be found then default to "latin1".
// Note: VIM specific encoding header is always skipped.
if (!prt_out_mbyte) {
- p_encoding = (char *)enc_skip(p_penc);
+ p_encoding = enc_skip(p_penc);
if (*p_encoding == NUL
|| !prt_find_resource(p_encoding, &res_encoding)) {
// 'printencoding' not set or not supported - find alternate
int props;
- p_encoding = (char *)enc_skip(p_enc);
+ p_encoding = enc_skip(p_enc);
props = enc_canon_props((char_u *)p_encoding);
if (!(props & ENC_8BIT)
|| !prt_find_resource(p_encoding, &res_encoding)) {
@@ -2627,9 +2614,9 @@ bool mch_print_begin(prt_settings_T *psettings)
// For the moment there are no checks on encoding resource files to
// perform
} else {
- p_encoding = (char *)enc_skip(p_penc);
+ p_encoding = enc_skip(p_penc);
if (*p_encoding == NUL) {
- p_encoding = (char *)enc_skip(p_enc);
+ p_encoding = enc_skip(p_enc);
}
if (prt_use_courier) {
// Include ASCII range encoding vector
@@ -2647,9 +2634,9 @@ bool mch_print_begin(prt_settings_T *psettings)
}
prt_conv.vc_type = CONV_NONE;
- if (!(enc_canon_props(p_enc) & enc_canon_props((char_u *)p_encoding) & ENC_8BIT)) {
+ if (!(enc_canon_props((char_u *)p_enc) & enc_canon_props((char_u *)p_encoding) & ENC_8BIT)) {
// Set up encoding conversion if required
- if (convert_setup(&prt_conv, p_enc, (char_u *)p_encoding) == FAIL) {
+ if (convert_setup(&prt_conv, p_enc, p_encoding) == FAIL) {
semsg(_("E620: Unable to convert to print encoding \"%s\""),
p_encoding);
return false;
@@ -2943,7 +2930,7 @@ int mch_print_begin_page(char_u *str)
int mch_print_blank_page(void)
{
- return mch_print_begin_page(NULL) ? (mch_print_end_page()) : FALSE;
+ return mch_print_begin_page(NULL) ? (mch_print_end_page()) : false;
}
static double prt_pos_x = 0;
@@ -3075,7 +3062,8 @@ int mch_print_text_out(char_u *const textp, size_t len)
if (prt_do_conv) {
// Convert from multi-byte to 8-bit encoding
- tofree = p = string_convert(&prt_conv, p, &len);
+ p = (char_u *)string_convert(&prt_conv, (char *)p, &len);
+ tofree = p;
if (p == NULL) {
p = (char_u *)"";
len = 0;
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index 95ae7a152c..308f64f011 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -45,7 +45,7 @@ char hash_removed;
void hash_init(hashtab_T *ht)
{
// This zeroes all "ht_" entries and all the "hi_key" in "ht_smallarray".
- memset(ht, 0, sizeof(hashtab_T));
+ CLEAR_POINTER(ht);
ht->ht_array = ht->ht_smallarray;
ht->ht_mask = HT_INIT_SIZE - 1;
}
@@ -67,7 +67,7 @@ void hash_clear(hashtab_T *ht)
void hash_clear_all(hashtab_T *ht, unsigned int off)
{
size_t todo = ht->ht_used;
- for (hashitem_T *hi = ht->ht_array; todo > 0; ++hi) {
+ for (hashitem_T *hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
xfree(hi->hi_key - off);
todo--;
@@ -342,11 +342,13 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
hashitem_T *oldarray = keep_smallarray
? memcpy(temparray, ht->ht_smallarray, sizeof(temparray))
: ht->ht_array;
+
+ if (newarray_is_small) {
+ CLEAR_FIELD(ht->ht_smallarray);
+ }
hashitem_T *newarray = newarray_is_small
? ht->ht_smallarray
- : xmalloc(sizeof(hashitem_T) * newsize);
-
- memset(newarray, 0, sizeof(hashitem_T) * newsize);
+ : xcalloc(newsize, sizeof(hashitem_T));
// Move all the items from the old array to the new one, placing them in
// the right spot. The new array won't have any removed items, thus this
@@ -354,7 +356,7 @@ static void hash_may_resize(hashtab_T *ht, size_t minitems)
hash_T newmask = newsize - 1;
size_t todo = ht->ht_used;
- for (hashitem_T *olditem = oldarray; todo > 0; ++olditem) {
+ for (hashitem_T *olditem = oldarray; todo > 0; olditem++) {
if (HASHITEM_EMPTY(olditem)) {
continue;
}
diff --git a/src/nvim/help.c b/src/nvim/help.c
new file mode 100644
index 0000000000..245d80489f
--- /dev/null
+++ b/src/nvim/help.c
@@ -0,0 +1,1179 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// help.c: functions for Vim help
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/globals.h"
+#include "nvim/help.h"
+#include "nvim/memory.h"
+#include "nvim/option.h"
+#include "nvim/optionstr.h"
+#include "nvim/os/input.h"
+#include "nvim/path.h"
+#include "nvim/strings.h"
+#include "nvim/syntax.h"
+#include "nvim/tag.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "help.c.generated.h"
+#endif
+
+/// ":help": open a read-only window on a help file
+void ex_help(exarg_T *eap)
+{
+ char *arg;
+ char *tag;
+ FILE *helpfd; // file descriptor of help file
+ int n;
+ int i;
+ win_T *wp;
+ int num_matches;
+ char **matches;
+ char *p;
+ int empty_fnum = 0;
+ int alt_fnum = 0;
+ buf_T *buf;
+ int len;
+ char *lang;
+ const bool old_KeyTyped = KeyTyped;
+
+ if (eap != NULL) {
+ // A ":help" command ends at the first LF, or at a '|' that is
+ // followed by some text. Set nextcmd to the following command.
+ for (arg = eap->arg; *arg; arg++) {
+ if (*arg == '\n' || *arg == '\r'
+ || (*arg == '|' && arg[1] != NUL && arg[1] != '|')) {
+ *arg++ = NUL;
+ eap->nextcmd = arg;
+ break;
+ }
+ }
+ arg = eap->arg;
+
+ if (eap->forceit && *arg == NUL && !curbuf->b_help) {
+ emsg(_("E478: Don't panic!"));
+ return;
+ }
+
+ if (eap->skip) { // not executing commands
+ return;
+ }
+ } else {
+ arg = "";
+ }
+
+ // remove trailing blanks
+ p = arg + STRLEN(arg) - 1;
+ while (p > arg && ascii_iswhite(*p) && p[-1] != '\\') {
+ *p-- = NUL;
+ }
+
+ // Check for a specified language
+ lang = check_help_lang(arg);
+
+ // When no argument given go to the index.
+ if (*arg == NUL) {
+ arg = "help.txt";
+ }
+
+ // Check if there is a match for the argument.
+ n = find_help_tags(arg, &num_matches, &matches, eap != NULL && eap->forceit);
+
+ i = 0;
+ if (n != FAIL && lang != NULL) {
+ // Find first item with the requested language.
+ for (i = 0; i < num_matches; i++) {
+ len = (int)STRLEN(matches[i]);
+ if (len > 3 && matches[i][len - 3] == '@'
+ && STRICMP(matches[i] + len - 2, lang) == 0) {
+ break;
+ }
+ }
+ }
+ if (i >= num_matches || n == FAIL) {
+ if (lang != NULL) {
+ semsg(_("E661: Sorry, no '%s' help for %s"), lang, arg);
+ } else {
+ semsg(_("E149: Sorry, no help for %s"), arg);
+ }
+ if (n != FAIL) {
+ FreeWild(num_matches, matches);
+ }
+ return;
+ }
+
+ // The first match (in the requested language) is the best match.
+ tag = xstrdup(matches[i]);
+ FreeWild(num_matches, matches);
+
+ // Re-use an existing help window or open a new one.
+ // Always open a new one for ":tab help".
+ if (!bt_help(curwin->w_buffer) || cmdmod.cmod_tab != 0) {
+ if (cmdmod.cmod_tab != 0) {
+ wp = NULL;
+ } else {
+ wp = NULL;
+ FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) {
+ if (bt_help(wp2->w_buffer)) {
+ wp = wp2;
+ break;
+ }
+ }
+ }
+ if (wp != NULL && wp->w_buffer->b_nwindows > 0) {
+ win_enter(wp, true);
+ } else {
+ // There is no help window yet.
+ // Try to open the file specified by the "helpfile" option.
+ if ((helpfd = os_fopen(p_hf, READBIN)) == NULL) {
+ smsg(_("Sorry, help file \"%s\" not found"), p_hf);
+ goto erret;
+ }
+ fclose(helpfd);
+
+ // Split off help window; put it at far top if no position
+ // specified, the current window is vertically split and
+ // narrow.
+ n = WSP_HELP;
+ if (cmdmod.cmod_split == 0 && curwin->w_width != Columns
+ && curwin->w_width < 80) {
+ n |= WSP_TOP;
+ }
+ if (win_split(0, n) == FAIL) {
+ goto erret;
+ }
+
+ if (curwin->w_height < p_hh) {
+ win_setheight((int)p_hh);
+ }
+
+ // Open help file (do_ecmd() will set b_help flag, readfile() will
+ // set b_p_ro flag).
+ // Set the alternate file to the previously edited file.
+ alt_fnum = curbuf->b_fnum;
+ (void)do_ecmd(0, NULL, NULL, NULL, ECMD_LASTL,
+ ECMD_HIDE + ECMD_SET_HELP,
+ NULL); // buffer is still open, don't store info
+
+ if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
+ curwin->w_alt_fnum = alt_fnum;
+ }
+ empty_fnum = curbuf->b_fnum;
+ }
+ }
+
+ restart_edit = 0; // don't want insert mode in help file
+
+ // Restore KeyTyped, setting 'filetype=help' may reset it.
+ // It is needed for do_tag top open folds under the cursor.
+ KeyTyped = old_KeyTyped;
+
+ do_tag((char_u *)tag, DT_HELP, 1, false, true);
+
+ // Delete the empty buffer if we're not using it. Careful: autocommands
+ // may have jumped to another window, check that the buffer is not in a
+ // window.
+ if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
+ buf = buflist_findnr(empty_fnum);
+ if (buf != NULL && buf->b_nwindows == 0) {
+ wipe_buffer(buf, true);
+ }
+ }
+
+ // keep the previous alternate file
+ if (alt_fnum != 0 && curwin->w_alt_fnum == empty_fnum
+ && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0) {
+ curwin->w_alt_fnum = alt_fnum;
+ }
+
+erret:
+ xfree(tag);
+}
+
+/// ":helpclose": Close one help window
+void ex_helpclose(exarg_T *eap)
+{
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (bt_help(win->w_buffer)) {
+ win_close(win, false, eap->forceit);
+ return;
+ }
+ }
+}
+
+/// In an argument search for a language specifiers in the form "@xx".
+/// Changes the "@" to NUL if found, and returns a pointer to "xx".
+///
+/// @return NULL if not found.
+char *check_help_lang(char *arg)
+{
+ int len = (int)STRLEN(arg);
+
+ if (len >= 3 && arg[len - 3] == '@' && ASCII_ISALPHA(arg[len - 2])
+ && ASCII_ISALPHA(arg[len - 1])) {
+ arg[len - 3] = NUL; // remove the '@'
+ return arg + len - 2;
+ }
+ return NULL;
+}
+
+/// Return a heuristic indicating how well the given string matches. The
+/// smaller the number, the better the match. This is the order of priorities,
+/// from best match to worst match:
+/// - Match with least alphanumeric characters is better.
+/// - Match with least total characters is better.
+/// - Match towards the start is better.
+/// - Match starting with "+" is worse (feature instead of command)
+/// Assumption is made that the matched_string passed has already been found to
+/// match some string for which help is requested. webb.
+///
+/// @param offset offset for match
+/// @param wrong_case no matching case
+///
+/// @return a heuristic indicating how well the given string matches.
+int help_heuristic(char *matched_string, int offset, int wrong_case)
+ FUNC_ATTR_PURE
+{
+ int num_letters;
+ char *p;
+
+ num_letters = 0;
+ for (p = matched_string; *p; p++) {
+ if (ASCII_ISALNUM(*p)) {
+ num_letters++;
+ }
+ }
+
+ // Multiply the number of letters by 100 to give it a much bigger
+ // weighting than the number of characters.
+ // If there only is a match while ignoring case, add 5000.
+ // If the match starts in the middle of a word, add 10000 to put it
+ // somewhere in the last half.
+ // If the match is more than 2 chars from the start, multiply by 200 to
+ // put it after matches at the start.
+ if (offset > 0
+ && ASCII_ISALNUM(matched_string[offset])
+ && ASCII_ISALNUM(matched_string[offset - 1])) {
+ offset += 10000;
+ } else if (offset > 2) {
+ offset *= 200;
+ }
+ if (wrong_case) {
+ offset += 5000;
+ }
+ // Features are less interesting than the subjects themselves, but "+"
+ // alone is not a feature.
+ if (matched_string[0] == '+' && matched_string[1] != NUL) {
+ offset += 100;
+ }
+ return 100 * num_letters + (int)STRLEN(matched_string) + offset;
+}
+
+/// Compare functions for qsort() below, that checks the help heuristics number
+/// that has been put after the tagname by find_tags().
+static int help_compare(const void *s1, const void *s2)
+{
+ char *p1;
+ char *p2;
+
+ p1 = *(char **)s1 + strlen(*(char **)s1) + 1;
+ p2 = *(char **)s2 + strlen(*(char **)s2) + 1;
+
+ // Compare by help heuristic number first.
+ int cmp = strcmp(p1, p2);
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ // Compare by strings as tie-breaker when same heuristic number.
+ return strcmp(*(char **)s1, *(char **)s2);
+}
+
+/// Find all help tags matching "arg", sort them and return in matches[], with
+/// the number of matches in num_matches.
+/// The matches will be sorted with a "best" match algorithm.
+/// When "keep_lang" is true try keeping the language of the current buffer.
+int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep_lang)
+{
+ int i;
+
+ // Specific tags that either have a specific replacement or won't go
+ // through the generic rules.
+ static char *(except_tbl[][2]) = {
+ { "*", "star" },
+ { "g*", "gstar" },
+ { "[*", "[star" },
+ { "]*", "]star" },
+ { ":*", ":star" },
+ { "/*", "/star" }, // NOLINT
+ { "/\\*", "/\\\\star" },
+ { "\"*", "quotestar" },
+ { "**", "starstar" },
+ { "cpo-*", "cpo-star" },
+ { "/\\(\\)", "/\\\\(\\\\)" },
+ { "/\\%(\\)", "/\\\\%(\\\\)" },
+ { "?", "?" },
+ { "??", "??" },
+ { ":?", ":?" },
+ { "?<CR>", "?<CR>" },
+ { "g?", "g?" },
+ { "g?g?", "g?g?" },
+ { "g??", "g??" },
+ { "-?", "-?" },
+ { "q?", "q?" },
+ { "v_g?", "v_g?" },
+ { "/\\?", "/\\\\?" },
+ { "/\\z(\\)", "/\\\\z(\\\\)" },
+ { "\\=", "\\\\=" },
+ { ":s\\=", ":s\\\\=" },
+ { "[count]", "\\[count]" },
+ { "[quotex]", "\\[quotex]" },
+ { "[range]", "\\[range]" },
+ { ":[range]", ":\\[range]" },
+ { "[pattern]", "\\[pattern]" },
+ { "\\|", "\\\\bar" },
+ { "\\%$", "/\\\\%\\$" },
+ { "s/\\~", "s/\\\\\\~" },
+ { "s/\\U", "s/\\\\U" },
+ { "s/\\L", "s/\\\\L" },
+ { "s/\\1", "s/\\\\1" },
+ { "s/\\2", "s/\\\\2" },
+ { "s/\\3", "s/\\\\3" },
+ { "s/\\9", "s/\\\\9" },
+ { NULL, NULL }
+ };
+
+ static const char *(expr_table[]) = {
+ "!=?", "!~?", "<=?", "<?", "==?", "=~?",
+ ">=?", ">?", "is?", "isnot?"
+ };
+ char *d = (char *)IObuff; // assume IObuff is long enough!
+ d[0] = NUL;
+
+ if (STRNICMP(arg, "expr-", 5) == 0) {
+ // When the string starting with "expr-" and containing '?' and matches
+ // the table, it is taken literally (but ~ is escaped). Otherwise '?'
+ // is recognized as a wildcard.
+ for (i = (int)ARRAY_SIZE(expr_table); --i >= 0;) {
+ if (STRCMP(arg + 5, expr_table[i]) == 0) {
+ for (int si = 0, di = 0;; si++) {
+ if (arg[si] == '~') {
+ d[di++] = '\\';
+ }
+ d[di++] = arg[si];
+ if (arg[si] == NUL) {
+ break;
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ // Recognize a few exceptions to the rule. Some strings that contain
+ // '*'are changed to "star", otherwise '*' is recognized as a wildcard.
+ for (i = 0; except_tbl[i][0] != NULL; i++) {
+ if (STRCMP(arg, except_tbl[i][0]) == 0) {
+ STRCPY(d, except_tbl[i][1]);
+ break;
+ }
+ }
+ }
+
+ if (d[0] == NUL) { // no match in table
+ // Replace "\S" with "/\\S", etc. Otherwise every tag is matched.
+ // Also replace "\%^" and "\%(", they match every tag too.
+ // Also "\zs", "\z1", etc.
+ // Also "\@<", "\@=", "\@<=", etc.
+ // And also "\_$" and "\_^".
+ if (arg[0] == '\\'
+ && ((arg[1] != NUL && arg[2] == NUL)
+ || (vim_strchr("%_z@", arg[1]) != NULL
+ && arg[2] != NUL))) {
+ vim_snprintf(d, IOSIZE, "/\\\\%s", arg + 1);
+ // Check for "/\\_$", should be "/\\_\$"
+ if (d[3] == '_' && d[4] == '$') {
+ STRCPY(d + 4, "\\$");
+ }
+ } else {
+ // Replace:
+ // "[:...:]" with "\[:...:]"
+ // "[++...]" with "\[++...]"
+ // "\{" with "\\{" -- matching "} \}"
+ if ((arg[0] == '[' && (arg[1] == ':'
+ || (arg[1] == '+' && arg[2] == '+')))
+ || (arg[0] == '\\' && arg[1] == '{')) {
+ *d++ = '\\';
+ }
+
+ // If tag starts with "('", skip the "(". Fixes CTRL-] on ('option'.
+ if (*arg == '(' && arg[1] == '\'') {
+ arg++;
+ }
+ for (const char *s = arg; *s; s++) {
+ // Replace "|" with "bar" and '"' with "quote" to match the name of
+ // the tags for these commands.
+ // Replace "*" with ".*" and "?" with "." to match command line
+ // completion.
+ // Insert a backslash before '~', '$' and '.' to avoid their
+ // special meaning.
+ if ((char_u *)d - IObuff > IOSIZE - 10) { // getting too long!?
+ break;
+ }
+ switch (*s) {
+ case '|':
+ STRCPY(d, "bar");
+ d += 3;
+ continue;
+ case '"':
+ STRCPY(d, "quote");
+ d += 5;
+ continue;
+ case '*':
+ *d++ = '.';
+ break;
+ case '?':
+ *d++ = '.';
+ continue;
+ case '$':
+ case '.':
+ case '~':
+ *d++ = '\\';
+ break;
+ }
+
+ // Replace "^x" by "CTRL-X". Don't do this for "^_" to make
+ // ":help i_^_CTRL-D" work.
+ // Insert '-' before and after "CTRL-X" when applicable.
+ if (*s < ' '
+ || (*s == '^' && s[1]
+ && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", s[1]) != NULL))) {
+ if ((char_u *)d > IObuff && d[-1] != '_' && d[-1] != '\\') {
+ *d++ = '_'; // prepend a '_' to make x_CTRL-x
+ }
+ STRCPY(d, "CTRL-");
+ d += 5;
+ if (*s < ' ') {
+ *d++ = (char)(*s + '@');
+ if (d[-1] == '\\') {
+ *d++ = '\\'; // double a backslash
+ }
+ } else {
+ *d++ = *++s;
+ }
+ if (s[1] != NUL && s[1] != '_') {
+ *d++ = '_'; // append a '_'
+ }
+ continue;
+ } else if (*s == '^') { // "^" or "CTRL-^" or "^_"
+ *d++ = '\\';
+ } else if (s[0] == '\\' && s[1] != '\\' && *arg == '/' && s == arg + 1) {
+ // Insert a backslash before a backslash after a slash, for search
+ // pattern tags: "/\|" --> "/\\|".
+ *d++ = '\\';
+ }
+
+ // "CTRL-\_" -> "CTRL-\\_" to avoid the special meaning of "\_" in
+ // "CTRL-\_CTRL-N"
+ if (STRNICMP(s, "CTRL-\\_", 7) == 0) {
+ STRCPY(d, "CTRL-\\\\");
+ d += 7;
+ s += 6;
+ }
+
+ *d++ = *s;
+
+ // If tag contains "({" or "([", tag terminates at the "(".
+ // This is for help on functions, e.g.: abs({expr}).
+ if (*s == '(' && (s[1] == '{' || s[1] == '[')) {
+ break;
+ }
+
+ // If tag starts with ', toss everything after a second '. Fixes
+ // CTRL-] on 'option'. (would include the trailing '.').
+ if (*s == '\'' && s > arg && *arg == '\'') {
+ break;
+ }
+ // Also '{' and '}'. Fixes CTRL-] on '{address}'.
+ if (*s == '}' && s > arg && *arg == '{') {
+ break;
+ }
+ }
+ *d = NUL;
+
+ if (*IObuff == '`') {
+ if ((char_u *)d > IObuff + 2 && d[-1] == '`') {
+ // remove the backticks from `command`
+ memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ d[-2] = NUL;
+ } else if ((char_u *)d > IObuff + 3 && d[-2] == '`' && d[-1] == ',') {
+ // remove the backticks and comma from `command`,
+ memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ d[-3] = NUL;
+ } else if ((char_u *)d > IObuff + 4 && d[-3] == '`'
+ && d[-2] == '\\' && d[-1] == '.') {
+ // remove the backticks and dot from `command`\.
+ memmove(IObuff, IObuff + 1, STRLEN(IObuff));
+ d[-4] = NUL;
+ }
+ }
+ }
+ }
+
+ *matches = NULL;
+ *num_matches = 0;
+ int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE | TAG_NO_TAGFUNC;
+ if (keep_lang) {
+ flags |= TAG_KEEP_LANG;
+ }
+ if (find_tags((char *)IObuff, num_matches, matches, flags, MAXCOL, NULL) == OK
+ && *num_matches > 0) {
+ // Sort the matches found on the heuristic number that is after the
+ // tag name.
+ qsort((void *)(*matches), (size_t)(*num_matches),
+ sizeof(char_u *), help_compare);
+ // Delete more than TAG_MANY to reduce the size of the listing.
+ while (*num_matches > TAG_MANY) {
+ xfree((*matches)[--*num_matches]);
+ }
+ }
+ return OK;
+}
+
+/// Cleanup matches for help tags:
+/// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
+/// tag matches it. Otherwise remove "@en" if "en" is the only language.
+void cleanup_help_tags(int num_file, char **file)
+{
+ char_u buf[4];
+ char_u *p = buf;
+
+ if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
+ *p++ = '@';
+ *p++ = p_hlg[0];
+ *p++ = p_hlg[1];
+ }
+ *p = NUL;
+
+ for (int i = 0; i < num_file; i++) {
+ int len = (int)STRLEN(file[i]) - 3;
+ if (len <= 0) {
+ continue;
+ }
+ if (STRCMP(file[i] + len, "@en") == 0) {
+ // Sorting on priority means the same item in another language may
+ // be anywhere. Search all items for a match up to the "@en".
+ int j;
+ for (j = 0; j < num_file; j++) {
+ if (j != i
+ && (int)STRLEN(file[j]) == len + 3
+ && STRNCMP(file[i], file[j], len + 1) == 0) {
+ break;
+ }
+ }
+ if (j == num_file) {
+ // item only exists with @en, remove it
+ file[i][len] = NUL;
+ }
+ }
+ }
+
+ if (*buf != NUL) {
+ for (int i = 0; i < num_file; i++) {
+ int len = (int)STRLEN(file[i]) - 3;
+ if (len <= 0) {
+ continue;
+ }
+ if (STRCMP(file[i] + len, buf) == 0) {
+ // remove the default language
+ file[i][len] = NUL;
+ }
+ }
+ }
+}
+
+/// Called when starting to edit a buffer for a help file.
+void prepare_help_buffer(void)
+{
+ curbuf->b_help = true;
+ set_string_option_direct("buftype", -1, "help", OPT_FREE|OPT_LOCAL, 0);
+
+ // Always set these options after jumping to a help tag, because the
+ // user may have an autocommand that gets in the way.
+ // Accept all ASCII chars for keywords, except ' ', '*', '"', '|', and
+ // latin1 word characters (for translated help files).
+ // Only set it when needed, buf_init_chartab() is some work.
+ char *p = "!-~,^*,^|,^\",192-255";
+ if (STRCMP(curbuf->b_p_isk, p) != 0) {
+ set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0);
+ check_buf_options(curbuf);
+ (void)buf_init_chartab(curbuf, false);
+ }
+
+ // Don't use the global foldmethod.
+ set_string_option_direct("fdm", -1, "manual", OPT_FREE|OPT_LOCAL, 0);
+
+ curbuf->b_p_ts = 8; // 'tabstop' is 8.
+ curwin->w_p_list = false; // No list mode.
+
+ curbuf->b_p_ma = false; // Not modifiable.
+ curbuf->b_p_bin = false; // Reset 'bin' before reading file.
+ curwin->w_p_nu = 0; // No line numbers.
+ curwin->w_p_rnu = 0; // No relative line numbers.
+ RESET_BINDING(curwin); // No scroll or cursor binding.
+ curwin->w_p_arab = false; // No arabic mode.
+ curwin->w_p_rl = false; // Help window is left-to-right.
+ curwin->w_p_fen = false; // No folding in the help window.
+ curwin->w_p_diff = false; // No 'diff'.
+ curwin->w_p_spell = false; // No spell checking.
+
+ set_buflisted(false);
+}
+
+/// After reading a help file: May cleanup a help buffer when syntax
+/// highlighting is not used.
+void fix_help_buffer(void)
+{
+ linenr_T lnum;
+ char *line;
+ bool in_example = false;
+
+ // Set filetype to "help".
+ if (STRCMP(curbuf->b_p_ft, "help") != 0) {
+ curbuf->b_ro_locked++;
+ set_option_value_give_err("ft", 0L, "help", OPT_LOCAL);
+ curbuf->b_ro_locked--;
+ }
+
+ if (!syntax_present(curwin)) {
+ for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
+ line = (char *)ml_get_buf(curbuf, lnum, false);
+ const size_t len = STRLEN(line);
+ if (in_example && len > 0 && !ascii_iswhite(line[0])) {
+ // End of example: non-white or '<' in first column.
+ if (line[0] == '<') {
+ // blank-out a '<' in the first column
+ line = (char *)ml_get_buf(curbuf, lnum, true);
+ line[0] = ' ';
+ }
+ in_example = false;
+ }
+ if (!in_example && len > 0) {
+ if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) {
+ // blank-out a '>' in the last column (start of example)
+ line = (char *)ml_get_buf(curbuf, lnum, true);
+ line[len - 1] = ' ';
+ in_example = true;
+ } else if (line[len - 1] == '~') {
+ // blank-out a '~' at the end of line (header marker)
+ line = (char *)ml_get_buf(curbuf, lnum, true);
+ line[len - 1] = ' ';
+ }
+ }
+ }
+ }
+
+ // In the "help.txt" and "help.abx" file, add the locally added help
+ // files. This uses the very first line in the help file.
+ char *const fname = path_tail(curbuf->b_fname);
+ if (FNAMECMP(fname, "help.txt") == 0
+ || (FNAMENCMP(fname, "help.", 5) == 0
+ && ASCII_ISALPHA(fname[5])
+ && ASCII_ISALPHA(fname[6])
+ && TOLOWER_ASC(fname[7]) == 'x'
+ && fname[8] == NUL)) {
+ for (lnum = 1; lnum < curbuf->b_ml.ml_line_count; lnum++) {
+ line = (char *)ml_get_buf(curbuf, lnum, false);
+ if (strstr(line, "*local-additions*") == NULL) {
+ continue;
+ }
+
+ // Go through all directories in 'runtimepath', skipping
+ // $VIMRUNTIME.
+ char *p = p_rtp;
+ while (*p != NUL) {
+ copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
+ char *const rt = vim_getenv("VIMRUNTIME");
+ if (rt != NULL
+ && path_full_compare(rt, (char *)NameBuff, false, true) != kEqualFiles) {
+ int fcount;
+ char **fnames;
+ char *s;
+ vimconv_T vc;
+ char *cp;
+
+ // Find all "doc/ *.txt" files in this directory.
+ if (!add_pathsep((char *)NameBuff)
+ || STRLCAT(NameBuff, "doc/*.??[tx]", // NOLINT
+ sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ continue;
+ }
+
+ // Note: We cannot just do `&NameBuff` because it is a statically sized array
+ // so `NameBuff == &NameBuff` according to C semantics.
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, buff_list, &fcount,
+ &fnames, EW_FILE|EW_SILENT) == OK
+ && fcount > 0) {
+ // If foo.abx is found use it instead of foo.txt in
+ // the same directory.
+ for (int i1 = 0; i1 < fcount; i1++) {
+ for (int i2 = 0; i2 < fcount; i2++) {
+ if (i1 == i2) {
+ continue;
+ }
+ if (fnames[i1] == NULL || fnames[i2] == NULL) {
+ continue;
+ }
+ const char *const f1 = fnames[i1];
+ const char *const f2 = fnames[i2];
+ const char *const t1 = path_tail(f1);
+ const char *const t2 = path_tail(f2);
+ const char *const e1 = strrchr(t1, '.');
+ const char *const e2 = strrchr(t2, '.');
+ if (e1 == NULL || e2 == NULL) {
+ continue;
+ }
+ if (FNAMECMP(e1, ".txt") != 0
+ && FNAMECMP(e1, fname + 4) != 0) {
+ // Not .txt and not .abx, remove it.
+ XFREE_CLEAR(fnames[i1]);
+ continue;
+ }
+ if (e1 - f1 != e2 - f2
+ || FNAMENCMP(f1, f2, e1 - f1) != 0) {
+ continue;
+ }
+ if (FNAMECMP(e1, ".txt") == 0
+ && FNAMECMP(e2, fname + 4) == 0) {
+ // use .abx instead of .txt
+ XFREE_CLEAR(fnames[i1]);
+ }
+ }
+ }
+ for (int fi = 0; fi < fcount; fi++) {
+ if (fnames[fi] == NULL) {
+ continue;
+ }
+
+ FILE *const fd = os_fopen(fnames[fi], "r");
+ if (fd == NULL) {
+ continue;
+ }
+ vim_fgets(IObuff, IOSIZE, fd);
+ if (IObuff[0] == '*'
+ && (s = vim_strchr((char *)IObuff + 1, '*'))
+ != NULL) {
+ TriState this_utf = kNone;
+ // Change tag definition to a
+ // reference and remove <CR>/<NL>.
+ IObuff[0] = '|';
+ *s = '|';
+ while (*s != NUL) {
+ if (*s == '\r' || *s == '\n') {
+ *s = NUL;
+ }
+ // The text is utf-8 when a byte
+ // above 127 is found and no
+ // illegal byte sequence is found.
+ if ((char_u)(*s) >= 0x80 && this_utf != kFalse) {
+ this_utf = kTrue;
+ const int l = utf_ptr2len(s);
+ if (l == 1) {
+ this_utf = kFalse;
+ }
+ s += l - 1;
+ }
+ s++;
+ }
+ // The help file is latin1 or utf-8;
+ // conversion to the current
+ // 'encoding' may be required.
+ vc.vc_type = CONV_NONE;
+ convert_setup(&vc,
+ (this_utf == kTrue ? "utf-8" : "latin1"),
+ p_enc);
+ if (vc.vc_type == CONV_NONE) {
+ // No conversion needed.
+ cp = (char *)IObuff;
+ } else {
+ // Do the conversion. If it fails
+ // use the unconverted text.
+ cp = string_convert(&vc, (char *)IObuff, NULL);
+ if (cp == NULL) {
+ cp = (char *)IObuff;
+ }
+ }
+ convert_setup(&vc, NULL, NULL);
+
+ ml_append(lnum, cp, (colnr_T)0, false);
+ if ((char_u *)cp != IObuff) {
+ xfree(cp);
+ }
+ lnum++;
+ }
+ fclose(fd);
+ }
+ FreeWild(fcount, fnames);
+ }
+ }
+ xfree(rt);
+ }
+ break;
+ }
+ }
+}
+
+/// ":exusage"
+void ex_exusage(exarg_T *eap)
+{
+ do_cmdline_cmd("help ex-cmd-index");
+}
+
+/// ":viusage"
+void ex_viusage(exarg_T *eap)
+{
+ do_cmdline_cmd("help normal-index");
+}
+
+/// Generate tags in one help directory
+///
+/// @param dir Path to the doc directory
+/// @param ext Suffix of the help files (".txt", ".itx", ".frx", etc.)
+/// @param tagname Name of the tags file ("tags" for English, "tags-fr" for
+/// French)
+/// @param add_help_tags Whether to add the "help-tags" tag
+/// @param ignore_writeerr ignore write error
+static void helptags_one(char *dir, const char *ext, const char *tagfname, bool add_help_tags,
+ bool ignore_writeerr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ garray_T ga;
+ int filecount;
+ char **files;
+ char *p1, *p2;
+ char *s;
+ TriState utf8 = kNone;
+ bool mix = false; // detected mixed encodings
+
+ // Find all *.txt files.
+ size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff));
+ if (dirlen >= MAXPATHL
+ || STRLCAT(NameBuff, "/**/*", sizeof(NameBuff)) >= MAXPATHL // NOLINT
+ || STRLCAT(NameBuff, ext, sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ return;
+ }
+
+ // Note: We cannot just do `&NameBuff` because it is a statically sized array
+ // so `NameBuff == &NameBuff` according to C semantics.
+ char *buff_list[1] = { (char *)NameBuff };
+ const int res = gen_expand_wildcards(1, buff_list, &filecount, &files,
+ EW_FILE|EW_SILENT);
+ if (res == FAIL || filecount == 0) {
+ if (!got_int) {
+ semsg(_("E151: No match: %s"), NameBuff);
+ }
+ if (res != FAIL) {
+ FreeWild(filecount, files);
+ }
+ return;
+ }
+
+ // Open the tags file for writing.
+ // Do this before scanning through all the files.
+ memcpy(NameBuff, dir, dirlen + 1);
+ if (!add_pathsep((char *)NameBuff)
+ || STRLCAT(NameBuff, tagfname, sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ return;
+ }
+
+ FILE *const fd_tags = os_fopen((char *)NameBuff, "w");
+ if (fd_tags == NULL) {
+ if (!ignore_writeerr) {
+ semsg(_("E152: Cannot open %s for writing"), NameBuff);
+ }
+ FreeWild(filecount, files);
+ return;
+ }
+
+ // If using the "++t" argument or generating tags for "$VIMRUNTIME/doc"
+ // add the "help-tags" tag.
+ ga_init(&ga, (int)sizeof(char_u *), 100);
+ if (add_help_tags
+ || path_full_compare("$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) {
+ size_t s_len = 18 + STRLEN(tagfname);
+ s = xmalloc(s_len);
+ snprintf(s, s_len, "help-tags\t%s\t1\n", tagfname);
+ GA_APPEND(char *, &ga, s);
+ }
+
+ // Go over all the files and extract the tags.
+ for (int fi = 0; fi < filecount && !got_int; fi++) {
+ FILE *const fd = os_fopen(files[fi], "r");
+ if (fd == NULL) {
+ semsg(_("E153: Unable to open %s for reading"), files[fi]);
+ continue;
+ }
+ const char *const fname = files[fi] + dirlen + 1;
+
+ bool firstline = true;
+ while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) {
+ if (firstline) {
+ // Detect utf-8 file by a non-ASCII char in the first line.
+ TriState this_utf8 = kNone;
+ for (s = (char *)IObuff; *s != NUL; s++) {
+ if ((char_u)(*s) >= 0x80) {
+ this_utf8 = kTrue;
+ const int l = utf_ptr2len(s);
+ if (l == 1) {
+ // Illegal UTF-8 byte sequence.
+ this_utf8 = kFalse;
+ break;
+ }
+ s += l - 1;
+ }
+ }
+ if (this_utf8 == kNone) { // only ASCII characters found
+ this_utf8 = kFalse;
+ }
+ if (utf8 == kNone) { // first file
+ utf8 = this_utf8;
+ } else if (utf8 != this_utf8) {
+ semsg(_("E670: Mix of help file encodings within a language: %s"),
+ files[fi]);
+ mix = !got_int;
+ got_int = true;
+ }
+ firstline = false;
+ }
+ p1 = vim_strchr((char *)IObuff, '*'); // find first '*'
+ while (p1 != NULL) {
+ p2 = strchr((const char *)p1 + 1, '*'); // Find second '*'.
+ if (p2 != NULL && p2 > p1 + 1) { // Skip "*" and "**".
+ for (s = p1 + 1; s < p2; s++) {
+ if (*s == ' ' || *s == '\t' || *s == '|') {
+ break;
+ }
+ }
+
+ // Only accept a *tag* when it consists of valid
+ // characters, there is white space before it and is
+ // followed by a white character or end-of-line.
+ if (s == p2
+ && ((char_u *)p1 == IObuff || p1[-1] == ' ' || p1[-1] == '\t')
+ && (vim_strchr(" \t\n\r", s[1]) != NULL
+ || s[1] == '\0')) {
+ *p2 = '\0';
+ p1++;
+ size_t s_len= (size_t)(p2 - p1) + STRLEN(fname) + 2;
+ s = xmalloc(s_len);
+ GA_APPEND(char *, &ga, s);
+ snprintf(s, s_len, "%s\t%s", p1, fname);
+
+ // find next '*'
+ p2 = vim_strchr(p2 + 1, '*');
+ }
+ }
+ p1 = p2;
+ }
+ line_breakcheck();
+ }
+
+ fclose(fd);
+ }
+
+ FreeWild(filecount, files);
+
+ if (!got_int && ga.ga_data != NULL) {
+ // Sort the tags.
+ sort_strings(ga.ga_data, ga.ga_len);
+
+ // Check for duplicates.
+ for (int i = 1; i < ga.ga_len; i++) {
+ p1 = ((char **)ga.ga_data)[i - 1];
+ p2 = ((char **)ga.ga_data)[i];
+ while (*p1 == *p2) {
+ if (*p2 == '\t') {
+ *p2 = NUL;
+ vim_snprintf((char *)NameBuff, MAXPATHL,
+ _("E154: Duplicate tag \"%s\" in file %s/%s"),
+ ((char_u **)ga.ga_data)[i], dir, p2 + 1);
+ emsg((char *)NameBuff);
+ *p2 = '\t';
+ break;
+ }
+ p1++;
+ p2++;
+ }
+ }
+
+ if (utf8 == kTrue) {
+ fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n");
+ }
+
+ // Write the tags into the file.
+ for (int i = 0; i < ga.ga_len; i++) {
+ s = ((char **)ga.ga_data)[i];
+ if (STRNCMP(s, "help-tags\t", 10) == 0) {
+ // help-tags entry was added in formatted form
+ fputs(s, fd_tags);
+ } else {
+ fprintf(fd_tags, "%s\t/" "*", s);
+ for (p1 = s; *p1 != '\t'; p1++) {
+ // insert backslash before '\\' and '/'
+ if (*p1 == '\\' || *p1 == '/') {
+ putc('\\', fd_tags);
+ }
+ putc(*p1, fd_tags);
+ }
+ fprintf(fd_tags, "*\n");
+ }
+ }
+ }
+ if (mix) {
+ got_int = false; // continue with other languages
+ }
+
+ GA_DEEP_CLEAR_PTR(&ga);
+ fclose(fd_tags); // there is no check for an error...
+}
+
+/// Generate tags in one help directory, taking care of translations.
+static void do_helptags(char *dirname, bool add_help_tags, bool ignore_writeerr)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int len;
+ garray_T ga;
+ char lang[2];
+ char ext[5];
+ char fname[8];
+ int filecount;
+ char **files;
+
+ // Get a list of all files in the help directory and in subdirectories.
+ STRLCPY(NameBuff, dirname, sizeof(NameBuff));
+ if (!add_pathsep((char *)NameBuff)
+ || STRLCAT(NameBuff, "**", sizeof(NameBuff)) >= MAXPATHL) {
+ emsg(_(e_fnametoolong));
+ return;
+ }
+
+ // Note: We cannot just do `&NameBuff` because it is a statically sized array
+ // so `NameBuff == &NameBuff` according to C semantics.
+ char *buff_list[1] = { (char *)NameBuff };
+ if (gen_expand_wildcards(1, buff_list, &filecount, &files,
+ EW_FILE|EW_SILENT) == FAIL
+ || filecount == 0) {
+ semsg(_("E151: No match: %s"), NameBuff);
+ return;
+ }
+
+ // Go over all files in the directory to find out what languages are
+ // present.
+ int j;
+ ga_init(&ga, 1, 10);
+ for (int i = 0; i < filecount; i++) {
+ len = (int)STRLEN(files[i]);
+ if (len <= 4) {
+ continue;
+ }
+
+ if (STRICMP(files[i] + len - 4, ".txt") == 0) {
+ // ".txt" -> language "en"
+ lang[0] = 'e';
+ lang[1] = 'n';
+ } else if (files[i][len - 4] == '.'
+ && ASCII_ISALPHA(files[i][len - 3])
+ && ASCII_ISALPHA(files[i][len - 2])
+ && TOLOWER_ASC(files[i][len - 1]) == 'x') {
+ // ".abx" -> language "ab"
+ lang[0] = (char)TOLOWER_ASC(files[i][len - 3]);
+ lang[1] = (char)TOLOWER_ASC(files[i][len - 2]);
+ } else {
+ continue;
+ }
+
+ // Did we find this language already?
+ for (j = 0; j < ga.ga_len; j += 2) {
+ if (STRNCMP(lang, ((char_u *)ga.ga_data) + j, 2) == 0) {
+ break;
+ }
+ }
+ if (j == ga.ga_len) {
+ // New language, add it.
+ ga_grow(&ga, 2);
+ ((char *)ga.ga_data)[ga.ga_len++] = lang[0];
+ ((char *)ga.ga_data)[ga.ga_len++] = lang[1];
+ }
+ }
+
+ // Loop over the found languages to generate a tags file for each one.
+ for (j = 0; j < ga.ga_len; j += 2) {
+ STRCPY(fname, "tags-xx");
+ fname[5] = ((char *)ga.ga_data)[j];
+ fname[6] = ((char *)ga.ga_data)[j + 1];
+ if (fname[5] == 'e' && fname[6] == 'n') {
+ // English is an exception: use ".txt" and "tags".
+ fname[4] = NUL;
+ STRCPY(ext, ".txt");
+ } else {
+ // Language "ab" uses ".abx" and "tags-ab".
+ STRCPY(ext, ".xxx");
+ ext[1] = fname[5];
+ ext[2] = fname[6];
+ }
+ helptags_one(dirname, (char *)ext, (char *)fname, add_help_tags, ignore_writeerr);
+ }
+
+ ga_clear(&ga);
+ FreeWild(filecount, files);
+}
+
+static void helptags_cb(char *fname, void *cookie)
+ FUNC_ATTR_NONNULL_ALL
+{
+ do_helptags(fname, *(bool *)cookie, true);
+}
+
+/// ":helptags"
+void ex_helptags(exarg_T *eap)
+{
+ expand_T xpc;
+ char *dirname;
+ bool add_help_tags = false;
+
+ // Check for ":helptags ++t {dir}".
+ if (STRNCMP(eap->arg, "++t", 3) == 0 && ascii_iswhite(eap->arg[3])) {
+ add_help_tags = true;
+ eap->arg = skipwhite(eap->arg + 3);
+ }
+
+ if (STRCMP(eap->arg, "ALL") == 0) {
+ do_in_path(p_rtp, "doc", DIP_ALL + DIP_DIR, helptags_cb, &add_help_tags);
+ } else {
+ ExpandInit(&xpc);
+ xpc.xp_context = EXPAND_DIRECTORIES;
+ dirname = (char *)ExpandOne(&xpc, (char_u *)eap->arg, NULL,
+ WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE);
+ if (dirname == NULL || !os_isdir(dirname)) {
+ semsg(_("E150: Not a directory: %s"), eap->arg);
+ } else {
+ do_helptags(dirname, add_help_tags, false);
+ }
+ xfree(dirname);
+ }
+}
diff --git a/src/nvim/help.h b/src/nvim/help.h
new file mode 100644
index 0000000000..21e11392ee
--- /dev/null
+++ b/src/nvim/help.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_HELP_H
+#define NVIM_HELP_H
+
+#include <stdbool.h>
+
+#include "nvim/ex_cmds_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "help.h.generated.h"
+#endif
+#endif // NVIM_HELP_H
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 71c7194479..a78b933108 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -6,6 +6,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/decoration_provider.h"
+#include "nvim/drawscreen.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -13,8 +14,7 @@
#include "nvim/map.h"
#include "nvim/message.h"
#include "nvim/option.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -32,7 +32,9 @@ static Map(int, int) blend_attr_entries = MAP_INIT;
static Map(int, int) blendthrough_attr_entries = MAP_INIT;
/// highlight entries private to a namespace
-static Map(ColorKey, ColorItem) ns_hl;
+static Map(ColorKey, ColorItem) ns_hls;
+typedef int NSHlAttr[HLF_COUNT + 1];
+static PMap(handle_T) ns_hl_attr;
void highlight_init(void)
{
@@ -147,42 +149,51 @@ int hl_get_syn_attr(int ns_id, int idx, HlAttrs at_en)
void ns_hl_def(NS ns_id, int hl_id, HlAttrs attrs, int link_id, Dict(highlight) *dict)
{
- if ((attrs.rgb_ae_attr & HL_DEFAULT)
- && map_has(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id))) {
- return;
- }
if (ns_id == 0) {
assert(dict);
// set in global (':highlight') namespace
set_hl_group(hl_id, attrs, dict, link_id);
return;
}
+ if ((attrs.rgb_ae_attr & HL_DEFAULT)
+ && map_has(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id))) {
+ return;
+ }
DecorProvider *p = get_decor_provider(ns_id, true);
int attr_id = link_id > 0 ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
ColorItem it = { .attr_id = attr_id,
.link_id = link_id,
.version = p->hl_valid,
- .is_default = (attrs.rgb_ae_attr & HL_DEFAULT) };
- map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
+ .is_default = (attrs.rgb_ae_attr & HL_DEFAULT),
+ .link_global = (attrs.rgb_ae_attr & HL_GLOBAL) };
+ map_put(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id), it);
+ p->hl_cached = false;
}
-int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
+int ns_get_hl(NS *ns_hl, int hl_id, bool link, bool nodefault)
{
static int recursive = 0;
- if (ns_id < 0) {
+ if (*ns_hl == 0) {
+ // ns=0 (the default namespace) does not have a provider so stop here
+ return -1;
+ }
+
+ if (*ns_hl < 0) {
if (ns_hl_active <= 0) {
return -1;
}
- ns_id = ns_hl_active;
+ *ns_hl = ns_hl_active;
}
+ int ns_id = *ns_hl;
+
DecorProvider *p = get_decor_provider(ns_id, true);
- ColorItem it = map_get(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id));
+ ColorItem it = map_get(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id));
// TODO(bfredl): map_ref true even this?
- bool valid_cache = it.version >= p->hl_valid;
+ bool valid_item = it.version >= p->hl_valid;
- if (!valid_cache && p->hl_def != LUA_NOREF && !recursive) {
+ if (!valid_item && p->hl_def != LUA_NOREF && !recursive) {
MAXSIZE_TEMP_ARRAY(args, 3);
ADD_C(args, INTEGER_OBJ((Integer)ns_id));
ADD_C(args, STRING_OBJ(cstr_to_string((char *)syn_id2name(hl_id))));
@@ -212,47 +223,79 @@ int ns_get_hl(NS ns_id, int hl_id, bool link, bool nodefault)
}
}
- it.attr_id = fallback ? -1 : hl_get_syn_attr((int)ns_id, hl_id, attrs);
+ it.attr_id = fallback ? -1 : hl_get_syn_attr(ns_id, hl_id, attrs);
it.version = p->hl_valid - tmp;
it.is_default = attrs.rgb_ae_attr & HL_DEFAULT;
- map_put(ColorKey, ColorItem)(&ns_hl, ColorKey(ns_id, hl_id), it);
+ it.link_global = attrs.rgb_ae_attr & HL_GLOBAL;
+ map_put(ColorKey, ColorItem)(&ns_hls, ColorKey(ns_id, hl_id), it);
+ valid_item = true;
}
- if (it.is_default && nodefault) {
+ if ((it.is_default && nodefault) || !valid_item) {
return -1;
}
if (link) {
- return it.attr_id >= 0 ? 0 : it.link_id;
+ if (it.attr_id >= 0) {
+ return 0;
+ } else {
+ if (it.link_global) {
+ *ns_hl = 0;
+ }
+ return it.link_id;
+ }
} else {
return it.attr_id;
}
}
-bool win_check_ns_hl(win_T *wp)
+bool hl_check_ns(void)
{
- if (ns_hl_changed) {
- highlight_changed();
- if (wp) {
- update_window_hl(wp, true);
+ int ns = 0;
+ if (ns_hl_fast > 0) {
+ ns = ns_hl_fast;
+ } else if (ns_hl_win >= 0) {
+ ns = ns_hl_win;
+ } else {
+ ns = ns_hl_global;
+ }
+ if (ns_hl_active == ns) {
+ return false;
+ }
+
+ ns_hl_active = ns;
+ hl_attr_active = highlight_attr;
+ if (ns > 0) {
+ update_ns_hl(ns);
+ NSHlAttr *hl_def = (NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns);
+ if (hl_def) {
+ hl_attr_active = *hl_def;
}
- ns_hl_changed = false;
- return true;
}
- return false;
+ need_highlight_changed = true;
+ return true;
+}
+
+/// prepare for drawing window `wp` or global elements if NULL
+///
+/// Note: pum should be drawn in the context of the current window!
+bool win_check_ns_hl(win_T *wp)
+{
+ ns_hl_win = wp ? wp->w_ns_hl : -1;
+ return hl_check_ns();
}
/// Get attribute code for a builtin highlight group.
///
/// The final syntax group could be modified by hi-link or 'winhighlight'.
-int hl_get_ui_attr(int idx, int final_id, bool optional)
+int hl_get_ui_attr(int ns_id, int idx, int final_id, bool optional)
{
HlAttrs attrs = HLATTRS_INIT;
bool available = false;
if (final_id > 0) {
- int syn_attr = syn_id2attr(final_id);
- if (syn_attr != 0) {
+ int syn_attr = syn_ns_id2attr(ns_id, final_id, optional);
+ if (syn_attr > 0) {
attrs = syn_attr2entry(syn_attr);
available = true;
}
@@ -265,8 +308,6 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
if (pum_drawn()) {
must_redraw_pum = true;
}
- } else if (idx == HLF_MSG) {
- msg_grid.blending = attrs.hl_blend > -1;
}
if (optional && !available) {
@@ -278,6 +319,21 @@ int hl_get_ui_attr(int idx, int final_id, bool optional)
void update_window_hl(win_T *wp, bool invalid)
{
+ int ns_id = wp->w_ns_hl;
+
+ update_ns_hl(ns_id);
+ if (ns_id != wp->w_ns_hl_active || wp->w_ns_hl_attr == NULL) {
+ wp->w_ns_hl_active = ns_id;
+
+ wp->w_ns_hl_attr = *(NSHlAttr *)pmap_get(handle_T)(&ns_hl_attr, ns_id);
+ if (!wp->w_ns_hl_attr) {
+ // No specific highlights, use the defaults.
+ wp->w_ns_hl_attr = highlight_attr;
+ }
+ }
+
+ int *hl_def = wp->w_ns_hl_attr;
+
if (!wp->w_hl_needs_update && !invalid) {
return;
}
@@ -285,34 +341,17 @@ void update_window_hl(win_T *wp, bool invalid)
// If a floating window is blending it always have a named
// wp->w_hl_attr_normal group. HL_ATTR(HLF_NFLOAT) is always named.
- bool has_blend = wp->w_floating && wp->w_p_winbl != 0;
// determine window specific background set in 'winhighlight'
bool float_win = wp->w_floating && !wp->w_float_config.external;
- if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] != 0) {
- wp->w_hl_attr_normal = hl_get_ui_attr(HLF_INACTIVE,
- wp->w_hl_ids[HLF_INACTIVE],
- !has_blend);
- } else if (float_win && wp->w_hl_ids[HLF_NFLOAT] != 0) {
- wp->w_hl_attr_normal = hl_get_ui_attr(HLF_NFLOAT,
- wp->w_hl_ids[HLF_NFLOAT], !has_blend);
- } else if (wp->w_hl_id_normal != 0) {
- wp->w_hl_attr_normal = hl_get_ui_attr(-1, wp->w_hl_id_normal, !has_blend);
+ if (float_win && hl_def[HLF_NFLOAT] != 0) {
+ wp->w_hl_attr_normal = hl_def[HLF_NFLOAT];
+ } else if (hl_def[HLF_COUNT] > 0) {
+ wp->w_hl_attr_normal = hl_def[HLF_COUNT];
} else {
wp->w_hl_attr_normal = float_win ? HL_ATTR(HLF_NFLOAT) : 0;
}
- // NOOOO! You cannot just pretend that "Normal" is just like any other
- // syntax group! It needs at least 10 layers of special casing! Noooooo!
- //
- // haha, theme engine go brrr
- int normality = syn_check_group(S_LEN("Normal"));
- int ns_attr = ns_get_hl(-1, normality, false, false);
- if (ns_attr > 0) {
- // TODO(bfredl): hantera NormalNC and so on
- wp->w_hl_attr_normal = ns_attr;
- }
-
// if blend= attribute is not set, 'winblend' value overrides it.
if (wp->w_floating && wp->w_p_winbl > 0) {
HlEntry entry = kv_A(attr_entries, wp->w_hl_attr_normal);
@@ -322,28 +361,13 @@ void update_window_hl(win_T *wp, bool invalid)
}
}
- if (wp != curwin && wp->w_hl_ids[HLF_INACTIVE] == 0) {
- wp->w_hl_attr_normal = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
- wp->w_hl_attr_normal);
- }
-
- for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
- int attr;
- if (wp->w_hl_ids[hlf] != 0) {
- attr = hl_get_ui_attr(hlf, wp->w_hl_ids[hlf], false);
- } else {
- attr = HL_ATTR(hlf);
- }
- wp->w_hl_attrs[hlf] = attr;
- }
-
wp->w_float_config.shadow = false;
if (wp->w_floating && wp->w_float_config.border) {
for (int i = 0; i < 8; i++) {
- int attr = wp->w_hl_attrs[HLF_BORDER];
+ int attr = hl_def[HLF_BORDER];
if (wp->w_float_config.border_hl_ids[i]) {
- attr = hl_get_ui_attr(HLF_BORDER, wp->w_float_config.border_hl_ids[i],
- false);
+ attr = hl_get_ui_attr(ns_id, HLF_BORDER,
+ wp->w_float_config.border_hl_ids[i], false);
HlAttrs a = syn_attr2entry(attr);
if (a.hl_blend) {
wp->w_float_config.shadow = true;
@@ -355,6 +379,65 @@ void update_window_hl(win_T *wp, bool invalid)
// shadow might cause blending
check_blending(wp);
+
+ // TODO(bfredl): this a bit ad-hoc. move it from highlight ns logic to 'winhl'
+ // implementation?
+ if (hl_def[HLF_INACTIVE] == 0) {
+ wp->w_hl_attr_normalnc = hl_combine_attr(HL_ATTR(HLF_INACTIVE),
+ wp->w_hl_attr_normal);
+ } else {
+ wp->w_hl_attr_normalnc = hl_def[HLF_INACTIVE];
+ }
+}
+
+void update_ns_hl(int ns_id)
+{
+ if (ns_id <= 0) {
+ return;
+ }
+ DecorProvider *p = get_decor_provider(ns_id, true);
+ if (p->hl_cached) {
+ return;
+ }
+
+ NSHlAttr **alloc = (NSHlAttr **)pmap_ref(handle_T)(&ns_hl_attr, ns_id, true);
+ if (*alloc == NULL) {
+ *alloc = xmalloc(sizeof(**alloc));
+ }
+ int *hl_attrs = **alloc;
+
+ for (int hlf = 0; hlf < HLF_COUNT; hlf++) {
+ int id = syn_check_group(hlf_names[hlf], STRLEN(hlf_names[hlf]));
+ bool optional = (hlf == HLF_INACTIVE || hlf == HLF_NFLOAT);
+ hl_attrs[hlf] = hl_get_ui_attr(ns_id, hlf, id, optional);
+ }
+
+ // NOOOO! You cannot just pretend that "Normal" is just like any other
+ // syntax group! It needs at least 10 layers of special casing! Noooooo!
+ //
+ // haha, tema engine go brrr
+ int normality = syn_check_group(S_LEN("Normal"));
+ hl_attrs[HLF_COUNT] = hl_get_ui_attr(ns_id, -1, normality, true);
+
+ // hl_get_ui_attr might have invalidated the decor provider
+ p = get_decor_provider(ns_id, true);
+ p->hl_cached = true;
+}
+
+int win_bg_attr(win_T *wp)
+{
+ if (ns_hl_fast < 0) {
+ int local = (wp == curwin) ? wp->w_hl_attr_normal : wp->w_hl_attr_normalnc;
+ if (local) {
+ return local;
+ }
+ }
+
+ if (wp == curwin || hl_attr_active[HLF_INACTIVE] == 0) {
+ return hl_attr_active[HLF_COUNT];
+ } else {
+ return hl_attr_active[HLF_INACTIVE];
+ }
}
/// Gets HL_UNDERLINE highlight.
@@ -403,7 +486,7 @@ void clear_hl_tables(bool reinit)
map_destroy(int, int)(&combine_attr_entries);
map_destroy(int, int)(&blend_attr_entries);
map_destroy(int, int)(&blendthrough_attr_entries);
- map_destroy(ColorKey, ColorItem)(&ns_hl);
+ map_destroy(ColorKey, ColorItem)(&ns_hls);
}
}
@@ -437,52 +520,52 @@ int hl_combine_attr(int char_attr, int prim_attr)
}
HlAttrs char_aep = syn_attr2entry(char_attr);
- HlAttrs spell_aep = syn_attr2entry(prim_attr);
+ HlAttrs prim_aep = syn_attr2entry(prim_attr);
// start with low-priority attribute, and override colors if present below.
HlAttrs new_en = char_aep;
- if (spell_aep.cterm_ae_attr & HL_NOCOMBINE) {
- new_en.cterm_ae_attr = spell_aep.cterm_ae_attr;
+ if (prim_aep.cterm_ae_attr & HL_NOCOMBINE) {
+ new_en.cterm_ae_attr = prim_aep.cterm_ae_attr;
} else {
- new_en.cterm_ae_attr |= spell_aep.cterm_ae_attr;
+ new_en.cterm_ae_attr |= prim_aep.cterm_ae_attr;
}
- if (spell_aep.rgb_ae_attr & HL_NOCOMBINE) {
- new_en.rgb_ae_attr = spell_aep.rgb_ae_attr;
+ if (prim_aep.rgb_ae_attr & HL_NOCOMBINE) {
+ new_en.rgb_ae_attr = prim_aep.rgb_ae_attr;
} else {
- new_en.rgb_ae_attr |= spell_aep.rgb_ae_attr;
+ new_en.rgb_ae_attr |= prim_aep.rgb_ae_attr;
}
- if (spell_aep.cterm_fg_color > 0) {
- new_en.cterm_fg_color = spell_aep.cterm_fg_color;
+ if (prim_aep.cterm_fg_color > 0) {
+ new_en.cterm_fg_color = prim_aep.cterm_fg_color;
new_en.rgb_ae_attr &= ((~HL_FG_INDEXED)
- | (spell_aep.rgb_ae_attr & HL_FG_INDEXED));
+ | (prim_aep.rgb_ae_attr & HL_FG_INDEXED));
}
- if (spell_aep.cterm_bg_color > 0) {
- new_en.cterm_bg_color = spell_aep.cterm_bg_color;
+ if (prim_aep.cterm_bg_color > 0) {
+ new_en.cterm_bg_color = prim_aep.cterm_bg_color;
new_en.rgb_ae_attr &= ((~HL_BG_INDEXED)
- | (spell_aep.rgb_ae_attr & HL_BG_INDEXED));
+ | (prim_aep.rgb_ae_attr & HL_BG_INDEXED));
}
- if (spell_aep.rgb_fg_color >= 0) {
- new_en.rgb_fg_color = spell_aep.rgb_fg_color;
+ if (prim_aep.rgb_fg_color >= 0) {
+ new_en.rgb_fg_color = prim_aep.rgb_fg_color;
new_en.rgb_ae_attr &= ((~HL_FG_INDEXED)
- | (spell_aep.rgb_ae_attr & HL_FG_INDEXED));
+ | (prim_aep.rgb_ae_attr & HL_FG_INDEXED));
}
- if (spell_aep.rgb_bg_color >= 0) {
- new_en.rgb_bg_color = spell_aep.rgb_bg_color;
+ if (prim_aep.rgb_bg_color >= 0) {
+ new_en.rgb_bg_color = prim_aep.rgb_bg_color;
new_en.rgb_ae_attr &= ((~HL_BG_INDEXED)
- | (spell_aep.rgb_ae_attr & HL_BG_INDEXED));
+ | (prim_aep.rgb_ae_attr & HL_BG_INDEXED));
}
- if (spell_aep.rgb_sp_color >= 0) {
- new_en.rgb_sp_color = spell_aep.rgb_sp_color;
+ if (prim_aep.rgb_sp_color >= 0) {
+ new_en.rgb_sp_color = prim_aep.rgb_sp_color;
}
- if (spell_aep.hl_blend >= 0) {
- new_en.hl_blend = spell_aep.hl_blend;
+ if (prim_aep.hl_blend >= 0) {
+ new_en.hl_blend = prim_aep.hl_blend;
}
id = get_attr_entry((HlEntry){ .attr = new_en, .kind = kHlCombine,
@@ -646,7 +729,7 @@ static int hl_cterm2rgb_color(int nr)
0x08, 0x12, 0x1C, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
0x80, 0x8A, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
};
- static char_u ansi_table[16][4] = {
+ static uint8_t ansi_table[16][4] = {
// R G B idx
{ 0, 0, 0, 1 }, // black
{ 224, 0, 0, 2 }, // dark red
@@ -820,7 +903,7 @@ Dictionary hlattrs2dict(Dictionary *hl_alloc, HlAttrs ae, bool use_rgb)
*hl_alloc = hl;
return hl;
} else {
- Dictionary allocated = copy_dictionary(hl);
+ Dictionary allocated = copy_dictionary(hl, NULL);
kv_destroy(hl);
return allocated;
}
@@ -852,7 +935,6 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
- CHECK_FLAG(dict, mask, global, , HL_GLOBAL);
if (HAS_KEY(dict->fg)) {
fg = object_to_color(dict->fg, "fg", true, err);
@@ -895,14 +977,21 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
return hlattrs;
}
- if (HAS_KEY(dict->link)) {
+ if (HAS_KEY(dict->link) || HAS_KEY(dict->global_link)) {
if (link_id) {
- *link_id = object_to_hl_id(dict->link, "link", err);
+ if (HAS_KEY(dict->global_link)) {
+ *link_id = object_to_hl_id(dict->global_link, "link", err);
+ mask |= HL_GLOBAL;
+ } else {
+ *link_id = object_to_hl_id(dict->link, "link", err);
+ }
+
if (ERROR_SET(err)) {
return hlattrs;
}
} else {
- api_set_error(err, kErrorTypeValidation, "Invalid Key: 'link'");
+ api_set_error(err, kErrorTypeValidation, "Invalid Key: '%s'",
+ HAS_KEY(dict->global_link) ? "global_link" : "link");
}
}
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index ae63f83d65..50299bb91c 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "nvim/api/private/defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/highlight_defs.h"
#include "nvim/ui.h"
@@ -11,6 +12,13 @@
# include "highlight.h.generated.h"
#endif
+static inline int win_hl_attr(win_T *wp, int hlf)
+{
+ // wp->w_ns_hl_attr might be null if we check highlights
+ // prior to entering redraw
+ return ((wp->w_ns_hl_attr && ns_hl_fast < 0) ? wp->w_ns_hl_attr : hl_attr_active)[hlf];
+}
+
#define HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp) \
do { \
bool dark_ = (*p_bg == 'd'); \
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index f41f980054..ffcb0f3f22 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -180,7 +180,7 @@ EXTERN const char *hlf_names[] INIT(= {
[HLF_CU] = "Cursor",
});
-EXTERN int highlight_attr[HLF_COUNT]; // Highl. attr for each context.
+EXTERN int highlight_attr[HLF_COUNT + 1]; // Highl. attr for each context.
EXTERN int highlight_attr_last[HLF_COUNT]; // copy for detecting changed groups
EXTERN int highlight_user[9]; // User[1-9] attributes
EXTERN int highlight_stlnc[9]; // On top of user
@@ -190,6 +190,13 @@ EXTERN RgbValue normal_fg INIT(= -1);
EXTERN RgbValue normal_bg INIT(= -1);
EXTERN RgbValue normal_sp INIT(= -1);
+EXTERN NS ns_hl_global INIT(= 0); // global highlight namespace
+EXTERN NS ns_hl_win INIT(= -1); // highlight namespace for the current window
+EXTERN NS ns_hl_fast INIT(= -1); // highlight namespace specified in a fast callback
+EXTERN NS ns_hl_active INIT(= 0); // currently active/cached namespace
+
+EXTERN int *hl_attr_active INIT(= highlight_attr);
+
typedef enum {
kHlUnknown,
kHlUI,
@@ -219,8 +226,15 @@ typedef struct {
int link_id;
int version;
bool is_default;
+ bool link_global;
} ColorItem;
-#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, \
- .version = -1, .is_default = false }
+#define COLOR_ITEM_INITIALIZER { .attr_id = -1, .link_id = -1, .version = -1, \
+ .is_default = false, .link_global = false }
+
+/// highlight attributes with associated priorities
+typedef struct {
+ int attr_id;
+ int priority;
+} HlPriAttr;
#endif // NVIM_HIGHLIGHT_DEFS_H
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index f6ec03fb14..ed1f0185b7 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -9,7 +9,10 @@
#include "nvim/autocmd.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
@@ -17,7 +20,6 @@
#include "nvim/match.h"
#include "nvim/option.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
/// \addtogroup SG_SET
/// @{
@@ -77,6 +79,8 @@ typedef struct {
int sg_rgb_sp_idx; ///< RGB special color index
int sg_blend; ///< blend level (0-100 inclusive), -1 if unset
+
+ int sg_parent; ///< parent of @nested.group
} HlGroup;
enum {
@@ -181,6 +185,54 @@ static const char *highlight_init_both[] = {
"default link DiagnosticSignWarn DiagnosticWarn",
"default link DiagnosticSignInfo DiagnosticInfo",
"default link DiagnosticSignHint DiagnosticHint",
+
+ "default link @error Error",
+ "default link @text.underline Underlined",
+ "default link @todo Todo",
+ "default link @debug Debug",
+
+ // Miscs
+ "default link @comment Comment",
+ "default link @punctuation Delimiter",
+
+ // Constants
+ "default link @constant Constant",
+ "default link @constant.builtin Special",
+ "default link @constant.macro Define",
+ "default link @define Define",
+ "default link @macro Macro",
+ "default link @string String",
+ "default link @string.escape SpecialChar",
+ "default link @character Character",
+ "default link @character.special SpecialChar",
+ "default link @number Number",
+ "default link @boolean Boolean",
+ "default link @float Float",
+
+ // Functions
+ "default link @function Function",
+ "default link @function.builtin Special",
+ "default link @function.macro Macro",
+ "default link @parameter Identifier",
+ "default link @method Function",
+ "default link @field Identifier",
+ "default link @property Identifier",
+ "default link @constructor Special",
+
+ // Keywords
+ "default link @conditional Conditional",
+ "default link @repeat Repeat",
+ "default link @label Label",
+ "default link @operator Operator",
+ "default link @keyword Keyword",
+ "default link @exception Exception",
+
+ "default link @type Type",
+ "default link @type.definition Typedef",
+ "default link @storageclass StorageClass",
+ "default link @structure Structure",
+ "default link @include Include",
+ "default link @preproc PreProc",
NULL
};
@@ -638,7 +690,7 @@ static int color_numbers_8[28] = { 0, 4, 2, 6,
// Lookup the "cterm" value to be used for color with index "idx" in
// color_names[].
-// "boldp" will be set to TRUE or FALSE for a foreground color when using 8
+// "boldp" will be set to kTrue or kFalse for a foreground color when using 8
// colors, otherwise it will be unchanged.
int lookup_color(const int idx, const bool foreground, TriState *const boldp)
{
@@ -689,14 +741,14 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_cleared = false;
g->sg_link = link_id;
g->sg_script_ctx = current_sctx;
- g->sg_script_ctx.sc_lnum += sourcing_lnum;
+ g->sg_script_ctx.sc_lnum += SOURCING_LNUM;
g->sg_set |= SG_LINK;
if (is_default) {
g->sg_deflink = link_id;
g->sg_deflink_sctx = current_sctx;
- g->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ g->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
}
- return;
+ goto update;
}
g->sg_cleared = false;
@@ -733,7 +785,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
g->sg_blend = attrs.hl_blend;
g->sg_script_ctx = current_sctx;
- g->sg_script_ctx.sc_lnum += sourcing_lnum;
+ g->sg_script_ctx.sc_lnum += SOURCING_LNUM;
g->sg_attr = hl_get_syn_attr(0, id, attrs);
@@ -751,6 +803,12 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id)
ui_mode_info_set();
}
}
+
+update:
+ if (!updating_screen) {
+ redraw_all_later(UPD_NOT_VALID);
+ }
+ need_highlight_changed = true;
}
/// Handle ":highlight" command
@@ -794,14 +852,14 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
// Isolate the name.
- name_end = (const char *)skiptowhite((const char_u *)line);
+ name_end = (const char *)skiptowhite(line);
linep = (const char *)skipwhite(name_end);
// Check for "default" argument.
if (strncmp(line, "default", (size_t)(name_end - line)) == 0) {
dodefault = true;
line = linep;
- name_end = (const char *)skiptowhite((const char_u *)line);
+ name_end = (const char *)skiptowhite(line);
linep = (const char *)skipwhite(name_end);
}
@@ -833,9 +891,9 @@ void do_highlight(const char *line, const bool forceit, const bool init)
int to_id;
HlGroup *hlgroup = NULL;
- from_end = (const char *)skiptowhite((const char_u *)from_start);
+ from_end = (const char *)skiptowhite(from_start);
to_start = (const char *)skipwhite(from_end);
- to_end = (const char *)skiptowhite((const char_u *)to_start);
+ to_end = (const char *)skiptowhite(to_start);
if (ends_excmd((uint8_t)(*from_start))
|| ends_excmd((uint8_t)(*to_start))) {
@@ -861,7 +919,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (dodefault && (forceit || hlgroup->sg_deflink == 0)) {
hlgroup->sg_deflink = to_id;
hlgroup->sg_deflink_sctx = current_sctx;
- hlgroup->sg_deflink_sctx.sc_lnum += sourcing_lnum;
+ hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hlgroup->sg_deflink_sctx);
}
}
@@ -871,7 +929,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// for the group, unless '!' is used
if (to_id > 0 && !forceit && !init
&& hl_has_settings(from_id - 1, dodefault)) {
- if (sourcing_name == NULL && !dodefault) {
+ if (SOURCING_NAME == NULL && !dodefault) {
emsg(_("E414: group has settings, highlight link ignored"));
}
} else if (hlgroup->sg_link != to_id
@@ -882,10 +940,10 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
hlgroup->sg_link = to_id;
hlgroup->sg_script_ctx = current_sctx;
- hlgroup->sg_script_ctx.sc_lnum += sourcing_lnum;
+ hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hlgroup->sg_script_ctx);
hlgroup->sg_cleared = false;
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
// Only call highlight changed() once after multiple changes
need_highlight_changed = true;
@@ -908,10 +966,10 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
init_highlight(true, true);
highlight_changed();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
return;
}
- name_end = (const char *)skiptowhite((const char_u *)line);
+ name_end = (const char *)skiptowhite(line);
linep = (const char *)skipwhite(name_end);
}
@@ -996,7 +1054,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
}
} else {
arg_start = linep;
- linep = (const char *)skiptowhite((const char_u *)linep);
+ linep = (const char *)skiptowhite(linep);
}
if (linep == arg_start) {
semsg(_("E417: missing argument: %s"), key_start);
@@ -1148,7 +1206,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (dark != -1
&& dark != (*p_bg == 'd')
&& !option_was_set("bg")) {
- set_option_value("bg", 0L, (dark ? "dark" : "light"), 0);
+ set_option_value_give_err("bg", 0L, (dark ? "dark" : "light"), 0);
reset_option_was_set("bg");
}
}
@@ -1262,17 +1320,17 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// changed
ui_refresh();
} else {
- // TUI and newer UIs will repaint the screen themselves. NOT_VALID
+ // TUI and newer UIs will repaint the screen themselves. UPD_NOT_VALID
// redraw below will still handle usages of guibg=fg etc.
ui_default_colors_set();
}
did_highlight_changed = true;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
} else {
set_hl_attr(idx);
}
hl_table[idx].sg_script_ctx = current_sctx;
- hl_table[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
+ hl_table[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&hl_table[idx].sg_script_ctx);
// Only call highlight_changed() once, after a sequence of highlight
@@ -1284,7 +1342,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
// redrawing. This may happen when evaluating 'statusline' changes the
// StatusLine group.
if (!updating_screen) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
need_highlight_changed = true;
}
@@ -1295,7 +1353,7 @@ void free_highlight(void)
{
ga_clear(&highlight_ga);
map_destroy(cstr_t, int)(&highlight_unames);
- arena_mem_free(arena_finish(&highlight_arena), NULL);
+ arena_mem_free(arena_finish(&highlight_arena));
}
#endif
@@ -1313,7 +1371,7 @@ void restore_cterm_colors(void)
/// @param check_link if true also check for an existing link.
///
-/// @return TRUE if highlight group "idx" has any settings.
+/// @return true if highlight group "idx" has any settings.
static int hl_has_settings(int idx, bool check_link)
{
return hl_table[idx].sg_cleared == 0
@@ -1363,7 +1421,12 @@ static void highlight_list_one(const int id)
const HlGroup *sgp = &hl_table[id - 1]; // index is ID minus one
bool didh = false;
- if (message_filtered(sgp->sg_name)) {
+ if (message_filtered((char *)sgp->sg_name)) {
+ return;
+ }
+
+ // don't list specialized groups if a parent is used instead
+ if (sgp->sg_parent && sgp->sg_cleared) {
return;
}
@@ -1653,7 +1716,12 @@ static void set_hl_attr(int idx)
int syn_name2id(const char *name)
FUNC_ATTR_NONNULL_ALL
{
- return syn_name2id_len(name, STRLEN(name));
+ if (name[0] == '@') {
+ // if we look up @aaa.bbb, we have to consider @aaa as well
+ return syn_check_group(name, strlen(name));
+ } else {
+ return syn_name2id_len(name, STRLEN(name));
+ }
}
/// Lookup a highlight group name and return its ID.
@@ -1693,7 +1761,7 @@ int syn_name2attr(const char_u *name)
return 0;
}
-/// Return TRUE if highlight group "name" exists.
+/// Return true if highlight group "name" exists.
int highlight_exists(const char *name)
{
return syn_name2id(name) > 0;
@@ -1742,11 +1810,19 @@ static int syn_add_group(const char *name, size_t len)
if (!vim_isprintc(c)) {
emsg(_("E669: Unprintable character in group name"));
return 0;
- } else if (!ASCII_ISALNUM(c) && c != '_') {
- // This is an error, but since there previously was no check only give a warning.
+ } else if (!ASCII_ISALNUM(c) && c != '_' && c != '.' && c != '@') {
+ // '.' and '@' are allowed characters for use with treesitter capture names.
msg_source(HL_ATTR(HLF_W));
- msg(_("W18: Invalid character in group name"));
- break;
+ emsg(_(e_highlight_group_name_invalid_char));
+ return 0;
+ }
+ }
+
+ int scoped_parent = 0;
+ if (len > 1 && name[0] == '@') {
+ char *delim = xmemrchr(name, '.', len);
+ if (delim) {
+ scoped_parent = syn_check_group(name, (size_t)(delim - name));
}
}
@@ -1765,7 +1841,7 @@ static int syn_add_group(const char *name, size_t len)
// Append another syntax_highlight entry.
HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga);
- memset(hlgp, 0, sizeof(*hlgp));
+ CLEAR_POINTER(hlgp);
hlgp->sg_name = (char_u *)arena_memdupz(&highlight_arena, name, len);
hlgp->sg_rgb_bg = -1;
hlgp->sg_rgb_fg = -1;
@@ -1775,6 +1851,9 @@ static int syn_add_group(const char *name, size_t len)
hlgp->sg_rgb_sp_idx = kColorIdxNone;
hlgp->sg_blend = -1;
hlgp->sg_name_u = arena_memdupz(&highlight_arena, name, len);
+ hlgp->sg_parent = scoped_parent;
+ // will get set to false by caller if settings are added
+ hlgp->sg_cleared = true;
vim_strup((char_u *)hlgp->sg_name_u);
int id = highlight_ga.ga_len; // ID is index plus one
@@ -1788,11 +1867,18 @@ static int syn_add_group(const char *name, size_t len)
/// @see syn_attr2entry
int syn_id2attr(int hl_id)
{
- hl_id = syn_get_final_id(hl_id);
+ return syn_ns_id2attr(-1, hl_id, false);
+}
+
+int syn_ns_id2attr(int ns_id, int hl_id, bool optional)
+{
+ hl_id = syn_ns_get_final_id(&ns_id, hl_id);
HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one
- int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set);
- if (attr >= 0) {
+ int attr = ns_get_hl(&ns_id, hl_id, false, sgp->sg_set);
+
+ // if a highlight group is optional, don't use the global value
+ if (attr >= 0 || (optional && ns_id > 0)) {
return attr;
}
return sgp->sg_attr;
@@ -1801,10 +1887,16 @@ int syn_id2attr(int hl_id)
/// Translate a group ID to the final group ID (following links).
int syn_get_final_id(int hl_id)
{
+ int id = curwin->w_ns_hl_active;
+ return syn_ns_get_final_id(&id, hl_id);
+}
+
+int syn_ns_get_final_id(int *ns_id, int hl_id)
+{
int count;
if (hl_id > highlight_ga.ga_len || hl_id < 1) {
- return 0; // Can be called from eval!!
+ return 0; // Can be called from eval!!
}
// Follow links until there is no more.
@@ -1812,10 +1904,10 @@ int syn_get_final_id(int hl_id)
for (count = 100; --count >= 0;) {
HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one
- // ACHTUNG: when using "tmp" attribute (no link) the function might be
+ // TODO(bfredl): when using "tmp" attribute (no link) the function might be
// called twice. it needs be smart enough to remember attr only to
// syn_id2attr time
- int check = ns_get_hl(-1, hl_id, true, sgp->sg_set);
+ int check = ns_get_hl(ns_id, hl_id, true, sgp->sg_set);
if (check == 0) {
return hl_id; // how dare! it broke the link!
} else if (check > 0) {
@@ -1823,10 +1915,13 @@ int syn_get_final_id(int hl_id)
continue;
}
- if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len) {
+ if (sgp->sg_link > 0 && sgp->sg_link <= highlight_ga.ga_len) {
+ hl_id = sgp->sg_link;
+ } else if (sgp->sg_cleared && sgp->sg_parent > 0) {
+ hl_id = sgp->sg_parent;
+ } else {
break;
}
- hl_id = sgp->sg_link;
}
return hl_id;
@@ -1863,7 +1958,7 @@ static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int
HlGroup *const hlt = hl_table;
if (id_alt == 0) {
- memset(&hlt[hlcnt + i], 0, sizeof(HlGroup));
+ CLEAR_POINTER(&hlt[hlcnt + i]);
hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
} else {
@@ -1913,19 +2008,22 @@ void highlight_changed(void)
if (id == 0) {
abort();
}
- int final_id = syn_get_final_id(id);
+ int ns_id = -1;
+ int final_id = syn_ns_get_final_id(&ns_id, id);
if (hlf == HLF_SNC) {
id_SNC = final_id;
} else if (hlf == HLF_S) {
id_S = final_id;
}
- highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id,
+ highlight_attr[hlf] = hl_get_ui_attr(ns_id, hlf, final_id,
(hlf == HLF_INACTIVE || hlf == HLF_LC));
if (highlight_attr[hlf] != highlight_attr_last[hlf]) {
if (hlf == HLF_MSG) {
clear_cmdline = true;
+ HlAttrs attrs = syn_attr2entry(highlight_attr[hlf]);
+ msg_grid.blending = attrs.hl_blend > -1;
}
ui_call_hl_group_set(cstr_as_string((char *)hlf_names[hlf]),
highlight_attr[hlf]);
@@ -1933,6 +2031,9 @@ void highlight_changed(void)
}
}
+ // sentinel value. used when no hightlight namespace is active
+ highlight_attr[HLF_COUNT] = 0;
+
//
// Setup the user highlights
//
@@ -1945,7 +2046,7 @@ void highlight_changed(void)
hlcnt = highlight_ga.ga_len;
if (id_S == -1) {
// Make sure id_S is always valid to simplify code below. Use the last entry
- memset(&hl_table[hlcnt + 9], 0, sizeof(HlGroup));
+ CLEAR_POINTER(&hl_table[hlcnt + 9]);
id_S = hlcnt + 10;
}
for (int i = 0; i < 9; i++) {
@@ -1973,13 +2074,13 @@ void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
// (part of) subcommand already typed
if (*arg != NUL) {
- const char *p = (const char *)skiptowhite((const char_u *)arg);
+ const char *p = (const char *)skiptowhite(arg);
if (*p != NUL) { // Past "default" or group name.
include_default = 0;
if (strncmp("default", arg, (unsigned)(p - arg)) == 0) {
arg = (const char *)skipwhite(p);
xp->xp_pattern = (char *)arg;
- p = (const char *)skiptowhite((const char_u *)arg);
+ p = (const char *)skiptowhite(arg);
}
if (*p != NUL) { // past group name
include_link = 0;
@@ -1989,10 +2090,10 @@ void set_context_in_highlight_cmd(expand_T *xp, const char *arg)
if (strncmp("link", arg, (unsigned)(p - arg)) == 0
|| strncmp("clear", arg, (unsigned)(p - arg)) == 0) {
xp->xp_pattern = skipwhite(p);
- p = (const char *)skiptowhite((char_u *)xp->xp_pattern);
+ p = (const char *)skiptowhite(xp->xp_pattern);
if (*p != NUL) { // Past first group name.
xp->xp_pattern = skipwhite(p);
- p = (const char *)skiptowhite((char_u *)xp->xp_pattern);
+ p = (const char *)skiptowhite(xp->xp_pattern);
}
}
if (*p != NUL) { // Past group name(s).
diff --git a/src/nvim/highlight_group.h b/src/nvim/highlight_group.h
index 1474588889..bf6bad1a86 100644
--- a/src/nvim/highlight_group.h
+++ b/src/nvim/highlight_group.h
@@ -1,7 +1,8 @@
#ifndef NVIM_HIGHLIGHT_GROUP_H
#define NVIM_HIGHLIGHT_GROUP_H
-#include "nvim/eval.h"
+#include "nvim/api/private/helpers.h"
+#include "nvim/highlight_defs.h"
#include "nvim/types.h"
#define MAX_HL_ID 20000 // maximum value for a highlight ID.
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index 8d08c2fc19..9413abb2e1 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -18,9 +18,12 @@
#include <sys/types.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/eval.h"
#include "nvim/event/stream.h"
+#include "nvim/ex_eval.h"
#include "nvim/fileio.h"
#include "nvim/if_cscope.h"
#include "nvim/memory.h"
@@ -150,10 +153,10 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx)
// (part of) subcommand already typed
if (*arg != NUL) {
- const char *p = (const char *)skiptowhite((const char_u *)arg);
+ const char *p = (const char *)skiptowhite(arg);
if (*p != NUL) { // Past first word.
xp->xp_pattern = skipwhite(p);
- if (*skiptowhite((char_u *)xp->xp_pattern) != NUL) {
+ if (*skiptowhite(xp->xp_pattern) != NUL) {
xp->xp_context = EXPAND_NOTHING;
} else if (STRNICMP(arg, "add", p - arg) == 0) {
xp->xp_context = EXPAND_FILES;
@@ -200,19 +203,19 @@ static void do_cscope_general(exarg_T *eap, int make_split)
/// Implementation of ":cscope" and ":lcscope"
void ex_cscope(exarg_T *eap)
{
- do_cscope_general(eap, FALSE);
+ do_cscope_general(eap, false);
}
/// Implementation of ":scscope". Same as ex_cscope(), but splits window, too.
void ex_scscope(exarg_T *eap)
{
- do_cscope_general(eap, TRUE);
+ do_cscope_general(eap, true);
}
/// Implementation of ":cstag"
void ex_cstag(exarg_T *eap)
{
- int ret = FALSE;
+ int ret = false;
if (*eap->arg == NUL) {
(void)emsg(_("E562: Usage: cstag <ident>"));
@@ -275,7 +278,7 @@ void ex_cstag(exarg_T *eap)
/// This simulates a vim_fgets(), but for cscope, returns the next line
/// from the cscope output. should only be called from find_tags()
///
-/// @return true if eof, FALSE otherwise
+/// @return true if eof, false otherwise
bool cs_fgets(char_u *buf, int size)
FUNC_ATTR_NONNULL_ALL
{
@@ -415,16 +418,15 @@ static int cs_add_common(char *arg1, char *arg2, char *flags)
char *fname2 = NULL;
char *ppath = NULL;
size_t usedlen = 0;
- char_u *fbuf = NULL;
+ char *fbuf = NULL;
// get the filename (arg1), expand it, and try to stat it
fname = xmalloc(MAXPATHL + 1);
- expand_env((char_u *)arg1, (char_u *)fname, MAXPATHL);
+ expand_env(arg1, fname, MAXPATHL);
size_t len = STRLEN(fname);
- fbuf = (char_u *)fname;
- (void)modify_fname(":p", false, &usedlen,
- &fname, (char **)&fbuf, &len);
+ fbuf = fname;
+ (void)modify_fname(":p", false, &usedlen, &fname, &fbuf, &len);
if (fname == NULL) {
goto add_err;
}
@@ -443,7 +445,7 @@ staterr:
// get the prepend path (arg2), expand it, and see if it exists
if (arg2 != NULL) {
ppath = xmalloc(MAXPATHL + 1);
- expand_env((char_u *)arg2, (char_u *)ppath, MAXPATHL);
+ expand_env(arg2, ppath, MAXPATHL);
if (!os_path_exists((char_u *)ppath)) {
goto staterr;
}
@@ -661,7 +663,7 @@ static char *cs_create_cmd(char *csoption, char *pattern)
pat = pattern;
if (search != 4 && search != 6) {
while (ascii_iswhite(*pat)) {
- ++pat;
+ pat++;
}
}
@@ -755,14 +757,14 @@ err_closing:
#endif
// expand the cscope exec for env var's
prog = xmalloc(MAXPATHL + 1);
- expand_env(p_csprg, (char_u *)prog, MAXPATHL);
+ expand_env(p_csprg, prog, MAXPATHL);
// alloc space to hold the cscope command
size_t len = strlen(prog) + strlen(csinfo[i].fname) + 32;
if (csinfo[i].ppath) {
// expand the prepend path for env var's
ppath = xmalloc(MAXPATHL + 1);
- expand_env((char_u *)csinfo[i].ppath, (char_u *)ppath, MAXPATHL);
+ expand_env(csinfo[i].ppath, ppath, MAXPATHL);
len += strlen(ppath);
}
@@ -837,7 +839,7 @@ err_closing:
si.hStdOutput = stdout_wr;
si.hStdError = stdout_wr;
si.hStdInput = stdin_rd;
- created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE,
+ created = CreateProcess(NULL, cmd, NULL, NULL, true, CREATE_NEW_CONSOLE,
NULL, NULL, &si, &pi);
xfree(prog);
xfree(cmd);
@@ -873,7 +875,7 @@ err_closing:
/// Query cscope using command line interface. Parse the output and use tselect
/// to allow choices. Like Nvi, creates a pipe to send to/from query/cscope.
///
-/// @return TRUE if we jump to a tag or abort, FALSE if not.
+/// @return true if we jump to a tag or abort, false if not.
static int cs_find(exarg_T *eap)
{
char *opt, *pat;
@@ -898,7 +900,7 @@ static int cs_find(exarg_T *eap)
* Let's replace the NULs written by strtok() with spaces - we need the
* spaces to correctly display the quickfix/location list window's title.
*/
- for (int i = 0; i < eap_arg_len; ++i) {
+ for (int i = 0; i < eap_arg_len; i++) {
if (NUL == eap->arg[i]) {
eap->arg[i] = ' ';
}
@@ -951,7 +953,7 @@ static bool cs_find_common(char *opt, char *pat, int forceit, int verbose, bool
cmdletter = opt[0];
}
- qfpos = vim_strchr((char *)p_csqf, cmdletter);
+ qfpos = vim_strchr(p_csqf, cmdletter);
if (qfpos != NULL) {
qfpos++;
// next symbol must be + or -
@@ -1104,7 +1106,7 @@ static int cs_help(exarg_T *eap)
cmdp++;
}
- wait_return(TRUE);
+ wait_return(true);
return CSCOPE_SUCCESS;
}
@@ -1278,7 +1280,7 @@ static void cs_kill_execute(size_t i, char *cname)
(void)smsg_attr(HL_ATTR(HLF_R) | MSG_HIST,
_("cscope connection %s closed"), cname);
}
- cs_release_csp(i, TRUE);
+ cs_release_csp(i, true);
}
/// Convert the cscope output into a ctags style entry (as might be found
@@ -1836,7 +1838,7 @@ static void cs_release_csp(size_t i, bool freefnpp)
// Can't use sigaction(), loop for two seconds. First yield the CPU
// to give cscope a chance to exit quickly.
sleep(0);
- for (waited = 0; waited < 40; ++waited) {
+ for (waited = 0; waited < 40; waited++) {
pid = waitpid(csinfo[i].pid, &pstat, WNOHANG);
waitpid_errno = errno;
if (pid != 0) {
@@ -1930,7 +1932,7 @@ static int cs_reset(exarg_T *eap)
pplist[i] = csinfo[i].ppath;
fllist[i] = csinfo[i].flags;
if (csinfo[i].fname != NULL) {
- cs_release_csp(i, FALSE);
+ cs_release_csp(i, false);
}
}
@@ -2033,7 +2035,7 @@ static int cs_show(exarg_T *eap)
}
}
- wait_return(TRUE);
+ wait_return(false);
return CSCOPE_SUCCESS;
}
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 271498d41a..792f4d93c4 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -11,6 +11,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/extmark.h"
#include "nvim/indent.h"
@@ -24,16 +25,325 @@
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
+#include "nvim/textformat.h"
#include "nvim/undo.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent.c.generated.h"
#endif
+/// Set the integer values corresponding to the string setting of 'vartabstop'.
+/// "array" will be set, caller must free it if needed.
+///
+/// @return false for an error.
+bool tabstop_set(char *var, long **array)
+{
+ long valcount = 1;
+ int t;
+ char *cp;
+
+ if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
+ *array = NULL;
+ return true;
+ }
+
+ for (cp = var; *cp != NUL; cp++) {
+ if (cp == var || cp[-1] == ',') {
+ char *end;
+
+ if (strtol(cp, &end, 10) <= 0) {
+ if (cp != end) {
+ emsg(_(e_positive));
+ } else {
+ semsg(_(e_invarg2), cp);
+ }
+ return false;
+ }
+ }
+
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
+ valcount++;
+ continue;
+ }
+ semsg(_(e_invarg2), var);
+ return false;
+ }
+
+ *array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
+ (*array)[0] = valcount;
+
+ t = 1;
+ for (cp = var; *cp != NUL;) {
+ int n = atoi(cp);
+
+ // Catch negative values, overflow and ridiculous big values.
+ if (n <= 0 || n > TABSTOP_MAX) {
+ semsg(_(e_invarg2), cp);
+ XFREE_CLEAR(*array);
+ return false;
+ }
+ (*array)[t++] = n;
+ while (*cp != NUL && *cp != ',') {
+ cp++;
+ }
+ if (*cp != NUL) {
+ cp++;
+ }
+ }
+
+ return true;
+}
+
+/// Calculate the number of screen spaces a tab will occupy.
+/// If "vts" is set then the tab widths are taken from that array,
+/// otherwise the value of ts is used.
+int tabstop_padding(colnr_T col, long ts_arg, long *vts)
+{
+ long ts = ts_arg == 0 ? 8 : ts_arg;
+ colnr_T tabcol = 0;
+ int t;
+ long padding = 0;
+
+ if (vts == NULL || vts[0] == 0) {
+ return (int)(ts - (col % ts));
+ }
+
+ const long tabcount = vts[0];
+
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > col) {
+ padding = tabcol - col;
+ break;
+ }
+ }
+ if (t > tabcount) {
+ padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
+ }
+
+ return (int)padding;
+}
+
+/// Find the size of the tab that covers a particular column.
+int tabstop_at(colnr_T col, long ts, long *vts)
+{
+ colnr_T tabcol = 0;
+ int t;
+ long tab_size = 0;
+
+ if (vts == NULL || vts[0] == 0) {
+ return (int)ts;
+ }
+
+ const long tabcount = vts[0];
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > col) {
+ tab_size = vts[t];
+ break;
+ }
+ }
+ if (t > tabcount) {
+ tab_size = vts[tabcount];
+ }
+
+ return (int)tab_size;
+}
+
+/// Find the column on which a tab starts.
+colnr_T tabstop_start(colnr_T col, long ts, long *vts)
+{
+ colnr_T tabcol = 0;
+ int t;
+
+ if (vts == NULL || vts[0] == 0) {
+ return (int)((col / ts) * ts);
+ }
+
+ const long tabcount = vts[0];
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > col) {
+ return (int)(tabcol - vts[t]);
+ }
+ }
+
+ const int excess = (int)(tabcol % vts[tabcount]);
+ return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
+}
+
+/// Find the number of tabs and spaces necessary to get from one column
+/// to another.
+void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
+ int *nspcs)
+{
+ int spaces = end_col - start_col;
+ colnr_T tabcol = 0;
+ long padding = 0;
+ int t;
+ long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
+
+ if (vts == NULL || vts[0] == 0) {
+ int tabs = 0;
+
+ const int initspc = (int)(ts - (start_col % ts));
+ if (spaces >= initspc) {
+ spaces -= initspc;
+ tabs++;
+ }
+ tabs += (int)(spaces / ts);
+ spaces -= (int)((spaces / ts) * ts);
+
+ *ntabs = tabs;
+ *nspcs = spaces;
+ return;
+ }
+
+ // Find the padding needed to reach the next tabstop.
+ const long tabcount = vts[0];
+ for (t = 1; t <= tabcount; t++) {
+ tabcol += (colnr_T)vts[t];
+ if (tabcol > start_col) {
+ padding = tabcol - start_col;
+ break;
+ }
+ }
+ if (t > tabcount) {
+ padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
+ }
+
+ // If the space needed is less than the padding no tabs can be used.
+ if (spaces < padding) {
+ *ntabs = 0;
+ *nspcs = spaces;
+ return;
+ }
+
+ *ntabs = 1;
+ spaces -= (int)padding;
+
+ // At least one tab has been used. See if any more will fit.
+ while (spaces != 0 && ++t <= tabcount) {
+ padding = vts[t];
+ if (spaces < padding) {
+ *nspcs = spaces;
+ return;
+ }
+ *ntabs += 1;
+ spaces -= (int)padding;
+ }
+
+ *ntabs += spaces / (int)vts[tabcount];
+ *nspcs = spaces % (int)vts[tabcount];
+}
+
+/// See if two tabstop arrays contain the same values.
+bool tabstop_eq(long *ts1, long *ts2)
+{
+ int t;
+
+ if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
+ return false;
+ }
+ if (ts1 == ts2) {
+ return true;
+ }
+ if (ts1[0] != ts2[0]) {
+ return false;
+ }
+
+ for (t = 1; t <= ts1[0]; t++) {
+ if (ts1[t] != ts2[t]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/// Copy a tabstop array, allocating space for the new array.
+int *tabstop_copy(long *oldts)
+{
+ long *newts;
+ int t;
+
+ if (oldts == 0) {
+ return 0;
+ }
+
+ newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
+ for (t = 0; t <= oldts[0]; t++) {
+ newts[t] = oldts[t];
+ }
+
+ return (int *)newts;
+}
+
+/// Return a count of the number of tabstops.
+int tabstop_count(long *ts)
+{
+ return ts != NULL ? (int)ts[0] : 0;
+}
+
+/// Return the first tabstop, or 8 if there are no tabstops defined.
+int tabstop_first(long *ts)
+{
+ return ts != NULL ? (int)ts[1] : 8;
+}
+
+/// Return the effective shiftwidth value for current buffer, using the
+/// 'tabstop' value when 'shiftwidth' is zero.
+int get_sw_value(buf_T *buf)
+{
+ long result = get_sw_value_col(buf, 0);
+ assert(result >= 0 && result <= INT_MAX);
+ return (int)result;
+}
+
+/// Idem, using "pos".
+long get_sw_value_pos(buf_T *buf, pos_T *pos)
+{
+ pos_T save_cursor = curwin->w_cursor;
+ long sw_value;
+
+ curwin->w_cursor = *pos;
+ sw_value = get_sw_value_col(buf, get_nolist_virtcol());
+ curwin->w_cursor = save_cursor;
+ return sw_value;
+}
+
+/// Idem, using the first non-black in the current line.
+long get_sw_value_indent(buf_T *buf)
+{
+ pos_T pos = curwin->w_cursor;
+
+ pos.col = (colnr_T)getwhitecols_curline();
+ return get_sw_value_pos(buf, &pos);
+}
+
+/// Idem, using virtual column "col".
+long get_sw_value_col(buf_T *buf, colnr_T col)
+{
+ return buf->b_p_sw ? buf->b_p_sw
+ : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
+}
+
+/// Return the effective softtabstop value for the current buffer,
+/// using the shiftwidth value when 'softtabstop' is negative.
+int get_sts_value(void)
+{
+ long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
+ assert(result >= 0 && result <= INT_MAX);
+ return (int)result;
+}
+
// Count the size (in window cells) of the indent in the current line.
int get_indent(void)
{
- return get_indent_str_vtab(get_cursor_line_ptr(),
+ return get_indent_str_vtab((char *)get_cursor_line_ptr(),
curbuf->b_p_ts,
curbuf->b_p_vts_array,
false);
@@ -42,7 +352,7 @@ int get_indent(void)
// Count the size (in window cells) of the indent in line "lnum".
int get_indent_lnum(linenr_T lnum)
{
- return get_indent_str_vtab(ml_get(lnum),
+ return get_indent_str_vtab((char *)ml_get(lnum),
curbuf->b_p_ts,
curbuf->b_p_vts_array,
false);
@@ -52,7 +362,7 @@ int get_indent_lnum(linenr_T lnum)
// "buf".
int get_indent_buf(buf_T *buf, linenr_T lnum)
{
- return get_indent_str_vtab(ml_get_buf(buf, lnum, false),
+ return get_indent_str_vtab((char *)ml_get_buf(buf, lnum, false),
curbuf->b_p_ts,
buf->b_p_vts_array,
false);
@@ -90,7 +400,7 @@ int get_indent_str(const char_u *ptr, int ts, bool list)
/// Count the size (in window cells) of the indent in line "ptr", using
/// variable tabstops.
/// if "list" is true, count only screen size for tabs.
-int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list)
+int get_indent_str_vtab(const char *ptr, long ts, long *vts, bool list)
{
int count = 0;
@@ -402,7 +712,7 @@ int get_number_indent(linenr_T lnum)
if ((State & MODE_INSERT) || has_format_option(FO_Q_COMS)) {
lead_len = get_leader_len((char *)ml_get(lnum), NULL, false, true);
}
- regmatch.regprog = vim_regcomp((char *)curbuf->b_p_flp, RE_MAGIC);
+ regmatch.regprog = vim_regcomp(curbuf->b_p_flp, RE_MAGIC);
if (regmatch.regprog != NULL) {
regmatch.rm_ic = false;
@@ -424,6 +734,47 @@ int get_number_indent(linenr_T lnum)
return (int)col;
}
+/// This is called when 'breakindentopt' is changed and when a window is
+/// initialized
+bool briopt_check(win_T *wp)
+{
+ int bri_shift = 0;
+ int bri_min = 20;
+ bool bri_sbr = false;
+ int bri_list = 0;
+
+ char *p = wp->w_p_briopt;
+ while (*p != NUL) {
+ if (STRNCMP(p, "shift:", 6) == 0
+ && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
+ p += 6;
+ bri_shift = getdigits_int(&p, true, 0);
+ } else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
+ p += 4;
+ bri_min = getdigits_int(&p, true, 0);
+ } else if (STRNCMP(p, "sbr", 3) == 0) {
+ p += 3;
+ bri_sbr = true;
+ } else if (STRNCMP(p, "list:", 5) == 0) {
+ p += 5;
+ bri_list = (int)getdigits(&p, false, 0);
+ }
+ if (*p != ',' && *p != NUL) {
+ return false;
+ }
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ wp->w_briopt_shift = bri_shift;
+ wp->w_briopt_min = bri_min;
+ wp->w_briopt_sbr = bri_sbr;
+ wp->w_briopt_list = bri_list;
+
+ return true;
+}
+
// Return appropriate space number for breakindent, taking influencing
// parameters into account. Window must be specified, since it is not
// necessarily always the current one.
@@ -449,7 +800,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
prev_ts = wp->w_buffer->b_p_ts;
prev_tick = buf_get_changedtick(wp->w_buffer);
prev_vts = wp->w_buffer->b_p_vts_array;
- prev_indent = get_indent_str_vtab(line,
+ prev_indent = get_indent_str_vtab((char *)line,
wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array,
wp->w_p_list);
@@ -462,7 +813,7 @@ int get_breakindent_win(win_T *wp, char_u *line)
// add additional indent for numbered lists
if (wp->w_briopt_list != 0) {
regmatch_T regmatch = {
- .regprog = vim_regcomp((char *)curbuf->b_p_flp,
+ .regprog = vim_regcomp(curbuf->b_p_flp,
RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT),
};
@@ -547,7 +898,7 @@ int get_expr_indent(void)
// Need to make a copy, the 'indentexpr' option could be changed while
// evaluating it.
- char_u *inde_copy = vim_strsave(curbuf->b_p_inde);
+ char_u *inde_copy = vim_strsave((char_u *)curbuf->b_p_inde);
indent = (int)eval_to_number((char *)inde_copy);
xfree(inde_copy);
@@ -684,13 +1035,15 @@ int get_lisp_indent(void)
amount = 2;
} else {
char_u *line = that;
-
- amount = 0;
-
- while (*that && col) {
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, pos->lnum, 0, line, line);
+ while (*cts.cts_ptr != NUL && col > 0) {
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
col--;
}
+ amount = cts.cts_vcol;
+ that = (char_u *)cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
// Some keywords require "body" indenting rules (the
// non-standard-lisp ones are Scheme special forms):
@@ -706,10 +1059,15 @@ int get_lisp_indent(void)
}
firsttry = amount;
- while (ascii_iswhite(*that)) {
- amount += lbr_chartabsize(line, that, (colnr_T)amount);
- that++;
+ init_chartabsize_arg(&cts, curwin, (colnr_T)(that - line),
+ amount, line, that);
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr++;
}
+ that = (char_u *)cts.cts_ptr;
+ amount = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (*that && (*that != ';')) {
// Not a comment line.
@@ -722,33 +1080,38 @@ int get_lisp_indent(void)
parencount = 0;
quotecount = 0;
+ init_chartabsize_arg(&cts, curwin,
+ (colnr_T)(that - line), amount, line, that);
if (vi_lisp || ((*that != '"') && (*that != '\'')
&& (*that != '#') && ((*that < '0') || (*that > '9')))) {
- while (*that
- && (!ascii_iswhite(*that) || quotecount || parencount)
- && (!((*that == '(' || *that == '[')
+ while (*cts.cts_ptr
+ && (!ascii_iswhite(*cts.cts_ptr) || quotecount || parencount)
+ && (!((*cts.cts_ptr == '(' || *cts.cts_ptr == '[')
&& !quotecount && !parencount && vi_lisp))) {
- if (*that == '"') {
+ if (*cts.cts_ptr == '"') {
quotecount = !quotecount;
}
- if (((*that == '(') || (*that == '[')) && !quotecount) {
+ if (((*cts.cts_ptr == '(') || (*cts.cts_ptr == '[')) && !quotecount) {
parencount++;
}
- if (((*that == ')') || (*that == ']')) && !quotecount) {
+ if (((*cts.cts_ptr == ')') || (*cts.cts_ptr == ']')) && !quotecount) {
parencount--;
}
- if ((*that == '\\') && (*(that + 1) != NUL)) {
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ if ((*cts.cts_ptr == '\\') && (*(cts.cts_ptr + 1) != NUL)) {
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount);
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
}
- while (ascii_iswhite(*that)) {
- amount += lbr_chartabsize(line, that, (colnr_T)amount);
- that++;
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ cts.cts_vcol += lbr_chartabsize(&cts);
+ cts.cts_ptr++;
}
+ that = (char_u *)cts.cts_ptr;
+ amount = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
if (!*that || (*that == ';')) {
amount = firsttry;
@@ -769,10 +1132,10 @@ static int lisp_match(char_u *p)
{
char_u buf[LSIZE];
int len;
- char_u *word = *curbuf->b_p_lw != NUL ? curbuf->b_p_lw : p_lispwords;
+ char *word = (char *)(*curbuf->b_p_lw != NUL ? (char_u *)curbuf->b_p_lw : p_lispwords);
while (*word != NUL) {
- (void)copy_option_part((char **)&word, (char *)buf, LSIZE, ",");
+ (void)copy_option_part(&word, (char *)buf, LSIZE, ",");
len = (int)STRLEN(buf);
if ((STRNCMP(buf, p, len) == 0) && (p[len] == ' ')) {
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index c5e030ce25..166695ef5b 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -212,19 +212,17 @@ int is_pos_in_string(const char_u *line, colnr_T col)
* Below "XXX" means that this function may unlock the current line.
*/
-/*
- * Return true if the string "line" starts with a word from 'cinwords'.
- */
-bool cin_is_cinword(const char_u *line)
+/// @return true if the string "line" starts with a word from 'cinwords'.
+bool cin_is_cinword(const char *line)
{
bool retval = false;
size_t cinw_len = STRLEN(curbuf->b_p_cinw) + 1;
char_u *cinw_buf = xmalloc(cinw_len);
- line = (char_u *)skipwhite((char *)line);
+ line = skipwhite((char *)line);
- for (char_u *cinw = curbuf->b_p_cinw; *cinw;) {
- size_t len = copy_option_part((char **)&cinw, (char *)cinw_buf, cinw_len, ",");
+ for (char *cinw = curbuf->b_p_cinw; *cinw;) {
+ size_t len = copy_option_part(&cinw, (char *)cinw_buf, cinw_len, ",");
if (STRNCMP(line, cinw_buf, len) == 0
&& (!vim_iswordc(line[len]) || !vim_iswordc(line[len - 1]))) {
retval = true;
@@ -275,10 +273,8 @@ static const char_u *cin_skipcomment(const char_u *s)
return s;
}
-/*
- * Return TRUE if there is no code at *s. White space and comments are
- * not considered code.
- */
+/// Return true if there is no code at *s. White space and comments are
+/// not considered code.
static int cin_nocode(const char_u *s)
{
return *cin_skipcomment(s) == NUL;
@@ -317,17 +313,17 @@ static bool cin_has_js_key(const char_u *text)
if (*s == '\'' || *s == '"') {
// can be 'key': or "key":
quote = *s;
- ++s;
+ s++;
}
if (!vim_isIDc(*s)) { // need at least one ID character
return false;
}
while (vim_isIDc(*s)) {
- ++s;
+ s++;
}
if (*s && *s == quote) {
- ++s;
+ s++;
}
s = cin_skipcomment(s);
@@ -383,7 +379,7 @@ bool cin_islabel(void) // XXX
cursor_save = curwin->w_cursor;
while (curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
/*
* If we're in a comment or raw string now, skip to the start of
@@ -403,7 +399,7 @@ bool cin_islabel(void) // XXX
}
curwin->w_cursor = cursor_save;
- if (cin_isterminated(line, TRUE, FALSE)
+ if (cin_isterminated(line, true, false)
|| cin_isscopedecl(line)
|| cin_iscase(line, true)
|| (cin_islabel_skip(&line) && cin_nocode(line))) {
@@ -434,7 +430,7 @@ static int cin_isinit(void)
for (;;) {
int i, l;
- for (i = 0; i < (int)ARRAY_SIZE(skip); ++i) {
+ for (i = 0; i < (int)ARRAY_SIZE(skip); i++) {
l = (int)strlen(skip[i]);
if (cin_starts_with(s, skip[i])) {
s = cin_skipcomment(s + l);
@@ -455,7 +451,7 @@ static int cin_isinit(void)
return true;
}
- return FALSE;
+ return false;
}
/// Recognize a switch label: "case .*:" or "default:".
@@ -465,7 +461,7 @@ bool cin_iscase(const char_u *s, bool strict)
{
s = cin_skipcomment(s);
if (cin_starts_with(s, "case")) {
- for (s += 4; *s; ++s) {
+ for (s += 4; *s; s++) {
s = cin_skipcomment(s);
if (*s == NUL) {
break;
@@ -519,8 +515,8 @@ bool cin_isscopedecl(const char_u *p)
bool found = false;
- for (char_u *cinsd = curbuf->b_p_cinsd; *cinsd;) {
- const size_t len = copy_option_part((char **)&cinsd, (char *)cinsd_buf, cinsd_len, ",");
+ for (char *cinsd = curbuf->b_p_cinsd; *cinsd;) {
+ const size_t len = copy_option_part(&cinsd, (char *)cinsd_buf, cinsd_len, ",");
if (STRNCMP(s, cinsd_buf, len) == 0) {
const char_u *skip = cin_skipcomment(s + len);
if (*skip == ':' && skip[1] != ':') {
@@ -588,7 +584,7 @@ static bool cin_is_cpp_namespace(const char_u *s)
*/
static const char_u *after_label(const char_u *l)
{
- for (; *l; ++l) {
+ for (; *l; l++) {
if (*l == ':') {
if (l[1] == ':') { // skip over "::" for C++
l++;
@@ -680,10 +676,10 @@ static int cin_first_id_amount(void)
line = get_cursor_line_ptr();
p = (char_u *)skipwhite((char *)line);
- len = (int)(skiptowhite(p) - p);
+ len = (int)((char_u *)skiptowhite((char *)p) - p);
if (len == 6 && STRNCMP(p, "static", 6) == 0) {
p = (char_u *)skipwhite((char *)p + 6);
- len = (int)(skiptowhite(p) - p);
+ len = (int)((char_u *)skiptowhite((char *)p) - p);
}
if (len == 6 && STRNCMP(p, "struct", 6) == 0) {
p = (char_u *)skipwhite((char *)p + 6);
@@ -769,10 +765,10 @@ static int cin_ispreproc(const char_u *s)
if (*skipwhite((char *)s) == '#') {
return true;
}
- return FALSE;
+ return false;
}
-/// Return TRUE if line "*pp" at "*lnump" is a preprocessor statement or a
+/// Return true if line "*pp" at "*lnump" is a preprocessor statement or a
/// continuation line of a preprocessor statement. Decrease "*lnump" to the
/// start and return the line in "*pp".
/// Put the amount of indent in "*amount".
@@ -789,7 +785,7 @@ static int cin_ispreproc_cont(const char_u **pp, linenr_T *lnump, int *amount)
for (;;) {
if (cin_ispreproc(line)) {
- retval = TRUE;
+ retval = true;
*lnump = lnum;
break;
}
@@ -842,7 +838,7 @@ static char_u cin_isterminated(const char_u *s, int incl_open, int incl_comma)
{
char_u found_start = 0;
unsigned n_open = 0;
- int is_else = FALSE;
+ int is_else = false;
s = cin_skipcomment(s);
@@ -1095,14 +1091,12 @@ probablyFound:
return 0;
}
-/*
- * Return TRUE if we are at the end of a do-while.
- * do
- * nothing;
- * while (foo
- * && bar); <-- here
- * Adjust the cursor to the line with "while".
- */
+/// Return true if we are at the end of a do-while.
+/// do
+/// nothing;
+/// while (foo
+/// && bar); <-- here
+/// Adjust the cursor to the line with "while".
static int cin_iswhileofdo_end(int terminated)
{
const char_u *line;
@@ -1133,7 +1127,7 @@ static int cin_iswhileofdo_end(int terminated)
}
if (cin_starts_with(s, "while")) {
curwin->w_cursor.lnum = trypos->lnum;
- return TRUE;
+ return true;
}
}
@@ -1146,7 +1140,7 @@ static int cin_iswhileofdo_end(int terminated)
p++;
}
}
- return FALSE;
+ return false;
}
static int cin_isbreak(const char_u *p)
@@ -1190,7 +1184,7 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
return false;
}
- cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
+ cpp_base_class = lookfor_ctor_init = class_or_struct = false;
/* Search for a line starting with '#', empty, ending in ';' or containing
* '{' or '}' and start below it. This handles the following situations:
@@ -1256,7 +1250,7 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
if (s[1] == ':') {
/* skip double colon. It can't be a constructor
* initialization any more */
- lookfor_ctor_init = FALSE;
+ lookfor_ctor_init = false;
s = cin_skipcomment(s + 2);
} else if (lookfor_ctor_init || class_or_struct) {
/* we have something found, that looks like the start of
@@ -1270,8 +1264,8 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
}
} else if ((STRNCMP(s, "class", 5) == 0 && !vim_isIDc(s[5]))
|| (STRNCMP(s, "struct", 6) == 0 && !vim_isIDc(s[6]))) {
- class_or_struct = TRUE;
- lookfor_ctor_init = FALSE;
+ class_or_struct = true;
+ lookfor_ctor_init = false;
if (*s == 'c') {
s = cin_skipcomment(s + 5);
@@ -1280,12 +1274,12 @@ static int cin_is_cpp_baseclass(cpp_baseclass_cache_T *cached)
}
} else {
if (s[0] == '{' || s[0] == '}' || s[0] == ';') {
- cpp_base_class = lookfor_ctor_init = class_or_struct = FALSE;
+ cpp_base_class = lookfor_ctor_init = class_or_struct = false;
} else if (s[0] == ')') {
/* Constructor-initialization is assumed if we come across
* something like "):" */
- class_or_struct = FALSE;
- lookfor_ctor_init = TRUE;
+ class_or_struct = false;
+ lookfor_ctor_init = true;
} else if (s[0] == '?') {
// Avoid seeing '() :' after '?' as constructor init.
return false;
@@ -1345,11 +1339,9 @@ static int get_baseclass_amount(int col)
return amount;
}
-/*
- * Return TRUE if string "s" ends with the string "find", possibly followed by
- * white space and comments. Skip strings and comments.
- * Ignore "ignore" after "find" if it's not NULL.
- */
+/// Return true if string "s" ends with the string "find", possibly followed by
+/// white space and comments. Skip strings and comments.
+/// Ignore "ignore" after "find" if it's not NULL.
static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore)
{
const char_u *p = s;
@@ -1371,12 +1363,10 @@ static int cin_ends_in(const char_u *s, const char_u *find, const char_u *ignore
p++;
}
}
- return FALSE;
+ return false;
}
-/*
- * Return TRUE when "s" starts with "word" and then a non-ID character.
- */
+/// Return true when "s" starts with "word" and then a non-ID character.
static int cin_starts_with(const char_u *s, const char *word)
{
int l = (int)STRLEN(word);
@@ -1573,7 +1563,7 @@ static int corr_ind_maxparen(pos_T *startpos)
static int find_last_paren(const char_u *l, int start, int end)
{
int i;
- int retval = FALSE;
+ int retval = false;
int open_count = 0;
curwin->w_cursor.col = 0; // default is start of line
@@ -1588,7 +1578,7 @@ static int find_last_paren(const char_u *l, int start, int end)
open_count--;
} else {
curwin->w_cursor.col = i;
- retval = TRUE;
+ retval = true;
}
}
}
@@ -1601,8 +1591,8 @@ static int find_last_paren(const char_u *l, int start, int end)
*/
void parse_cino(buf_T *buf)
{
- char_u *p;
- char_u *l;
+ char *p;
+ char *l;
int divider;
int fraction = 0;
int sw = get_sw_value(buf);
@@ -1745,11 +1735,11 @@ void parse_cino(buf_T *buf)
if (*p == '-') {
p++;
}
- char_u *digits_start = p; // remember where the digits start
- int n = getdigits_int((char **)&p, true, 0);
+ char *digits_start = p; // remember where the digits start
+ int n = getdigits_int(&p, true, 0);
divider = 0;
if (*p == '.') { // ".5s" means a fraction.
- fraction = atoi((char *)++p);
+ fraction = atoi(++p);
while (ascii_isdigit(*p)) {
p++;
if (divider) {
@@ -1768,7 +1758,7 @@ void parse_cino(buf_T *buf)
n += (sw * fraction + divider / 2) / divider;
}
}
- ++p;
+ p++;
}
if (l[1] == '-') {
n = -n;
@@ -2029,7 +2019,7 @@ int get_c_indent(void)
if (trypos == NULL && curwin->w_cursor.lnum > 1) {
// There may be a statement before the comment, search from the end
// of the line for a comment start.
- linecomment_pos.col = check_linecomment(ml_get(curwin->w_cursor.lnum - 1));
+ linecomment_pos.col = check_linecomment((char *)ml_get(curwin->w_cursor.lnum - 1));
if (linecomment_pos.col != MAXCOL) {
trypos = &linecomment_pos;
trypos->lnum = curwin->w_cursor.lnum - 1;
@@ -2052,10 +2042,10 @@ int get_c_indent(void)
char lead_start[COM_MAX_LEN]; // start-comment string
char lead_middle[COM_MAX_LEN]; // middle-comment string
char lead_end[COM_MAX_LEN]; // end-comment string
- char_u *p;
+ char *p;
int start_align = 0;
int start_off = 0;
- int done = FALSE;
+ int done = false;
// find how indented the line beginning the comment is
getvcol(curwin, comment_pos, &col, NULL, NULL);
@@ -2071,11 +2061,11 @@ int get_c_indent(void)
while (*p != NUL && *p != ':') {
if (*p == COM_START || *p == COM_END || *p == COM_MIDDLE) {
- what = *p++;
+ what = (unsigned char)(*p++);
} else if (*p == COM_LEFT || *p == COM_RIGHT) {
- align = *p++;
+ align = (unsigned char)(*p++);
} else if (ascii_isdigit(*p) || *p == '-') {
- off = getdigits_int((char **)&p, true, 0);
+ off = getdigits_int(&p, true, 0);
} else {
p++;
}
@@ -2084,7 +2074,7 @@ int get_c_indent(void)
if (*p == ':') {
p++;
}
- (void)copy_option_part((char **)&p, lead_end, COM_MAX_LEN, ",");
+ (void)copy_option_part(&p, lead_end, COM_MAX_LEN, ",");
if (what == COM_START) {
STRCPY(lead_start, lead_end);
lead_start_len = (int)STRLEN(lead_start);
@@ -2098,7 +2088,7 @@ int get_c_indent(void)
* up with the comment opener per the 'comments' option. */
if (STRNCMP(theline, lead_middle, lead_middle_len) == 0
&& STRNCMP(theline, lead_end, STRLEN(lead_end)) != 0) {
- done = TRUE;
+ done = true;
if (curwin->w_cursor.lnum > 1) {
/* If the start comment string matches in the previous
* line, use the indent of that line plus offset. If
@@ -2330,7 +2320,7 @@ int get_c_indent(void)
/* look for opening unmatched paren, indent one level
* for each additional level */
n = 1;
- for (col = 0; col < our_paren_pos.col; ++col) {
+ for (col = 0; col < our_paren_pos.col; col++) {
switch (l[col]) {
case '(':
case '{':
@@ -2388,7 +2378,7 @@ int get_c_indent(void)
* but ignore (void) before the line (ignore_paren_col). */
col = our_paren_pos.col;
while ((int)our_paren_pos.col > ignore_paren_col) {
- --our_paren_pos.col;
+ our_paren_pos.col--;
switch (*ml_get_pos(&our_paren_pos)) {
case '(':
amount += curbuf->b_ind_unclosed2;
@@ -2562,7 +2552,7 @@ int get_c_indent(void)
}
}
- lookfor_break = FALSE;
+ lookfor_break = false;
if (cin_iscase(theline, false)) { // it's a switch() label
lookfor = LOOKFOR_CASE; // find a previous switch() label
@@ -2646,7 +2636,7 @@ int get_c_indent(void)
continue;
}
- terminated = cin_isterminated(l, FALSE, TRUE);
+ terminated = cin_isterminated(l, false, true);
/*
* If we are at top level and the line looks like a
@@ -2660,7 +2650,7 @@ int get_c_indent(void)
* don't add extra indent.
* TODO: does not work, if a function
* declaration is split over multiple lines:
- * cin_isfuncdecl returns FALSE then.
+ * cin_isfuncdecl returns false then.
*/
if (terminated == ',') {
break;
@@ -2862,7 +2852,7 @@ int get_c_indent(void)
if (n) {
amount = n;
l = after_label(get_cursor_line_ptr());
- if (l != NULL && cin_is_cinword(l)) {
+ if (l != NULL && cin_is_cinword((char *)l)) {
if (theline[0] == '{') {
amount += curbuf->b_ind_open_extra;
} else {
@@ -2971,7 +2961,7 @@ int get_c_indent(void)
* initialisation (not indented) or a variable declaration
* (indented).
*/
- terminated = cin_isterminated(l, FALSE, TRUE);
+ terminated = cin_isterminated(l, false, true);
if (js_cur_has_key) {
js_cur_has_key = false; // only check the first line
@@ -3116,7 +3106,7 @@ int get_c_indent(void)
* Check if we are after an "if", "while", etc.
* Also allow " } else".
*/
- if (cin_is_cinword(l) || cin_iselse((char_u *)skipwhite((char *)l))) {
+ if (cin_is_cinword((char *)l) || cin_iselse((char_u *)skipwhite((char *)l))) {
// Found an unterminated line after an if (), line up
// with the last one.
// if (cond)
@@ -3334,7 +3324,7 @@ int get_c_indent(void)
amount += curbuf->b_ind_open_extra;
}
}
- ++whilelevel;
+ whilelevel++;
}
/*
* We are after a "normal" statement.
@@ -3848,7 +3838,7 @@ static int find_match(int lookfor, linenr_T ourscope)
* another "do", so increment whilelevel. XXX
*/
if (cin_iswhileofdo(look, curwin->w_cursor.lnum)) {
- ++whilelevel;
+ whilelevel++;
continue;
}
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 37c2903cfd..0aa9feaca3 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -46,7 +46,7 @@ int ask_yesno(const char *const str, const bool direct)
int r = ' ';
while (r != 'y' && r != 'n') {
- // Same highlighting as for wait_return.
+ // same highlighting as for wait_return()
smsg_attr(HL_ATTR(HLF_R), "%s (y/n)?", str);
if (direct) {
r = get_keystroke(NULL);
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index a64d8e8f00..ba0a36cafe 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -12,11 +12,14 @@
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
@@ -33,14 +36,14 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
+#include "nvim/textformat.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -138,6 +141,21 @@ struct compl_S {
int cp_number; ///< sequence number
};
+/// state information used for getting the next set of insert completion
+/// matches.
+typedef struct {
+ char *e_cpt; ///< current entry in 'complete'
+ buf_T *ins_buf; ///< buffer being scanned
+ pos_T *cur_match_pos; ///< current match position
+ pos_T prev_match_pos; ///< previous match position
+ bool set_match_pos; ///< save first_match_pos/last_match_pos
+ pos_T first_match_pos; ///< first match position
+ pos_T last_match_pos; ///< last match position
+ bool found_all; ///< found all matches of a certain type.
+ char_u *dict; ///< dictionary file to search
+ int dict_f; ///< "dict" is an exact file name or not
+} ins_compl_next_state_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "insexpand.c.generated.h"
#endif
@@ -160,6 +178,7 @@ static char e_compldel[] = N_("E840: Completion function deleted text");
// "compl_curr_match" points to the currently selected entry.
// "compl_shown_match" is different from compl_curr_match during
// ins_compl_get_exp().
+// "compl_old_match" points to previous "compl_curr_match".
static compl_T *compl_first_match = NULL;
static compl_T *compl_curr_match = NULL;
@@ -201,12 +220,15 @@ static bool compl_started = false;
///< Which Ctrl-X mode are we in?
static int ctrl_x_mode = CTRL_X_NORMAL;
-static int compl_matches = 0;
+static int compl_matches = 0; ///< number of completion matches
static char *compl_pattern = NULL;
static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N
static pos_T compl_startpos;
+/// Length in bytes of the text being completed (this is deleted to be replaced
+/// by the match.)
+static int compl_length = 0;
static colnr_T compl_col = 0; ///< column where the text starts
///< that is being completed
static char_u *compl_orig_text = NULL; ///< text as it was before
@@ -214,6 +236,20 @@ static char_u *compl_orig_text = NULL; ///< text as it was before
static int compl_cont_mode = 0;
static expand_T compl_xp;
+// List of flags for method of completion.
+static int compl_cont_status = 0;
+
+#define CONT_ADDING 1 ///< "normal" or "adding" expansion
+#define CONT_INTRPT (2 + 4) ///< a ^X interrupted the current expansion
+ ///< it's set only iff N_ADDS is set
+#define CONT_N_ADDS 4 ///< next ^X<> will add-new or expand-current
+#define CONT_S_IPOS 8 ///< next ^X<> will set initial_pos?
+ ///< if so, word-wise-expansion will set SOL
+#define CONT_SOL 16 ///< pattern includes start of line, just for
+ ///< word-wise expansion, not set for ^X^L
+#define CONT_LOCAL 32 ///< for ctrl_x_mode 0, ^X^P/^X^N do a local
+ ///< expansion, (eg use complete=.)
+
static bool compl_opt_refresh_always = false;
static size_t spell_bad_len = 0; // length of located bad word
@@ -232,7 +268,7 @@ void ins_ctrl_x(void)
}
// We're not sure which CTRL-X mode it will be yet
ctrl_x_mode = CTRL_X_NOT_DEFINED_YET;
- edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
+ edit_submode = _(CTRL_X_MSG(ctrl_x_mode));
edit_submode_pre = NULL;
showmode();
} else {
@@ -357,9 +393,52 @@ bool ctrl_x_mode_not_defined_yet(void)
return ctrl_x_mode == CTRL_X_NOT_DEFINED_YET;
}
-/// Check that the "dict" or "tsr" option can be used.
+/// @return true if currently in "normal" or "adding" insert completion matches state
+bool compl_status_adding(void)
+{
+ return compl_cont_status & CONT_ADDING;
+}
+
+/// @return true if the completion pattern includes start of line, just for
+/// word-wise expansion.
+bool compl_status_sol(void)
+{
+ return compl_cont_status & CONT_SOL;
+}
+
+/// @return true if ^X^P/^X^N will do a local completion (i.e. use complete=.)
+bool compl_status_local(void)
+{
+ return compl_cont_status & CONT_LOCAL;
+}
+
+/// Clear the completion status flags
+void compl_status_clear(void)
+{
+ compl_cont_status = 0;
+}
+
+// @return true if completion is using the forward direction matches
+static bool compl_dir_forward(void)
+{
+ return compl_direction == FORWARD;
+}
+
+/// @return true if currently showing forward completion matches
+static bool compl_shows_dir_forward(void)
+{
+ return compl_shows_dir == FORWARD;
+}
+
+/// @return true if currently showing backward completion matches
+static bool compl_shows_dir_backward(void)
+{
+ return compl_shows_dir == BACKWARD;
+}
+
+/// Check that the 'dictionary' or 'thesaurus' option can be used.
///
-/// @param dict_opt check "dict" when true, "tsr" when false.
+/// @param dict_opt check 'dictionary' when true, 'thesaurus' when false.
bool check_compl_option(bool dict_opt)
{
if (dict_opt
@@ -443,6 +522,18 @@ bool vim_is_ctrl_x_key(int c)
return false;
}
+/// @return true if "match" is the original text when the completion began.
+static bool match_at_original_text(const compl_T *const match)
+{
+ return match->cp_flags & CP_ORIGINAL_TEXT;
+}
+
+/// @return true if "match" is the first match in the completion list.
+static bool is_first_match(const compl_T *const match)
+{
+ return match == compl_first_match;
+}
+
/// Check that character "c" is part of the item currently being
/// completed. Used to decide whether to abandon complete mode when the menu
/// is visible.
@@ -477,6 +568,106 @@ bool ins_compl_accept_char(int c)
return vim_iswordc(c);
}
+/// Get the completed text by inferring the case of the originally typed text.
+/// If the result is in allocated memory "tofree" is set to it.
+static char_u *ins_compl_infercase_gettext(char_u *str, int char_len, int compl_char_len,
+ int min_len, char **tofree)
+{
+ bool has_lower = false;
+ bool was_letter = false;
+
+ // Allocate wide character array for the completion and fill it.
+ int *const wca = xmalloc((size_t)char_len * sizeof(*wca));
+ {
+ const char_u *p = str;
+ for (int i = 0; i < char_len; i++) {
+ wca[i] = mb_ptr2char_adv(&p);
+ }
+ }
+
+ // Rule 1: Were any chars converted to lower?
+ {
+ const char_u *p = compl_orig_text;
+ for (int i = 0; i < min_len; i++) {
+ const int c = mb_ptr2char_adv(&p);
+ if (mb_islower(c)) {
+ has_lower = true;
+ if (mb_isupper(wca[i])) {
+ // Rule 1 is satisfied.
+ for (i = compl_char_len; i < char_len; i++) {
+ wca[i] = mb_tolower(wca[i]);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Rule 2: No lower case, 2nd consecutive letter converted to
+ // upper case.
+ if (!has_lower) {
+ const char_u *p = compl_orig_text;
+ for (int i = 0; i < min_len; i++) {
+ const int c = mb_ptr2char_adv(&p);
+ if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
+ // Rule 2 is satisfied.
+ for (i = compl_char_len; i < char_len; i++) {
+ wca[i] = mb_toupper(wca[i]);
+ }
+ break;
+ }
+ was_letter = mb_islower(c) || mb_isupper(c);
+ }
+ }
+
+ // Copy the original case of the part we typed.
+ {
+ const char_u *p = compl_orig_text;
+ for (int i = 0; i < min_len; i++) {
+ const int c = mb_ptr2char_adv(&p);
+ if (mb_islower(c)) {
+ wca[i] = mb_tolower(wca[i]);
+ } else if (mb_isupper(c)) {
+ wca[i] = mb_toupper(wca[i]);
+ }
+ }
+ }
+
+ // Generate encoding specific output from wide character array.
+ garray_T gap;
+ char *p = (char *)IObuff;
+ int i = 0;
+ ga_init(&gap, 1, 500);
+ while (i < char_len) {
+ if (gap.ga_data != NULL) {
+ ga_grow(&gap, 10);
+ assert(gap.ga_data != NULL); // suppress clang "Dereference of NULL pointer"
+ p = (char *)gap.ga_data + gap.ga_len;
+ gap.ga_len += utf_char2bytes(wca[i++], p);
+ } else if ((p - (char *)IObuff) + 6 >= IOSIZE) {
+ // Multi-byte characters can occupy up to five bytes more than
+ // ASCII characters, and we also need one byte for NUL, so when
+ // getting to six bytes from the edge of IObuff switch to using a
+ // growarray. Add the character in the next round.
+ ga_grow(&gap, IOSIZE);
+ *p = NUL;
+ STRCPY(gap.ga_data, IObuff);
+ gap.ga_len = (int)STRLEN(IObuff);
+ } else {
+ p += utf_char2bytes(wca[i++], p);
+ }
+ }
+ xfree(wca);
+
+ if (gap.ga_data != NULL) {
+ *tofree = gap.ga_data;
+ return gap.ga_data;
+ }
+
+ *p = NUL;
+ return IObuff;
+}
+
/// This is like ins_compl_add(), but if 'ic' and 'inf' are set, then the
/// case of the originally typed text is used, and the case of the completed
/// text is inferred, ie this tries to work out what case you probably wanted
@@ -488,13 +679,10 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *str = str_arg;
- int i, c;
- int actual_len; // Take multi-byte characters
- int actual_compl_length; // into account.
- int min_len;
- bool has_lower = false;
- bool was_letter = false;
+ int char_len; // count multi-byte characters
+ int compl_char_len;
int flags = 0;
+ char *tofree = NULL;
if (p_ic && curbuf->b_p_inf && len > 0) {
// Infer case of completed part.
@@ -502,101 +690,28 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
// Find actual length of completion.
{
const char_u *p = str;
- actual_len = 0;
+ char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
- actual_len++;
+ char_len++;
}
}
// Find actual length of original text.
{
const char_u *p = compl_orig_text;
- actual_compl_length = 0;
+ compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
- actual_compl_length++;
+ compl_char_len++;
}
}
- // "actual_len" may be smaller than "actual_compl_length" when using
+ // "char_len" may be smaller than "compl_char_len" when using
// thesaurus, only use the minimum when comparing.
- min_len = actual_len < actual_compl_length
- ? actual_len : actual_compl_length;
-
- // Allocate wide character array for the completion and fill it.
- int *const wca = xmalloc((size_t)actual_len * sizeof(*wca));
- {
- const char_u *p = str;
- for (i = 0; i < actual_len; i++) {
- wca[i] = mb_ptr2char_adv(&p);
- }
- }
-
- // Rule 1: Were any chars converted to lower?
- {
- const char_u *p = compl_orig_text;
- for (i = 0; i < min_len; i++) {
- c = mb_ptr2char_adv(&p);
- if (mb_islower(c)) {
- has_lower = true;
- if (mb_isupper(wca[i])) {
- // Rule 1 is satisfied.
- for (i = actual_compl_length; i < actual_len; i++) {
- wca[i] = mb_tolower(wca[i]);
- }
- break;
- }
- }
- }
- }
-
- // Rule 2: No lower case, 2nd consecutive letter converted to
- // upper case.
- if (!has_lower) {
- const char_u *p = compl_orig_text;
- for (i = 0; i < min_len; i++) {
- c = mb_ptr2char_adv(&p);
- if (was_letter && mb_isupper(c) && mb_islower(wca[i])) {
- // Rule 2 is satisfied.
- for (i = actual_compl_length; i < actual_len; i++) {
- wca[i] = mb_toupper(wca[i]);
- }
- break;
- }
- was_letter = mb_islower(c) || mb_isupper(c);
- }
- }
+ int min_len = char_len < compl_char_len ? char_len : compl_char_len;
- // Copy the original case of the part we typed.
- {
- const char_u *p = compl_orig_text;
- for (i = 0; i < min_len; i++) {
- c = mb_ptr2char_adv(&p);
- if (mb_islower(c)) {
- wca[i] = mb_tolower(wca[i]);
- } else if (mb_isupper(c)) {
- wca[i] = mb_toupper(wca[i]);
- }
- }
- }
-
- // Generate encoding specific output from wide character array.
- // Multi-byte characters can occupy up to five bytes more than
- // ASCII characters, and we also need one byte for NUL, so stay
- // six bytes away from the edge of IObuff.
- {
- char_u *p = IObuff;
- i = 0;
- while (i < actual_len && (p - IObuff + 6) < IOSIZE) {
- p += utf_char2bytes(wca[i++], (char *)p);
- }
- *p = NUL;
- }
-
- xfree(wca);
-
- str = IObuff;
+ str = ins_compl_infercase_gettext(str, char_len, compl_char_len, min_len, &tofree);
}
if (cont_s_ipos) {
flags |= CP_CONT_S_IPOS;
@@ -605,22 +720,30 @@ int ins_compl_add_infercase(char_u *str_arg, int len, bool icase, char_u *fname,
flags |= CP_ICASE;
}
- return ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false);
+ int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false);
+ xfree(tofree);
+ return res;
}
/// Add a match to the list of matches
///
-/// @param[in] str Match to add.
-/// @param[in] len Match length, -1 to use #STRLEN.
-/// @param[in] fname File name match comes from. May be NULL.
-/// @param[in] cptext Extra text for popup menu. May be NULL. If not NULL,
-/// must have exactly #CPT_COUNT items.
+/// @param[in] str text of the match to add
+/// @param[in] len length of "str". If -1, then the length of "str" is computed.
+/// @param[in] fname file name to associate with this match. May be NULL.
+/// @param[in] cptext list of strings to use with this match (for abbr, menu, info
+/// and kind). May be NULL.
+/// If not NULL, must have exactly #CPT_COUNT items.
/// @param[in] cptext_allocated If true, will not copy cptext strings.
///
/// @note Will free strings in case of error.
/// cptext itself will not be freed.
-/// @param[in] cdir Completion direction.
-/// @param[in] adup True if duplicate matches are to be accepted.
+/// @param[in] user_data user supplied data (any vim type) for this match
+/// @param[in] cdir match direction. If 0, use "compl_direction".
+/// @param[in] flags_arg match flags (cp_flags)
+/// @param[in] adup accept this match even if it is already present.
+///
+/// If "cdir" is FORWARD, then the match is added after the current match.
+/// Otherwise, it is added before the current match.
///
/// @return NOTDONE if the given string is already in the list of completions,
/// otherwise it is added to the list and OK is returned. FAIL will be
@@ -659,14 +782,14 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
if (compl_first_match != NULL && !adup) {
match = compl_first_match;
do {
- if (!(match->cp_flags & CP_ORIGINAL_TEXT)
+ if (!match_at_original_text(match)
&& STRNCMP(match->cp_str, str, len) == 0
- && match->cp_str[len] == NUL) {
+ && ((int)STRLEN(match->cp_str) <= len || match->cp_str[len] == NUL)) {
FREE_CPTEXT(cptext, cptext_allocated);
return NOTDONE;
}
match = match->cp_next;
- } while (match != NULL && match != compl_first_match);
+ } while (match != NULL && !is_first_match(match));
}
// Remove any popup menu before changing the list of matches.
@@ -719,7 +842,8 @@ static int ins_compl_add(char_u *const str, int len, char_u *const fname,
match->cp_user_data = *user_data;
}
- // Link the new match structure in the list of matches.
+ // Link the new match structure after (FORWARD) or before (BACKWARD) the
+ // current match in the list of matches .
if (compl_first_match == NULL) {
match->cp_next = match->cp_prev = NULL;
} else if (dir == FORWARD) {
@@ -775,9 +899,10 @@ static void ins_compl_longest_match(compl_T *match)
if (compl_leader == NULL) {
// First match, use it as a whole.
compl_leader = vim_strsave(match->cp_str);
+
had_match = (curwin->w_cursor.col > compl_col);
ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_bytes((char *)compl_leader + get_compl_len());
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
@@ -786,40 +911,42 @@ static void ins_compl_longest_match(compl_T *match)
ins_compl_delete();
}
compl_used_match = false;
- } else {
- // Reduce the text if this match differs from compl_leader.
- p = compl_leader;
- s = match->cp_str;
- while (*p != NUL) {
- c1 = utf_ptr2char((char *)p);
- c2 = utf_ptr2char((char *)s);
-
- if ((match->cp_flags & CP_ICASE)
- ? (mb_tolower(c1) != mb_tolower(c2))
- : (c1 != c2)) {
- break;
- }
- MB_PTR_ADV(p);
- MB_PTR_ADV(s);
- }
- if (*p != NUL) {
- // Leader was shortened, need to change the inserted text.
- *p = NUL;
- had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
- ins_redraw(false);
+ return;
+ }
- // When the match isn't there (to avoid matching itself) remove it
- // again after redrawing.
- if (!had_match) {
- ins_compl_delete();
- }
+ // Reduce the text if this match differs from compl_leader.
+ p = compl_leader;
+ s = match->cp_str;
+ while (*p != NUL) {
+ c1 = utf_ptr2char((char *)p);
+ c2 = utf_ptr2char((char *)s);
+
+ if ((match->cp_flags & CP_ICASE)
+ ? (mb_tolower(c1) != mb_tolower(c2))
+ : (c1 != c2)) {
+ break;
}
+ MB_PTR_ADV(p);
+ MB_PTR_ADV(s);
+ }
- compl_used_match = false;
+ if (*p != NUL) {
+ // Leader was shortened, need to change the inserted text.
+ *p = NUL;
+ had_match = (curwin->w_cursor.col > compl_col);
+ ins_compl_delete();
+ ins_bytes((char *)compl_leader + get_compl_len());
+ ins_redraw(false);
+
+ // When the match isn't there (to avoid matching itself) remove it
+ // again after redrawing.
+ if (!had_match) {
+ ins_compl_delete();
+ }
}
+
+ compl_used_match = false;
}
/// Add an array of matches to the list of matches.
@@ -844,20 +971,21 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase)
/// Return the number of matches (excluding the original).
static int ins_compl_make_cyclic(void)
{
- compl_T *match;
- int count = 0;
+ if (compl_first_match == NULL) {
+ return 0;
+ }
- if (compl_first_match != NULL) {
- // Find the end of the list.
- match = compl_first_match;
- // there's always an entry for the compl_orig_text, it doesn't count.
- while (match->cp_next != NULL && match->cp_next != compl_first_match) {
- match = match->cp_next;
- count++;
- }
- match->cp_next = compl_first_match;
- compl_first_match->cp_prev = match;
+ // Find the end of the list.
+ compl_T *match = compl_first_match;
+ int count = 0;
+ // there's always an entry for the compl_orig_text, it doesn't count.
+ while (match->cp_next != NULL && !is_first_match(match->cp_next)) {
+ match = match->cp_next;
+ count++;
}
+ match->cp_next = compl_first_match;
+ compl_first_match->cp_prev = match;
+
return count;
}
@@ -880,10 +1008,10 @@ void completeopt_was_set(void)
{
compl_no_insert = false;
compl_no_select = false;
- if (strstr((char *)p_cot, "noselect") != NULL) {
+ if (strstr(p_cot, "noselect") != NULL) {
compl_no_select = true;
}
- if (strstr((char *)p_cot, "noinsert") != NULL) {
+ if (strstr(p_cot, "noinsert") != NULL) {
compl_no_insert = true;
}
}
@@ -896,10 +1024,12 @@ static int compl_match_arraysize;
/// Remove any popup menu.
static void ins_compl_del_pum(void)
{
- if (compl_match_array != NULL) {
- pum_undisplay(false);
- XFREE_CLEAR(compl_match_array);
+ if (compl_match_array == NULL) {
+ return;
}
+
+ pum_undisplay(false);
+ XFREE_CLEAR(compl_match_array);
}
/// Check if the popup menu should be displayed.
@@ -907,7 +1037,7 @@ bool pum_wanted(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// "completeopt" must contain "menu" or "menuone"
- return vim_strchr((char *)p_cot, 'm') != NULL;
+ return vim_strchr(p_cot, 'm') != NULL;
}
/// Check that there are two or more matches to be shown in the popup menu.
@@ -917,17 +1047,16 @@ static bool pum_enough_matches(void)
{
// Don't display the popup menu if there are no matches or there is only
// one (ignoring the original text).
- compl_T *comp = compl_first_match;
+ compl_T *compl = compl_first_match;
int i = 0;
do {
- if (comp == NULL
- || ((comp->cp_flags & CP_ORIGINAL_TEXT) == 0 && ++i == 2)) {
+ if (compl == NULL || (!match_at_original_text(compl) && ++i == 2)) {
break;
}
- comp = comp->cp_next;
- } while (comp != compl_first_match);
+ compl = compl->cp_next;
+ } while (!is_first_match(compl));
- if (strstr((char *)p_cot, "menuone") != NULL) {
+ if (strstr(p_cot, "menuone") != NULL) {
return i >= 1;
}
return i >= 2;
@@ -951,6 +1080,8 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
return dict;
}
+/// Trigger the CompleteChanged autocmd event. Invoked each time the Insert mode
+/// completion menu is changed.
static void trigger_complete_changed_event(int cur)
{
static bool recursive = false;
@@ -979,141 +1110,151 @@ static void trigger_complete_changed_event(int cur)
restore_v_event(v_event, &save_v_event);
}
-/// Show the popup menu for the list of matches.
-/// Also adjusts "compl_shown_match" to an entry that is actually displayed.
-void ins_compl_show_pum(void)
+/// Build a popup menu to show the completion matches.
+///
+/// @return the popup menu entry that should be selected,
+/// -1 if nothing should be selected.
+static int ins_compl_build_pum(void)
{
- compl_T *compl;
- compl_T *shown_compl = NULL;
- bool did_find_shown_match = false;
- bool shown_match_ok = false;
- int i;
- int cur = -1;
- colnr_T col;
- int lead_len = 0;
- bool array_changed = false;
+ // Need to build the popup menu list.
+ compl_match_arraysize = 0;
+ compl_T *compl = compl_first_match;
- if (!pum_wanted() || !pum_enough_matches()) {
- return;
+ // If it's user complete function and refresh_always,
+ // do not use "compl_leader" as prefix filter.
+ if (ins_compl_need_restart()) {
+ XFREE_CLEAR(compl_leader);
}
- // Dirty hard-coded hack: remove any matchparen highlighting.
- do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif");
+ const int lead_len = compl_leader != NULL ? (int)STRLEN(compl_leader) : 0;
- // Update the screen before drawing the popup menu over it.
- update_screen(0);
-
- if (compl_match_array == NULL) {
- array_changed = true;
- // Need to build the popup menu list.
- compl_match_arraysize = 0;
- compl = compl_first_match;
- // If it's user complete function and refresh_always,
- // do not use "compl_leader" as prefix filter.
- if (ins_compl_need_restart()) {
- XFREE_CLEAR(compl_leader);
- }
- if (compl_leader != NULL) {
- lead_len = (int)STRLEN(compl_leader);
- }
- do {
- if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
- && (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
- compl_match_arraysize++;
- }
- compl = compl->cp_next;
- } while (compl != NULL && compl != compl_first_match);
- if (compl_match_arraysize == 0) {
- return;
+ do {
+ if (!match_at_original_text(compl)
+ && (compl_leader == NULL
+ || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
+ compl_match_arraysize++;
}
+ compl = compl->cp_next;
+ } while (compl != NULL && !is_first_match(compl));
- assert(compl_match_arraysize >= 0);
- compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
- // If the current match is the original text don't find the first
- // match after it, don't highlight anything.
- if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) {
- shown_match_ok = true;
- }
+ if (compl_match_arraysize == 0) {
+ return -1;
+ }
- i = 0;
- compl = compl_first_match;
- do {
- if ((compl->cp_flags & CP_ORIGINAL_TEXT) == 0
- && (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
- if (!shown_match_ok) {
- if (compl == compl_shown_match || did_find_shown_match) {
- // This item is the shown match or this is the
- // first displayed item after the shown match.
- compl_shown_match = compl;
- did_find_shown_match = true;
- shown_match_ok = true;
- } else {
- // Remember this displayed match for when the
- // shown match is just below it.
- shown_compl = compl;
- }
- cur = i;
- }
+ assert(compl_match_arraysize >= 0);
+ compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
- if (compl->cp_text[CPT_ABBR] != NULL) {
- compl_match_array[i].pum_text =
- compl->cp_text[CPT_ABBR];
- } else {
- compl_match_array[i].pum_text = compl->cp_str;
- }
- compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
- compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
- if (compl->cp_text[CPT_MENU] != NULL) {
- compl_match_array[i++].pum_extra =
- compl->cp_text[CPT_MENU];
+ // If the current match is the original text don't find the first
+ // match after it, don't highlight anything.
+ bool shown_match_ok = match_at_original_text(compl_shown_match);
+
+ compl_T *shown_compl = NULL;
+ bool did_find_shown_match = false;
+ int cur = -1;
+ int i = 0;
+ compl = compl_first_match;
+ do {
+ if (!match_at_original_text(compl)
+ && (compl_leader == NULL
+ || ins_compl_equal(compl, compl_leader, (size_t)lead_len))) {
+ if (!shown_match_ok) {
+ if (compl == compl_shown_match || did_find_shown_match) {
+ // This item is the shown match or this is the
+ // first displayed item after the shown match.
+ compl_shown_match = compl;
+ did_find_shown_match = true;
+ shown_match_ok = true;
} else {
- compl_match_array[i++].pum_extra = compl->cp_fname;
+ // Remember this displayed match for when the
+ // shown match is just below it.
+ shown_compl = compl;
}
+ cur = i;
}
- if (compl == compl_shown_match) {
- did_find_shown_match = true;
+ if (compl->cp_text[CPT_ABBR] != NULL) {
+ compl_match_array[i].pum_text = compl->cp_text[CPT_ABBR];
+ } else {
+ compl_match_array[i].pum_text = compl->cp_str;
+ }
+ compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
+ compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
+ if (compl->cp_text[CPT_MENU] != NULL) {
+ compl_match_array[i++].pum_extra = compl->cp_text[CPT_MENU];
+ } else {
+ compl_match_array[i++].pum_extra = compl->cp_fname;
+ }
+ }
- // When the original text is the shown match don't set
- // compl_shown_match.
- if (compl->cp_flags & CP_ORIGINAL_TEXT) {
- shown_match_ok = true;
- }
+ if (compl == compl_shown_match) {
+ did_find_shown_match = true;
- if (!shown_match_ok && shown_compl != NULL) {
- // The shown match isn't displayed, set it to the
- // previously displayed match.
- compl_shown_match = shown_compl;
- shown_match_ok = true;
- }
+ // When the original text is the shown match don't set
+ // compl_shown_match.
+ if (match_at_original_text(compl)) {
+ shown_match_ok = true;
}
- compl = compl->cp_next;
- } while (compl != NULL && compl != compl_first_match);
- if (!shown_match_ok) { // no displayed match at all
- cur = -1;
+ if (!shown_match_ok && shown_compl != NULL) {
+ // The shown match isn't displayed, set it to the
+ // previously displayed match.
+ compl_shown_match = shown_compl;
+ shown_match_ok = true;
+ }
}
+ compl = compl->cp_next;
+ } while (compl != NULL && !is_first_match(compl));
+
+ if (!shown_match_ok) { // no displayed match at all
+ cur = -1;
+ }
+
+ return cur;
+}
+
+/// Show the popup menu for the list of matches.
+/// Also adjusts "compl_shown_match" to an entry that is actually displayed.
+void ins_compl_show_pum(void)
+{
+ if (!pum_wanted() || !pum_enough_matches()) {
+ return;
+ }
+
+ // Dirty hard-coded hack: remove any matchparen highlighting.
+ do_cmdline_cmd("if exists('g:loaded_matchparen')|3match none|endif");
+
+ // Update the screen before drawing the popup menu over it.
+ update_screen(0);
+
+ int cur = -1;
+ bool array_changed = false;
+
+ if (compl_match_array == NULL) {
+ array_changed = true;
+ // Need to build the popup menu list.
+ cur = ins_compl_build_pum();
} else {
// popup menu already exists, only need to find the current item.
- for (i = 0; i < compl_match_arraysize; i++) {
+ for (int i = 0; i < compl_match_arraysize; i++) {
if (compl_match_array[i].pum_text == compl_shown_match->cp_str
- || compl_match_array[i].pum_text
- == compl_shown_match->cp_text[CPT_ABBR]) {
+ || compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i;
break;
}
}
}
+ if (compl_match_array == NULL) {
+ return;
+ }
+
// In Replace mode when a $ is displayed at the end of the line only
// part of the screen would be updated. We do need to redraw here.
dollar_vcol = -1;
// Compute the screen column of the start of the completed text.
// Use the cursor to get all wrapping and other settings right.
- col = curwin->w_cursor.col;
+ const colnr_T col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
pum_selected_item = cur;
pum_display(compl_match_array, compl_match_arraysize, cur, array_changed, 0);
@@ -1127,8 +1268,8 @@ void ins_compl_show_pum(void)
#define DICT_FIRST (1) ///< use just first element in "dict"
#define DICT_EXACT (2) ///< "dict" is the exact name of a file
-/// Add any identifiers that match the given pattern in the list of dictionary
-/// files "dict_start" to the list of completions.
+/// Add any identifiers that match the given pattern "pat" in the list of
+/// dictionary files "dict_start" to the list of completions.
///
/// @param flags DICT_FIRST and/or DICT_EXACT
/// @param thesaurus Thesaurus completion
@@ -1229,6 +1370,54 @@ theend:
xfree(buf);
}
+/// Add all the words in the line "*buf_arg" from the thesaurus file "fname"
+/// skipping the word at 'skip_word'.
+///
+/// @return OK on success.
+static int thesaurus_add_words_in_line(char *fname, char_u **buf_arg, int dir, char_u *skip_word)
+{
+ int status = OK;
+
+ // Add the other matches on the line
+ char_u *ptr = *buf_arg;
+ while (!got_int) {
+ // Find start of the next word. Skip white
+ // space and punctuation.
+ ptr = find_word_start(ptr);
+ if (*ptr == NUL || *ptr == NL) {
+ break;
+ }
+ char_u *wstart = ptr;
+
+ // Find end of the word.
+ // Japanese words may have characters in
+ // different classes, only separate words
+ // with single-byte non-word characters.
+ while (*ptr != NUL) {
+ const int l = utfc_ptr2len((const char *)ptr);
+
+ if (l < 2 && !vim_iswordc(*ptr)) {
+ break;
+ }
+ ptr += l;
+ }
+
+ // Add the word. Skip the regexp match.
+ if (wstart != skip_word) {
+ status = ins_compl_add_infercase(wstart, (int)(ptr - wstart), p_ic,
+ (char_u *)fname, dir, false);
+ if (status == FAIL) {
+ break;
+ }
+ }
+ }
+
+ *buf_arg = ptr;
+ return status;
+}
+
+/// Process "count" dictionary/thesaurus "files" and add the text matching
+/// "regmatch".
static void ins_compl_files(int count, char **files, int thesaurus, int flags, regmatch_T *regmatch,
char_u *buf, Direction *dir)
FUNC_ATTR_NONNULL_ARG(2, 7)
@@ -1253,8 +1442,7 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
// Read dictionary file line by line.
// Check each line for a match.
- while (!got_int && !compl_interrupted
- && !vim_fgets(buf, LSIZE, fp)) {
+ while (!got_int && !compl_interrupted && !vim_fgets(buf, LSIZE, fp)) {
ptr = buf;
while (vim_regexec(regmatch, (char *)buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
@@ -1267,38 +1455,10 @@ static void ins_compl_files(int count, char **files, int thesaurus, int flags, r
(int)(ptr - regmatch->startp[0]),
p_ic, (char_u *)files[i], *dir, false);
if (thesaurus) {
- char_u *wstart;
-
- // Add the other matches on the line
+ // For a thesaurus, add all the words in the line
ptr = buf;
- while (!got_int) {
- // Find start of the next word. Skip white
- // space and punctuation.
- ptr = find_word_start(ptr);
- if (*ptr == NUL || *ptr == NL) {
- break;
- }
- wstart = ptr;
-
- // Find end of the word.
- // Japanese words may have characters in
- // different classes, only separate words
- // with single-byte non-word characters.
- while (*ptr != NUL) {
- const int l = utfc_ptr2len((char *)ptr);
-
- if (l < 2 && !vim_iswordc(*ptr)) {
- break;
- }
- ptr += l;
- }
-
- // Add the word. Skip the regexp match.
- if (wstart != regmatch->startp[0]) {
- add_r = ins_compl_add_infercase(wstart, (int)(ptr - wstart),
- p_ic, (char_u *)files[i], *dir, false);
- }
- }
+ add_r = thesaurus_add_words_in_line(files[i], &ptr, *dir,
+ regmatch->startp[0]);
}
if (add_r == OK) {
// if dir was BACKWARD then honor it just once
@@ -1389,12 +1549,13 @@ static void ins_compl_free(void)
}
tv_clear(&match->cp_user_data);
xfree(match);
- } while (compl_curr_match != NULL && compl_curr_match != compl_first_match);
+ } while (compl_curr_match != NULL && !is_first_match(compl_curr_match));
compl_first_match = compl_curr_match = NULL;
compl_shown_match = NULL;
compl_old_match = NULL;
}
+/// Reset/clear the completion state.
void ins_compl_clear(void)
{
compl_cont_status = 0;
@@ -1448,6 +1609,12 @@ colnr_T ins_compl_col(void)
return compl_col;
}
+/// Return the length in bytes of the text being completed
+int ins_compl_len(void)
+{
+ return compl_length;
+}
+
/// Delete one character before the cursor and show the subset of the matches
/// that match the word that is now before the cursor.
/// Returns the character to be used, NUL if the work is done and another char
@@ -1483,12 +1650,12 @@ int ins_compl_bs(void)
xfree(compl_leader);
compl_leader = vim_strnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
+
ins_compl_new_leader();
if (compl_shown_match != NULL) {
// Make sure current match is not a hidden item.
compl_curr_match = compl_shown_match;
}
-
return NUL;
}
@@ -1511,7 +1678,7 @@ static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_bytes((char *)compl_leader + get_compl_len());
compl_used_match = false;
if (compl_started) {
@@ -1565,7 +1732,7 @@ void ins_compl_addleader(int c)
utf_char2bytes(c, (char *)buf);
buf[cc] = NUL;
- ins_char_bytes((char_u *)buf, (size_t)cc);
+ ins_char_bytes(buf, (size_t)cc);
} else {
ins_char(c);
}
@@ -1601,13 +1768,13 @@ static void ins_compl_set_original_text(char_u *str)
FUNC_ATTR_NONNULL_ALL
{
// Replace the original text entry.
- // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly be
- // at the last item for backward completion
- if (compl_first_match->cp_flags & CP_ORIGINAL_TEXT) { // safety check
+ // The CP_ORIGINAL_TEXT flag is either at the first item or might possibly
+ // be at the last item for backward completion
+ if (match_at_original_text(compl_first_match)) { // safety check
xfree(compl_first_match->cp_str);
compl_first_match->cp_str = vim_strsave(str);
} else if (compl_first_match->cp_prev != NULL
- && (compl_first_match->cp_prev->cp_flags & CP_ORIGINAL_TEXT)) {
+ && match_at_original_text(compl_first_match->cp_prev)) {
xfree(compl_first_match->cp_prev->cp_str);
compl_first_match->cp_prev->cp_str = vim_strsave(str);
}
@@ -1626,20 +1793,20 @@ void ins_compl_addfrommatch(void)
if ((int)STRLEN(p) <= len) { // the match is too short
// When still at the original match use the first entry that matches
// the leader.
- if (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) {
- p = NULL;
- for (cp = compl_shown_match->cp_next; cp != NULL
- && cp != compl_first_match; cp = cp->cp_next) {
- if (compl_leader == NULL
- || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) {
- p = cp->cp_str;
- break;
- }
- }
- if (p == NULL || (int)STRLEN(p) <= len) {
- return;
+ if (!match_at_original_text(compl_shown_match)) {
+ return;
+ }
+
+ p = NULL;
+ for (cp = compl_shown_match->cp_next; cp != NULL
+ && !is_first_match(cp); cp = cp->cp_next) {
+ if (compl_leader == NULL
+ || ins_compl_equal(cp, compl_leader, STRLEN(compl_leader))) {
+ p = cp->cp_str;
+ break;
}
- } else {
+ }
+ if (p == NULL || (int)STRLEN(p) <= len) {
return;
}
}
@@ -1648,6 +1815,249 @@ void ins_compl_addfrommatch(void)
ins_compl_addleader(c);
}
+/// Set the CTRL-X completion mode based on the key "c" typed after a CTRL-X.
+/// Uses the global variables: ctrl_x_mode, edit_submode, edit_submode_pre,
+/// compl_cont_mode and compl_cont_status.
+///
+/// @return true when the character is not to be inserted.
+static bool set_ctrl_x_mode(const int c)
+{
+ bool retval = false;
+
+ switch (c) {
+ case Ctrl_E:
+ case Ctrl_Y:
+ // scroll the window one line up or down
+ ctrl_x_mode = CTRL_X_SCROLL;
+ if (!(State & REPLACE_FLAG)) {
+ edit_submode = _(" (insert) Scroll (^E/^Y)");
+ } else {
+ edit_submode = _(" (replace) Scroll (^E/^Y)");
+ }
+ edit_submode_pre = NULL;
+ showmode();
+ break;
+ case Ctrl_L:
+ // complete whole line
+ ctrl_x_mode = CTRL_X_WHOLE_LINE;
+ break;
+ case Ctrl_F:
+ // complete filenames
+ ctrl_x_mode = CTRL_X_FILES;
+ break;
+ case Ctrl_K:
+ // complete words from a dictionary
+ ctrl_x_mode = CTRL_X_DICTIONARY;
+ break;
+ case Ctrl_R:
+ // Register insertion without exiting CTRL-X mode
+ // Simply allow ^R to happen without affecting ^X mode
+ break;
+ case Ctrl_T:
+ // complete words from a thesaurus
+ ctrl_x_mode = CTRL_X_THESAURUS;
+ break;
+ case Ctrl_U:
+ // user defined completion
+ ctrl_x_mode = CTRL_X_FUNCTION;
+ break;
+ case Ctrl_O:
+ // omni completion
+ ctrl_x_mode = CTRL_X_OMNI;
+ break;
+ case 's':
+ case Ctrl_S:
+ // complete spelling suggestions
+ ctrl_x_mode = CTRL_X_SPELL;
+ emsg_off++; // Avoid getting the E756 error twice.
+ spell_back_to_badword();
+ emsg_off--;
+ break;
+ case Ctrl_RSB:
+ // complete tag names
+ ctrl_x_mode = CTRL_X_TAGS;
+ break;
+ case Ctrl_I:
+ case K_S_TAB:
+ // complete keywords from included files
+ ctrl_x_mode = CTRL_X_PATH_PATTERNS;
+ break;
+ case Ctrl_D:
+ // complete definitions from included files
+ ctrl_x_mode = CTRL_X_PATH_DEFINES;
+ break;
+ case Ctrl_V:
+ case Ctrl_Q:
+ // complete vim commands
+ ctrl_x_mode = CTRL_X_CMDLINE;
+ break;
+ case Ctrl_Z:
+ // stop completion
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ showmode();
+ retval = true;
+ break;
+ case Ctrl_P:
+ case Ctrl_N:
+ // ^X^P means LOCAL expansion if nothing interrupted (eg we
+ // just started ^X mode, or there were enough ^X's to cancel
+ // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
+ // do normal expansion when interrupting a different mode (say
+ // ^X^F^X^P or ^P^X^X^P, see below)
+ // nothing changes if interrupting mode 0, (eg, the flag
+ // doesn't change when going to ADDING mode -- Acevedo
+ if (!(compl_cont_status & CONT_INTRPT)) {
+ compl_cont_status |= CONT_LOCAL;
+ } else if (compl_cont_mode != 0) {
+ compl_cont_status &= ~CONT_LOCAL;
+ }
+ FALLTHROUGH;
+ default:
+ // If we have typed at least 2 ^X's... for modes != 0, we set
+ // compl_cont_status = 0 (eg, as if we had just started ^X
+ // mode).
+ // For mode 0, we set "compl_cont_mode" to an impossible
+ // value, in both cases ^X^X can be used to restart the same
+ // mode (avoiding ADDING mode).
+ // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
+ // 'complete' and local ^P expansions respectively.
+ // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
+ // mode -- Acevedo
+ if (c == Ctrl_X) {
+ if (compl_cont_mode != 0) {
+ compl_cont_status = 0;
+ } else {
+ compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
+ }
+ }
+ ctrl_x_mode = CTRL_X_NORMAL;
+ edit_submode = NULL;
+ showmode();
+ break;
+ }
+
+ return retval;
+}
+
+/// Stop insert completion mode
+static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
+{
+ // Get here when we have finished typing a sequence of ^N and
+ // ^P or other completion characters in CTRL-X mode. Free up
+ // memory that was used, and make sure we can redo the insert.
+ if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
+ // If any of the original typed text has been changed, eg when
+ // ignorecase is set, we must add back-spaces to the redo
+ // buffer. We add as few as necessary to delete just the part
+ // of the original text that has changed.
+ // When using the longest match, edited the match or used
+ // CTRL-E then don't use the current match.
+ char_u *ptr;
+ if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
+ ptr = compl_curr_match->cp_str;
+ } else {
+ ptr = NULL;
+ }
+ ins_compl_fixRedoBufForLeader(ptr);
+ }
+
+ bool want_cindent = (get_can_cindent() && cindent_on());
+
+ // When completing whole lines: fix indent for 'cindent'.
+ // Otherwise, break line if it's too long.
+ if (compl_cont_mode == CTRL_X_WHOLE_LINE) {
+ // re-indent the current line
+ if (want_cindent) {
+ do_c_expr_indent();
+ want_cindent = false; // don't do it again
+ }
+ } else {
+ const int prev_col = curwin->w_cursor.col;
+
+ // put the cursor on the last char, for 'tw' formatting
+ if (prev_col > 0) {
+ dec_cursor();
+ }
+
+ // only format when something was inserted
+ if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
+ insertchar(NUL, 0, -1);
+ }
+
+ if (prev_col > 0
+ && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) {
+ inc_cursor();
+ }
+ }
+
+ // If the popup menu is displayed pressing CTRL-Y means accepting
+ // the selection without inserting anything. When
+ // compl_enter_selects is set the Enter key does the same.
+ if ((c == Ctrl_Y || (compl_enter_selects
+ && (c == CAR || c == K_KENTER || c == NL)))
+ && pum_visible()) {
+ retval = true;
+ }
+
+ // CTRL-E means completion is Ended, go back to the typed text.
+ // but only do this, if the Popup is still visible
+ if (c == Ctrl_E) {
+ ins_compl_delete();
+ char_u *p = NULL;
+ if (compl_leader != NULL) {
+ p = compl_leader;
+ } else if (compl_first_match != NULL) {
+ p = compl_orig_text;
+ }
+ if (p != NULL) {
+ const int compl_len = get_compl_len();
+ const int len = (int)STRLEN(p);
+ if (len > compl_len) {
+ ins_bytes_len((char *)p + compl_len, (size_t)(len - compl_len));
+ }
+ }
+ retval = true;
+ }
+
+ auto_format(false, true);
+
+ // Trigger the CompleteDonePre event to give scripts a chance to
+ // act upon the completion before clearing the info, and restore
+ // ctrl_x_mode, so that complete_info() can be used.
+ ctrl_x_mode = prev_mode;
+ ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
+
+ ins_compl_free();
+ compl_started = false;
+ compl_matches = 0;
+ if (!shortmess(SHM_COMPLETIONMENU)) {
+ msg_clr_cmdline(); // necessary for "noshowmode"
+ }
+ ctrl_x_mode = CTRL_X_NORMAL;
+ compl_enter_selects = false;
+ if (edit_submode != NULL) {
+ edit_submode = NULL;
+ showmode();
+ }
+
+ if (c == Ctrl_C && cmdwin_type != 0) {
+ // Avoid the popup menu remains displayed when leaving the
+ // command line window.
+ update_screen(0);
+ }
+
+ // Indent now if a key was typed that is in 'cinkeys'.
+ if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) {
+ do_c_expr_indent();
+ }
+ // Trigger the CompleteDone event to give scripts a chance to act
+ // upon the end of completion.
+ ins_apply_autocmds(EVENT_COMPLETEDONE);
+
+ return retval;
+}
+
/// Prepare for Insert mode completion, or stop it.
/// Called just after typing a character in Insert mode.
///
@@ -1656,7 +2066,6 @@ void ins_compl_addfrommatch(void)
/// @return true when the character is not to be inserted;
bool ins_compl_prep(int c)
{
- char_u *ptr;
bool retval = false;
const int prev_mode = ctrl_x_mode;
@@ -1696,111 +2105,14 @@ bool ins_compl_prep(int c)
// Set "compl_get_longest" when finding the first matches.
if (ctrl_x_mode_not_defined_yet()
|| (ctrl_x_mode_normal() && !compl_started)) {
- compl_get_longest = (strstr((char *)p_cot, "longest") != NULL);
+ compl_get_longest = (strstr(p_cot, "longest") != NULL);
compl_used_match = true;
}
if (ctrl_x_mode_not_defined_yet()) {
// We have just typed CTRL-X and aren't quite sure which CTRL-X mode
// it will be yet. Now we decide.
- switch (c) {
- case Ctrl_E:
- case Ctrl_Y:
- ctrl_x_mode = CTRL_X_SCROLL;
- if (!(State & REPLACE_FLAG)) {
- edit_submode = (char_u *)_(" (insert) Scroll (^E/^Y)");
- } else {
- edit_submode = (char_u *)_(" (replace) Scroll (^E/^Y)");
- }
- edit_submode_pre = NULL;
- showmode();
- break;
- case Ctrl_L:
- ctrl_x_mode = CTRL_X_WHOLE_LINE;
- break;
- case Ctrl_F:
- ctrl_x_mode = CTRL_X_FILES;
- break;
- case Ctrl_K:
- ctrl_x_mode = CTRL_X_DICTIONARY;
- break;
- case Ctrl_R:
- // Simply allow ^R to happen without affecting ^X mode
- break;
- case Ctrl_T:
- ctrl_x_mode = CTRL_X_THESAURUS;
- break;
- case Ctrl_U:
- ctrl_x_mode = CTRL_X_FUNCTION;
- break;
- case Ctrl_O:
- ctrl_x_mode = CTRL_X_OMNI;
- break;
- case 's':
- case Ctrl_S:
- ctrl_x_mode = CTRL_X_SPELL;
- emsg_off++; // Avoid getting the E756 error twice.
- spell_back_to_badword();
- emsg_off--;
- break;
- case Ctrl_RSB:
- ctrl_x_mode = CTRL_X_TAGS;
- break;
- case Ctrl_I:
- case K_S_TAB:
- ctrl_x_mode = CTRL_X_PATH_PATTERNS;
- break;
- case Ctrl_D:
- ctrl_x_mode = CTRL_X_PATH_DEFINES;
- break;
- case Ctrl_V:
- case Ctrl_Q:
- ctrl_x_mode = CTRL_X_CMDLINE;
- break;
- case Ctrl_Z:
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- showmode();
- retval = true;
- break;
- case Ctrl_P:
- case Ctrl_N:
- // ^X^P means LOCAL expansion if nothing interrupted (eg we
- // just started ^X mode, or there were enough ^X's to cancel
- // the previous mode, say ^X^F^X^X^P or ^P^X^X^X^P, see below)
- // do normal expansion when interrupting a different mode (say
- // ^X^F^X^P or ^P^X^X^P, see below)
- // nothing changes if interrupting mode 0, (eg, the flag
- // doesn't change when going to ADDING mode -- Acevedo
- if (!(compl_cont_status & CONT_INTRPT)) {
- compl_cont_status |= CONT_LOCAL;
- } else if (compl_cont_mode != 0) {
- compl_cont_status &= ~CONT_LOCAL;
- }
- FALLTHROUGH;
- default:
- // If we have typed at least 2 ^X's... for modes != 0, we set
- // compl_cont_status = 0 (eg, as if we had just started ^X
- // mode).
- // For mode 0, we set "compl_cont_mode" to an impossible
- // value, in both cases ^X^X can be used to restart the same
- // mode (avoiding ADDING mode).
- // Undocumented feature: In a mode != 0 ^X^P and ^X^X^P start
- // 'complete' and local ^P expansions respectively.
- // In mode 0 an extra ^X is needed since ^X^P goes to ADDING
- // mode -- Acevedo
- if (c == Ctrl_X) {
- if (compl_cont_mode != 0) {
- compl_cont_status = 0;
- } else {
- compl_cont_mode = CTRL_X_NOT_DEFINED_YET;
- }
- }
- ctrl_x_mode = CTRL_X_NORMAL;
- edit_submode = NULL;
- showmode();
- break;
- }
+ retval = set_ctrl_x_mode(c);
} else if (ctrl_x_mode_not_default()) {
// We're already in CTRL-X mode, do we stay in it?
if (!vim_is_ctrl_x_key(c)) {
@@ -1825,107 +2137,7 @@ bool ins_compl_prep(int c)
&& c != Ctrl_R
&& !ins_compl_pum_key(c))
|| ctrl_x_mode == CTRL_X_FINISHED) {
- // Get here when we have finished typing a sequence of ^N and
- // ^P or other completion characters in CTRL-X mode. Free up
- // memory that was used, and make sure we can redo the insert.
- if (compl_curr_match != NULL || compl_leader != NULL || c == Ctrl_E) {
- // If any of the original typed text has been changed, eg when
- // ignorecase is set, we must add back-spaces to the redo
- // buffer. We add as few as necessary to delete just the part
- // of the original text that has changed.
- // When using the longest match, edited the match or used
- // CTRL-E then don't use the current match.
- if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
- } else {
- ptr = NULL;
- }
- ins_compl_fixRedoBufForLeader(ptr);
- }
-
- bool want_cindent = (can_cindent_get() && cindent_on());
-
- // When completing whole lines: fix indent for 'cindent'.
- // Otherwise, break line if it's too long.
- if (compl_cont_mode == CTRL_X_WHOLE_LINE) {
- // re-indent the current line
- if (want_cindent) {
- do_c_expr_indent();
- want_cindent = false; // don't do it again
- }
- } else {
- int prev_col = curwin->w_cursor.col;
-
- // put the cursor on the last char, for 'tw' formatting
- if (prev_col > 0) {
- dec_cursor();
- }
-
- if (!arrow_used && !ins_need_undo_get() && c != Ctrl_E) {
- insertchar(NUL, 0, -1);
- }
-
- if (prev_col > 0
- && get_cursor_line_ptr()[curwin->w_cursor.col] != NUL) {
- inc_cursor();
- }
- }
-
- // If the popup menu is displayed pressing CTRL-Y means accepting
- // the selection without inserting anything. When
- // compl_enter_selects is set the Enter key does the same.
- if ((c == Ctrl_Y || (compl_enter_selects
- && (c == CAR || c == K_KENTER || c == NL)))
- && pum_visible()) {
- retval = true;
- }
-
- // CTRL-E means completion is Ended, go back to the typed text.
- // but only do this, if the Popup is still visible
- if (c == Ctrl_E) {
- ins_compl_delete();
- if (compl_leader != NULL) {
- ins_bytes(compl_leader + get_compl_len());
- } else if (compl_first_match != NULL) {
- ins_bytes(compl_orig_text + get_compl_len());
- }
- retval = true;
- }
-
- auto_format(false, true);
-
- // Trigger the CompleteDonePre event to give scripts a chance to
- // act upon the completion before clearing the info, and restore
- // ctrl_x_mode, so that complete_info() can be used.
- ctrl_x_mode = prev_mode;
- ins_apply_autocmds(EVENT_COMPLETEDONEPRE);
-
- ins_compl_free();
- compl_started = false;
- compl_matches = 0;
- if (!shortmess(SHM_COMPLETIONMENU)) {
- msg_clr_cmdline(); // necessary for "noshowmode"
- }
- ctrl_x_mode = CTRL_X_NORMAL;
- compl_enter_selects = false;
- if (edit_submode != NULL) {
- edit_submode = NULL;
- showmode();
- }
-
- // Avoid the popup menu remains displayed when leaving the
- // command line window.
- if (c == Ctrl_C && cmdwin_type != 0) {
- update_screen(0);
- }
-
- // Indent now if a key was typed that is in 'cinkeys'.
- if (want_cindent && in_cinkeys(KEY_COMPLETE, ' ', inindent(0))) {
- do_c_expr_indent();
- }
- // Trigger the CompleteDone event to give scripts a chance to act
- // upon the end of completion.
- ins_apply_autocmds(EVENT_COMPLETEDONE);
+ retval = ins_compl_stop(c, prev_mode, retval);
}
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act
@@ -2008,16 +2220,16 @@ static buf_T *ins_compl_next_buf(buf_T *buf, int flag)
return buf;
}
-/// Get the user-defined completion function name for completion 'type'
+/// Get the user-defined completion function name for completion "type"
static char_u *get_complete_funcname(int type)
{
switch (type) {
case CTRL_X_FUNCTION:
- return curbuf->b_p_cfu;
+ return (char_u *)curbuf->b_p_cfu;
case CTRL_X_OMNI:
- return curbuf->b_p_ofu;
+ return (char_u *)curbuf->b_p_ofu;
case CTRL_X_THESAURUS:
- return *curbuf->b_p_tsrfu == NUL ? p_tsrfu : curbuf->b_p_tsrfu;
+ return *curbuf->b_p_tsrfu == NUL ? (char_u *)p_tsrfu : (char_u *)curbuf->b_p_tsrfu;
default:
return (char_u *)"";
}
@@ -2139,16 +2351,21 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
}
} else {
word = tv_get_string_chk(tv);
- memset(cptext, 0, sizeof(cptext));
+ CLEAR_FIELD(cptext);
}
if (word == NULL || (!empty && *word == NUL)) {
for (size_t i = 0; i < CPT_COUNT; i++) {
xfree(cptext[i]);
}
+ tv_clear(&user_data);
return FAIL;
}
- return ins_compl_add((char_u *)word, -1, NULL,
- (char_u **)cptext, true, &user_data, dir, flags, dup);
+ int status = ins_compl_add((char_u *)word, -1, NULL, (char_u **)cptext, true,
+ &user_data, dir, flags, dup);
+ if (status != OK) {
+ tv_clear(&user_data);
+ }
+ return status;
}
/// Add completions from a list.
@@ -2254,7 +2471,7 @@ static void set_completion(colnr_T startcol, list_T *list)
}
/// "complete()" function
-void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
if ((State & MODE_INSERT) == 0) {
emsg(_("E785: complete() can only be used in Insert mode"));
@@ -2278,13 +2495,13 @@ void f_complete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "complete_add()" function
-void f_complete_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete_add(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = ins_compl_add_tv(&argvars[0], 0, false);
}
/// "complete_check()" function
-void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete_check(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int saved = RedrawingDisabled;
@@ -2303,18 +2520,19 @@ static char_u *ins_compl_mode(void)
return (char_u *)"";
}
+/// Assign the sequence number to all the completion matches which don't have
+/// one assigned yet.
static void ins_compl_update_sequence_numbers(void)
{
int number = 0;
compl_T *match;
- if (compl_direction == FORWARD) {
+ if (compl_dir_forward()) {
// search backwards for the first valid (!= -1) number.
// This should normally succeed already at the first loop
// cycle, so it's fast!
for (match = compl_curr_match->cp_prev;
- match != NULL && match != compl_first_match;
- match = match->cp_prev) {
+ match != NULL && !is_first_match(match); match = match->cp_prev) {
if (match->cp_number != -1) {
number = match->cp_number;
break;
@@ -2334,8 +2552,7 @@ static void ins_compl_update_sequence_numbers(void)
// number. This should normally succeed already at the
// first loop cycle, so it's fast!
for (match = compl_curr_match->cp_next;
- match != NULL && match != compl_first_match;
- match = match->cp_next) {
+ match != NULL && !is_first_match(match); match = match->cp_next) {
if (match->cp_number != -1) {
number = match->cp_number;
break;
@@ -2404,7 +2621,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
if (ret == OK && compl_first_match != NULL) {
compl_T *match = compl_first_match;
do {
- if (!(match->cp_flags & CP_ORIGINAL_TEXT)) {
+ if (!match_at_original_text(match)) {
dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di);
@@ -2420,7 +2637,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
}
}
match = match->cp_next;
- } while (match != NULL && match != compl_first_match);
+ } while (match != NULL && !is_first_match(match));
}
}
@@ -2439,7 +2656,7 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
}
/// "complete_info()" function
-void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_complete_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
@@ -2462,6 +2679,481 @@ static bool thesaurus_func_complete(int type)
&& (*curbuf->b_p_tsrfu != NUL || *p_tsrfu != NUL);
}
+/// Return value of process_next_cpt_value()
+enum {
+ INS_COMPL_CPT_OK = 1,
+ INS_COMPL_CPT_CONT,
+ INS_COMPL_CPT_END,
+};
+
+/// Process the next 'complete' option value in st->e_cpt.
+///
+/// If successful, the arguments are set as below:
+/// st->cpt - pointer to the next option value in "st->cpt"
+/// compl_type_arg - type of insert mode completion to use
+/// st->found_all - all matches of this type are found
+/// st->ins_buf - search for completions in this buffer
+/// st->first_match_pos - position of the first completion match
+/// st->last_match_pos - position of the last completion match
+/// st->set_match_pos - true if the first match position should be saved to
+/// avoid loops after the search wraps around.
+/// st->dict - name of the dictionary or thesaurus file to search
+/// st->dict_f - flag specifying whether "dict" is an exact file name or not
+///
+/// @return INS_COMPL_CPT_OK if the next value is processed successfully.
+/// INS_COMPL_CPT_CONT to skip the current completion source matching
+/// the "st->e_cpt" option value and process the next matching source.
+/// INS_COMPL_CPT_END if all the values in "st->e_cpt" are processed.
+static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_arg,
+ pos_T *start_match_pos)
+{
+ int compl_type = -1;
+ int status = INS_COMPL_CPT_OK;
+
+ st->found_all = false;
+
+ while (*st->e_cpt == ',' || *st->e_cpt == ' ') {
+ st->e_cpt++;
+ }
+
+ if (*st->e_cpt == '.' && !curbuf->b_scanned) {
+ st->ins_buf = curbuf;
+ st->first_match_pos = *start_match_pos;
+ // Move the cursor back one character so that ^N can match the
+ // word immediately after the cursor.
+ if (ctrl_x_mode_normal() && dec(&st->first_match_pos) < 0) {
+ // Move the cursor to after the last character in the
+ // buffer, so that word at start of buffer is found
+ // correctly.
+ st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count;
+ st->first_match_pos.col = (colnr_T)STRLEN(ml_get(st->first_match_pos.lnum));
+ }
+ st->last_match_pos = st->first_match_pos;
+ compl_type = 0;
+
+ // Remember the first match so that the loop stops when we
+ // wrap and come back there a second time.
+ st->set_match_pos = true;
+ } else if (vim_strchr("buwU", *st->e_cpt) != NULL
+ && (st->ins_buf = ins_compl_next_buf(st->ins_buf, *st->e_cpt)) != curbuf) {
+ // Scan a buffer, but not the current one.
+ if (st->ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer
+ compl_started = true;
+ st->first_match_pos.col = st->last_match_pos.col = 0;
+ st->first_match_pos.lnum = st->ins_buf->b_ml.ml_line_count + 1;
+ st->last_match_pos.lnum = 0;
+ compl_type = 0;
+ } else { // unloaded buffer, scan like dictionary
+ st->found_all = true;
+ if (st->ins_buf->b_fname == NULL) {
+ status = INS_COMPL_CPT_CONT;
+ goto done;
+ }
+ compl_type = CTRL_X_DICTIONARY;
+ st->dict = (char_u *)st->ins_buf->b_fname;
+ st->dict_f = DICT_EXACT;
+ }
+ msg_hist_off = true; // reset in msg_trunc_attr()
+ vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
+ st->ins_buf->b_fname == NULL
+ ? buf_spname(st->ins_buf)
+ : st->ins_buf->b_sfname == NULL
+ ? st->ins_buf->b_fname
+ : st->ins_buf->b_sfname);
+ (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
+ } else if (*st->e_cpt == NUL) {
+ status = INS_COMPL_CPT_END;
+ } else {
+ if (ctrl_x_mode_line_or_eval()) {
+ compl_type = -1;
+ } else if (*st->e_cpt == 'k' || *st->e_cpt == 's') {
+ if (*st->e_cpt == 'k') {
+ compl_type = CTRL_X_DICTIONARY;
+ } else {
+ compl_type = CTRL_X_THESAURUS;
+ }
+ if (*++st->e_cpt != ',' && *st->e_cpt != NUL) {
+ st->dict = (char_u *)st->e_cpt;
+ st->dict_f = DICT_FIRST;
+ }
+ } else if (*st->e_cpt == 'i') {
+ compl_type = CTRL_X_PATH_PATTERNS;
+ } else if (*st->e_cpt == 'd') {
+ compl_type = CTRL_X_PATH_DEFINES;
+ } else if (*st->e_cpt == ']' || *st->e_cpt == 't') {
+ msg_hist_off = true; // reset in msg_trunc_attr()
+ compl_type = CTRL_X_TAGS;
+ vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags."));
+ (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
+ } else {
+ compl_type = -1;
+ }
+
+ // in any case e_cpt is advanced to the next entry
+ (void)copy_option_part(&st->e_cpt, (char *)IObuff, IOSIZE, ",");
+
+ st->found_all = true;
+ if (compl_type == -1) {
+ status = INS_COMPL_CPT_CONT;
+ }
+ }
+
+done:
+ *compl_type_arg = compl_type;
+ return status;
+}
+
+/// Get the next set of identifiers or defines matching "compl_pattern" in
+/// included files.
+static void get_next_include_file_completion(int compl_type)
+{
+ find_pattern_in_path((char_u *)compl_pattern, compl_direction,
+ STRLEN(compl_pattern), false, false,
+ ((compl_type == CTRL_X_PATH_DEFINES
+ && !(compl_cont_status & CONT_SOL))
+ ? FIND_DEFINE : FIND_ANY),
+ 1L, ACTION_EXPAND, 1, MAXLNUM);
+}
+
+/// Get the next set of words matching "compl_pattern" in dictionary or
+/// thesaurus files.
+static void get_next_dict_tsr_completion(int compl_type, char_u *dict, int dict_f)
+{
+ if (thesaurus_func_complete(compl_type)) {
+ expand_by_function(compl_type, (char_u *)compl_pattern);
+ } else {
+ ins_compl_dictionaries(dict != NULL ? dict
+ : (compl_type == CTRL_X_THESAURUS
+ ? (*curbuf->b_p_tsr == NUL ? p_tsr : (char_u *)curbuf->b_p_tsr)
+ : (*curbuf->b_p_dict ==
+ NUL ? (char_u *)p_dict : (char_u *)curbuf->b_p_dict)),
+ (char_u *)compl_pattern,
+ dict != NULL ? dict_f : 0,
+ compl_type == CTRL_X_THESAURUS);
+ }
+}
+
+/// Get the next set of tag names matching "compl_pattern".
+static void get_next_tag_completion(void)
+{
+ // set p_ic according to p_ic, p_scs and pat for find_tags().
+ const int save_p_ic = p_ic;
+ p_ic = ignorecase((char_u *)compl_pattern);
+
+ // Find up to TAG_MANY matches. Avoids that an enormous number
+ // of matches is found when compl_pattern is empty
+ g_tag_at_cursor = true;
+ char **matches;
+ int num_matches;
+ if (find_tags(compl_pattern, &num_matches, &matches,
+ TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
+ | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
+ TAG_MANY, curbuf->b_ffname) == OK && num_matches > 0) {
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ }
+ g_tag_at_cursor = false;
+ p_ic = save_p_ic;
+}
+
+/// Get the next set of filename matching "compl_pattern".
+static void get_next_filename_completion(void)
+{
+ char **matches;
+ int num_matches;
+ if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+ EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
+ return;
+ }
+
+ // May change home directory back to "~".
+ tilde_replace((char_u *)compl_pattern, num_matches, matches);
+#ifdef BACKSLASH_IN_FILENAME
+ if (curbuf->b_p_csl[0] != NUL) {
+ for (int i = 0; i < num_matches; i++) {
+ char_u *ptr = matches[i];
+ while (*ptr != NUL) {
+ if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
+ *ptr = '/';
+ } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
+ *ptr = '\\';
+ }
+ ptr += utfc_ptr2len(ptr);
+ }
+ }
+ }
+#endif
+ ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
+}
+
+/// Get the next set of command-line completions matching "compl_pattern".
+static void get_next_cmdline_completion(void)
+{
+ char **matches;
+ int num_matches;
+ if (expand_cmdline(&compl_xp, (char_u *)compl_pattern,
+ (int)STRLEN(compl_pattern),
+ &num_matches, &matches) == EXPAND_OK) {
+ ins_compl_add_matches(num_matches, matches, false);
+ }
+}
+
+/// Get the next set of spell suggestions matching "compl_pattern".
+static void get_next_spell_completion(linenr_T lnum)
+{
+ char **matches;
+ int num_matches = expand_spelling(lnum, (char_u *)compl_pattern, &matches);
+ if (num_matches > 0) {
+ ins_compl_add_matches(num_matches, matches, p_ic);
+ } else {
+ xfree(matches);
+ }
+}
+
+/// Return the next word or line from buffer "ins_buf" at position
+/// "cur_match_pos" for completion. The length of the match is set in "len".
+/// @param ins_buf buffer being scanned
+/// @param cur_match_pos current match position
+/// @param match_len
+/// @param cont_s_ipos next ^X<> will set initial_pos
+static char_u *ins_comp_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_pos, int *match_len,
+ bool *cont_s_ipos)
+{
+ *match_len = 0;
+ char_u *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum, false) + cur_match_pos->col;
+ int len;
+ if (ctrl_x_mode_line_or_eval()) {
+ if (compl_status_adding()) {
+ if (cur_match_pos->lnum >= ins_buf->b_ml.ml_line_count) {
+ return NULL;
+ }
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
+ if (!p_paste) {
+ ptr = (char_u *)skipwhite((char *)ptr);
+ }
+ }
+ len = (int)STRLEN(ptr);
+ } else {
+ char_u *tmp_ptr = ptr;
+
+ if (compl_status_adding() && compl_length <= (int)STRLEN(tmp_ptr)) {
+ tmp_ptr += compl_length;
+ // Skip if already inside a word.
+ if (vim_iswordp(tmp_ptr)) {
+ return NULL;
+ }
+ // Find start of next word.
+ tmp_ptr = find_word_start(tmp_ptr);
+ }
+ // Find end of this word.
+ tmp_ptr = find_word_end(tmp_ptr);
+ len = (int)(tmp_ptr - ptr);
+
+ if (compl_status_adding() && len == compl_length) {
+ if (cur_match_pos->lnum < ins_buf->b_ml.ml_line_count) {
+ // Try next line, if any. the new word will be "join" as if the
+ // normal command "J" was used. IOSIZE is always greater than
+ // compl_length, so the next STRNCPY always works -- Acevedo
+ STRNCPY(IObuff, ptr, len); // NOLINT(runtime/printf)
+ ptr = ml_get_buf(ins_buf, cur_match_pos->lnum + 1, false);
+ tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr);
+ // Find start of next word.
+ tmp_ptr = find_word_start(tmp_ptr);
+ // Find end of next word.
+ tmp_ptr = find_word_end(tmp_ptr);
+ if (tmp_ptr > ptr) {
+ if (*ptr != ')' && IObuff[len - 1] != TAB) {
+ if (IObuff[len - 1] != ' ') {
+ IObuff[len++] = ' ';
+ }
+ // IObuf =~ "\k.* ", thus len >= 2
+ if (p_js
+ && (IObuff[len - 2] == '.'
+ || IObuff[len - 2] == '?'
+ || IObuff[len - 2] == '!')) {
+ IObuff[len++] = ' ';
+ }
+ }
+ // copy as much as possible of the new word
+ if (tmp_ptr - ptr >= IOSIZE - len) {
+ tmp_ptr = ptr + IOSIZE - len - 1;
+ }
+ STRLCPY(IObuff + len, ptr, IOSIZE - len);
+ len += (int)(tmp_ptr - ptr);
+ *cont_s_ipos = true;
+ }
+ IObuff[len] = NUL;
+ ptr = IObuff;
+ }
+ if (len == compl_length) {
+ return NULL;
+ }
+ }
+ }
+
+ *match_len = len;
+ return ptr;
+}
+
+/// Get the next set of words matching "compl_pattern" for default completion(s)
+/// (normal ^P/^N and ^X^L).
+/// Search for "compl_pattern" in the buffer "st->ins_buf" starting from the
+/// position "st->start_pos" in the "compl_direction" direction. If
+/// "st->set_match_pos" is true, then set the "st->first_match_pos" and
+/// "st->last_match_pos".
+///
+/// @return OK if a new next match is found, otherwise FAIL.
+static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
+{
+ // If 'infercase' is set, don't use 'smartcase' here
+ const int save_p_scs = p_scs;
+ assert(st->ins_buf);
+ if (st->ins_buf->b_p_inf) {
+ p_scs = false;
+ }
+
+ // Buffers other than curbuf are scanned from the beginning or the
+ // end but never from the middle, thus setting nowrapscan in this
+ // buffers is a good idea, on the other hand, we always set
+ // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
+ const int save_p_ws = p_ws;
+ if (st->ins_buf != curbuf) {
+ p_ws = false;
+ } else if (*st->e_cpt == '.') {
+ p_ws = true;
+ }
+ bool looped_around = false;
+ int found_new_match = FAIL;
+ for (;;) {
+ bool cont_s_ipos = false;
+
+ msg_silent++; // Don't want messages for wrapscan.
+ // ctrl_x_mode_line_or_eval() || word-wise search that
+ // has added a word that was at the beginning of the line.
+ if (ctrl_x_mode_line_or_eval() || (compl_cont_status & CONT_SOL)) {
+ found_new_match = search_for_exact_line(st->ins_buf, st->cur_match_pos,
+ compl_direction, (char_u *)compl_pattern);
+ } else {
+ found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
+ NULL, compl_direction, (char_u *)compl_pattern, 1L,
+ SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
+ }
+ msg_silent--;
+ if (!compl_started || st->set_match_pos) {
+ // set "compl_started" even on fail
+ compl_started = true;
+ st->first_match_pos = *st->cur_match_pos;
+ st->last_match_pos = *st->cur_match_pos;
+ st->set_match_pos = false;
+ } else if (st->first_match_pos.lnum == st->last_match_pos.lnum
+ && st->first_match_pos.col == st->last_match_pos.col) {
+ found_new_match = FAIL;
+ } else if (compl_dir_forward()
+ && (st->prev_match_pos.lnum > st->cur_match_pos->lnum
+ || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
+ && st->prev_match_pos.col >= st->cur_match_pos->col))) {
+ if (looped_around) {
+ found_new_match = FAIL;
+ } else {
+ looped_around = true;
+ }
+ } else if (!compl_dir_forward()
+ && (st->prev_match_pos.lnum < st->cur_match_pos->lnum
+ || (st->prev_match_pos.lnum == st->cur_match_pos->lnum
+ && st->prev_match_pos.col <= st->cur_match_pos->col))) {
+ if (looped_around) {
+ found_new_match = FAIL;
+ } else {
+ looped_around = true;
+ }
+ }
+ st->prev_match_pos = *st->cur_match_pos;
+ if (found_new_match == FAIL) {
+ break;
+ }
+
+ // when ADDING, the text before the cursor matches, skip it
+ if (compl_status_adding() && st->ins_buf == curbuf
+ && start_pos->lnum == st->cur_match_pos->lnum
+ && start_pos->col == st->cur_match_pos->col) {
+ continue;
+ }
+ int len;
+ char_u *ptr = ins_comp_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
+ &len, &cont_s_ipos);
+ if (ptr == NULL) {
+ continue;
+ }
+ if (ins_compl_add_infercase(ptr, len, p_ic,
+ st->ins_buf == curbuf ? NULL : (char_u *)st->ins_buf->b_sfname,
+ 0, cont_s_ipos) != NOTDONE) {
+ found_new_match = OK;
+ break;
+ }
+ }
+ p_scs = save_p_scs;
+ p_ws = save_p_ws;
+
+ return found_new_match;
+}
+
+/// get the next set of completion matches for "type".
+/// @return true if a new match is found, otherwise false.
+static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_T *ini)
+{
+ int found_new_match = FAIL;
+
+ switch (type) {
+ case -1:
+ break;
+ case CTRL_X_PATH_PATTERNS:
+ case CTRL_X_PATH_DEFINES:
+ get_next_include_file_completion(type);
+ break;
+
+ case CTRL_X_DICTIONARY:
+ case CTRL_X_THESAURUS:
+ get_next_dict_tsr_completion(type, st->dict, st->dict_f);
+ st->dict = NULL;
+ break;
+
+ case CTRL_X_TAGS:
+ get_next_tag_completion();
+ break;
+
+ case CTRL_X_FILES:
+ get_next_filename_completion();
+ break;
+
+ case CTRL_X_CMDLINE:
+ case CTRL_X_CMDLINE_CTRL_X:
+ get_next_cmdline_completion();
+ break;
+
+ case CTRL_X_FUNCTION:
+ case CTRL_X_OMNI:
+ expand_by_function(type, (char_u *)compl_pattern);
+ break;
+
+ case CTRL_X_SPELL:
+ get_next_spell_completion(st->first_match_pos.lnum);
+ break;
+
+ default: // normal ^P/^N and ^X^L
+ found_new_match = get_next_default_completion(st, ini);
+ if (found_new_match == FAIL && st->ins_buf == curbuf) {
+ st->found_all = true;
+ }
+ }
+
+ // check if compl_curr_match has changed, (e.g. other type of
+ // expansion added something)
+ if (type != 0 && compl_curr_match != compl_old_match) {
+ found_new_match = OK;
+ }
+
+ return found_new_match;
+}
+
/// Get the next expansion(s), using "compl_pattern".
/// The search starts at position "ini" in curbuf and in the direction
/// compl_direction.
@@ -2471,28 +3163,10 @@ static bool thesaurus_func_complete(int type)
/// Return the total number of matches or -1 if still unknown -- Acevedo
static int ins_compl_get_exp(pos_T *ini)
{
- static pos_T first_match_pos;
- static pos_T last_match_pos;
- static char_u *e_cpt = (char_u *)""; // curr. entry in 'complete'
- static bool found_all = false; // Found all matches of a
- // certain type.
- static buf_T *ins_buf = NULL; // buffer being scanned
-
- pos_T *pos;
- char **matches;
- int save_p_scs;
- bool save_p_ws;
- int save_p_ic;
+ static ins_compl_next_state_T st;
int i;
- int num_matches;
- int len;
int found_new_match;
int type = ctrl_x_mode;
- char_u *ptr;
- char_u *dict = NULL;
- int dict_f = 0;
- bool set_match_pos;
- pos_T prev_pos = { 0, 0, 0 };
assert(curbuf != NULL);
@@ -2500,112 +3174,34 @@ static int ins_compl_get_exp(pos_T *ini)
FOR_ALL_BUFFERS(buf) {
buf->b_scanned = false;
}
- found_all = false;
- ins_buf = curbuf;
- e_cpt = (compl_cont_status & CONT_LOCAL)
- ? (char_u *)"." : curbuf->b_p_cpt;
- last_match_pos = first_match_pos = *ini;
- } else if (ins_buf != curbuf && !buf_valid(ins_buf)) {
- ins_buf = curbuf; // In case the buffer was wiped out.
+ st.found_all = false;
+ st.ins_buf = curbuf;
+ st.e_cpt = (compl_cont_status & CONT_LOCAL) ? "." : curbuf->b_p_cpt;
+ st.last_match_pos = st.first_match_pos = *ini;
+ } else if (st.ins_buf != curbuf && !buf_valid(st.ins_buf)) {
+ st.ins_buf = curbuf; // In case the buffer was wiped out.
}
- assert(ins_buf != NULL);
+ assert(st.ins_buf != NULL);
compl_old_match = compl_curr_match; // remember the last current match
- pos = (compl_direction == FORWARD) ? &last_match_pos : &first_match_pos;
+ st.cur_match_pos = (compl_dir_forward() ? &st.last_match_pos : &st.first_match_pos);
// For ^N/^P loop over all the flags/windows/buffers in 'complete'
for (;;) {
found_new_match = FAIL;
- set_match_pos = false;
+ st.set_match_pos = false;
// For ^N/^P pick a new entry from e_cpt if compl_started is off,
// or if found_all says this entry is done. For ^X^L only use the
// entries from 'complete' that look in loaded buffers.
if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
- && (!compl_started || found_all)) {
- found_all = false;
- while (*e_cpt == ',' || *e_cpt == ' ') {
- e_cpt++;
- }
- if (*e_cpt == '.' && !curbuf->b_scanned) {
- ins_buf = curbuf;
- first_match_pos = *ini;
- // Move the cursor back one character so that ^N can match the
- // word immediately after the cursor.
- if (ctrl_x_mode_normal() && dec(&first_match_pos) < 0) {
- // Move the cursor to after the last character in the
- // buffer, so that word at start of buffer is found
- // correctly.
- first_match_pos.lnum = ins_buf->b_ml.ml_line_count;
- first_match_pos.col = (colnr_T)STRLEN(ml_get(first_match_pos.lnum));
- }
- last_match_pos = first_match_pos;
- type = 0;
-
- // Remember the first match so that the loop stops when we
- // wrap and come back there a second time.
- set_match_pos = true;
- } else if (vim_strchr("buwU", *e_cpt) != NULL
- && (ins_buf = ins_compl_next_buf(ins_buf, *e_cpt)) != curbuf) {
- // Scan a buffer, but not the current one.
- if (ins_buf->b_ml.ml_mfp != NULL) { // loaded buffer
- compl_started = true;
- first_match_pos.col = last_match_pos.col = 0;
- first_match_pos.lnum = ins_buf->b_ml.ml_line_count + 1;
- last_match_pos.lnum = 0;
- type = 0;
- } else { // unloaded buffer, scan like dictionary
- found_all = true;
- if (ins_buf->b_fname == NULL) {
- continue;
- }
- type = CTRL_X_DICTIONARY;
- dict = (char_u *)ins_buf->b_fname;
- dict_f = DICT_EXACT;
- }
- msg_hist_off = true; // reset in msg_trunc_attr()
- vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
- ins_buf->b_fname == NULL
- ? buf_spname(ins_buf)
- : ins_buf->b_sfname == NULL
- ? ins_buf->b_fname
- : ins_buf->b_sfname);
- (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
- } else if (*e_cpt == NUL) {
+ && (!compl_started || st.found_all)) {
+ int status = process_next_cpt_value(&st, &type, ini);
+ if (status == INS_COMPL_CPT_END) {
break;
- } else {
- if (ctrl_x_mode_line_or_eval()) {
- type = -1;
- } else if (*e_cpt == 'k' || *e_cpt == 's') {
- if (*e_cpt == 'k') {
- type = CTRL_X_DICTIONARY;
- } else {
- type = CTRL_X_THESAURUS;
- }
- if (*++e_cpt != ',' && *e_cpt != NUL) {
- dict = e_cpt;
- dict_f = DICT_FIRST;
- }
- } else if (*e_cpt == 'i') {
- type = CTRL_X_PATH_PATTERNS;
- } else if (*e_cpt == 'd') {
- type = CTRL_X_PATH_DEFINES;
- } else if (*e_cpt == ']' || *e_cpt == 't') {
- msg_hist_off = true; // reset in msg_trunc_attr()
- type = CTRL_X_TAGS;
- vim_snprintf((char *)IObuff, IOSIZE, "%s", _("Scanning tags."));
- (void)msg_trunc_attr((char *)IObuff, true, HL_ATTR(HLF_R));
- } else {
- type = -1;
- }
-
- // in any case e_cpt is advanced to the next entry
- (void)copy_option_part((char **)&e_cpt, (char *)IObuff, IOSIZE, ",");
-
- found_all = true;
- if (type == -1) {
- continue;
- }
+ }
+ if (status == INS_COMPL_CPT_CONT) {
+ continue;
}
}
@@ -2615,271 +3211,12 @@ static int ins_compl_get_exp(pos_T *ini)
break;
}
- switch (type) {
- case -1:
- break;
- case CTRL_X_PATH_PATTERNS:
- case CTRL_X_PATH_DEFINES:
- find_pattern_in_path((char_u *)compl_pattern, compl_direction,
- STRLEN(compl_pattern), false, false,
- ((type == CTRL_X_PATH_DEFINES
- && !(compl_cont_status & CONT_SOL))
- ? FIND_DEFINE
- : FIND_ANY),
- 1L, ACTION_EXPAND, 1, MAXLNUM);
- break;
-
- case CTRL_X_DICTIONARY:
- case CTRL_X_THESAURUS:
- if (thesaurus_func_complete(type)) {
- expand_by_function(type, (char_u *)compl_pattern);
- } else {
- ins_compl_dictionaries(dict != NULL ? dict
- : (type == CTRL_X_THESAURUS
- ? (*curbuf->b_p_tsr == NUL ? p_tsr : curbuf->b_p_tsr)
- : (*curbuf->b_p_dict ==
- NUL ? p_dict : curbuf->b_p_dict)),
- (char_u *)compl_pattern,
- dict != NULL ? dict_f : 0, type == CTRL_X_THESAURUS);
- }
- dict = NULL;
- break;
-
- case CTRL_X_TAGS:
- // set p_ic according to p_ic, p_scs and pat for find_tags().
- save_p_ic = p_ic;
- p_ic = ignorecase((char_u *)compl_pattern);
-
- // Find up to TAG_MANY matches. Avoids that an enormous number
- // of matches is found when compl_pattern is empty
- g_tag_at_cursor = true;
- if (find_tags((char_u *)compl_pattern, &num_matches, &matches,
- TAG_REGEXP | TAG_NAMES | TAG_NOIC | TAG_INS_COMP
- | (ctrl_x_mode_not_default() ? TAG_VERBOSE : 0),
- TAG_MANY, (char_u *)curbuf->b_ffname) == OK && num_matches > 0) {
- ins_compl_add_matches(num_matches, matches, p_ic);
- }
- g_tag_at_cursor = false;
- p_ic = save_p_ic;
- break;
-
- case CTRL_X_FILES:
- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
- EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK) {
- // May change home directory back to "~".
- tilde_replace((char_u *)compl_pattern, num_matches, matches);
-#ifdef BACKSLASH_IN_FILENAME
- if (curbuf->b_p_csl[0] != NUL) {
- for (int i = 0; i < num_matches; i++) {
- char_u *ptr = matches[i];
- while (*ptr != NUL) {
- if (curbuf->b_p_csl[0] == 's' && *ptr == '\\') {
- *ptr = '/';
- } else if (curbuf->b_p_csl[0] == 'b' && *ptr == '/') {
- *ptr = '\\';
- }
- ptr += utfc_ptr2len(ptr);
- }
- }
- }
-#endif
- ins_compl_add_matches(num_matches, matches, p_fic || p_wic);
- }
- break;
-
- case CTRL_X_CMDLINE:
- case CTRL_X_CMDLINE_CTRL_X:
- if (expand_cmdline(&compl_xp, (char_u *)compl_pattern,
- (int)STRLEN(compl_pattern),
- &num_matches, &matches) == EXPAND_OK) {
- ins_compl_add_matches(num_matches, matches, false);
- }
- break;
-
- case CTRL_X_FUNCTION:
- case CTRL_X_OMNI:
- expand_by_function(type, (char_u *)compl_pattern);
- break;
-
- case CTRL_X_SPELL:
- num_matches = expand_spelling(first_match_pos.lnum,
- (char_u *)compl_pattern, &matches);
- if (num_matches > 0) {
- ins_compl_add_matches(num_matches, matches, p_ic);
- }
- break;
-
- default: // normal ^P/^N and ^X^L
- // If 'infercase' is set, don't use 'smartcase' here
- save_p_scs = p_scs;
- assert(ins_buf);
- if (ins_buf->b_p_inf) {
- p_scs = false;
- }
-
- // Buffers other than curbuf are scanned from the beginning or the
- // end but never from the middle, thus setting nowrapscan in this
- // buffers is a good idea, on the other hand, we always set
- // wrapscan for curbuf to avoid missing matches -- Acevedo,Webb
- save_p_ws = p_ws;
- if (ins_buf != curbuf) {
- p_ws = false;
- } else if (*e_cpt == '.') {
- p_ws = true;
- }
- bool looped_around = false;
- for (;;) {
- bool cont_s_ipos = false;
-
- msg_silent++; // Don't want messages for wrapscan.
- // ctrl_x_mode_line_or_eval() || word-wise search that
- // has added a word that was at the beginning of the line.
- if (ctrl_x_mode_line_or_eval()
- || (compl_cont_status & CONT_SOL)) {
- found_new_match = search_for_exact_line(ins_buf, pos,
- compl_direction,
- (char_u *)compl_pattern);
- } else {
- found_new_match = searchit(NULL, ins_buf, pos, NULL,
- compl_direction,
- (char_u *)compl_pattern, 1L,
- SEARCH_KEEP + SEARCH_NFMSG,
- RE_LAST, NULL);
- }
- msg_silent--;
- if (!compl_started || set_match_pos) {
- // set "compl_started" even on fail
- compl_started = true;
- first_match_pos = *pos;
- last_match_pos = *pos;
- set_match_pos = false;
- } else if (first_match_pos.lnum == last_match_pos.lnum
- && first_match_pos.col == last_match_pos.col) {
- found_new_match = FAIL;
- } else if ((compl_direction == FORWARD)
- && (prev_pos.lnum > pos->lnum
- || (prev_pos.lnum == pos->lnum
- && prev_pos.col >= pos->col))) {
- if (looped_around) {
- found_new_match = FAIL;
- } else {
- looped_around = true;
- }
- } else if ((compl_direction != FORWARD)
- && (prev_pos.lnum < pos->lnum
- || (prev_pos.lnum == pos->lnum
- && prev_pos.col <= pos->col))) {
- if (looped_around) {
- found_new_match = FAIL;
- } else {
- looped_around = true;
- }
- }
- prev_pos = *pos;
- if (found_new_match == FAIL) {
- if (ins_buf == curbuf) {
- found_all = true;
- }
- break;
- }
-
- // when ADDING, the text before the cursor matches, skip it
- if ((compl_cont_status & CONT_ADDING) && ins_buf == curbuf
- && ini->lnum == pos->lnum
- && ini->col == pos->col) {
- continue;
- }
- ptr = ml_get_buf(ins_buf, pos->lnum, false) + pos->col;
- if (ctrl_x_mode_line_or_eval()) {
- if (compl_cont_status & CONT_ADDING) {
- if (pos->lnum >= ins_buf->b_ml.ml_line_count) {
- continue;
- }
- ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
- if (!p_paste) {
- ptr = (char_u *)skipwhite((char *)ptr);
- }
- }
- len = (int)STRLEN(ptr);
- } else {
- char_u *tmp_ptr = ptr;
-
- if (compl_cont_status & CONT_ADDING) {
- tmp_ptr += compl_length;
- // Skip if already inside a word.
- if (vim_iswordp(tmp_ptr)) {
- continue;
- }
- // Find start of next word.
- tmp_ptr = find_word_start(tmp_ptr);
- }
- // Find end of this word.
- tmp_ptr = find_word_end(tmp_ptr);
- len = (int)(tmp_ptr - ptr);
-
- if ((compl_cont_status & CONT_ADDING)
- && len == compl_length) {
- if (pos->lnum < ins_buf->b_ml.ml_line_count) {
- // Try next line, if any. the new word will be "join" as if the
- // normal command "J" was used. IOSIZE is always greater than
- // compl_length, so the next STRNCPY always works -- Acevedo
- STRNCPY(IObuff, ptr, len); // NOLINT(runtime/printf)
- ptr = ml_get_buf(ins_buf, pos->lnum + 1, false);
- tmp_ptr = ptr = (char_u *)skipwhite((char *)ptr);
- // Find start of next word.
- tmp_ptr = find_word_start(tmp_ptr);
- // Find end of next word.
- tmp_ptr = find_word_end(tmp_ptr);
- if (tmp_ptr > ptr) {
- if (*ptr != ')' && IObuff[len - 1] != TAB) {
- if (IObuff[len - 1] != ' ') {
- IObuff[len++] = ' ';
- }
- // IObuf =~ "\k.* ", thus len >= 2
- if (p_js
- && (IObuff[len - 2] == '.'
- || IObuff[len - 2] == '?'
- || IObuff[len - 2] == '!')) {
- IObuff[len++] = ' ';
- }
- }
- // copy as much as possible of the new word
- if (tmp_ptr - ptr >= IOSIZE - len) {
- tmp_ptr = ptr + IOSIZE - len - 1;
- }
- STRLCPY(IObuff + len, ptr, IOSIZE - len);
- len += (int)(tmp_ptr - ptr);
- cont_s_ipos = true;
- }
- IObuff[len] = NUL;
- ptr = IObuff;
- }
- if (len == compl_length) {
- continue;
- }
- }
- }
- if (ins_compl_add_infercase(ptr, len, p_ic,
- ins_buf == curbuf ? NULL : (char_u *)ins_buf->b_sfname,
- 0, cont_s_ipos) != NOTDONE) {
- found_new_match = OK;
- break;
- }
- }
- p_scs = save_p_scs;
- p_ws = save_p_ws;
- }
-
- // check if compl_curr_match has changed, (e.g. other type of
- // expansion added something)
- if (type != 0 && compl_curr_match != compl_old_match) {
- found_new_match = OK;
- }
+ // get the next set of completion matches
+ found_new_match = get_next_completion_match(type, &st, ini);
// break the loop for specialized modes (use 'complete' just for the
// generic ctrl_x_mode == CTRL_X_NORMAL) or when we've found a new match
- if ((ctrl_x_mode_not_default()
- && !ctrl_x_mode_line_or_eval())
+ if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
|| found_new_match != FAIL) {
if (got_int) {
break;
@@ -2889,8 +3226,7 @@ static int ins_compl_get_exp(pos_T *ini)
ins_compl_check_keys(0, false);
}
- if ((ctrl_x_mode_not_default()
- && !ctrl_x_mode_line_or_eval())
+ if ((ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())
|| compl_interrupted) {
break;
}
@@ -2898,8 +3234,8 @@ static int ins_compl_get_exp(pos_T *ini)
} else {
// Mark a buffer scanned when it has been scanned completely
if (type == 0 || type == CTRL_X_PATH_PATTERNS) {
- assert(ins_buf);
- ins_buf->b_scanned = true;
+ assert(st.ins_buf);
+ st.ins_buf->b_scanned = true;
}
compl_started = false;
@@ -2907,16 +3243,14 @@ static int ins_compl_get_exp(pos_T *ini)
}
compl_started = true;
- if ((ctrl_x_mode_normal()
- || ctrl_x_mode_line_or_eval())
- && *e_cpt == NUL) { // Got to end of 'complete'
+ if ((ctrl_x_mode_normal() || ctrl_x_mode_line_or_eval())
+ && *st.e_cpt == NUL) { // Got to end of 'complete'
found_new_match = FAIL;
}
i = -1; // total of matches, unknown
if (found_new_match == FAIL
- || (ctrl_x_mode_not_default()
- && !ctrl_x_mode_line_or_eval())) {
+ || (ctrl_x_mode_not_default() && !ctrl_x_mode_line_or_eval())) {
i = ins_compl_make_cyclic();
}
@@ -2924,7 +3258,7 @@ static int ins_compl_get_exp(pos_T *ini)
// If several matches were added (FORWARD) or the search failed and has
// just been made cyclic then we have to move compl_curr_match to the
// next or previous entry (if any) -- Acevedo
- compl_curr_match = compl_direction == FORWARD
+ compl_curr_match = compl_dir_forward()
? compl_old_match->cp_next
: compl_old_match->cp_prev;
if (compl_curr_match == NULL) {
@@ -2936,6 +3270,31 @@ static int ins_compl_get_exp(pos_T *ini)
return i;
}
+/// Update "compl_shown_match" to the actually shown match, it may differ when
+/// "compl_leader" is used to omit some of the matches.
+static void ins_compl_update_shown_match(void)
+{
+ while (!ins_compl_equal(compl_shown_match,
+ compl_leader, STRLEN(compl_leader))
+ && compl_shown_match->cp_next != NULL
+ && !is_first_match(compl_shown_match->cp_next)) {
+ compl_shown_match = compl_shown_match->cp_next;
+ }
+
+ // If we didn't find it searching forward, and compl_shows_dir is
+ // backward, find the last match.
+ if (compl_shows_dir_backward()
+ && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
+ && (compl_shown_match->cp_next == NULL
+ || is_first_match(compl_shown_match->cp_next))) {
+ while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
+ && compl_shown_match->cp_prev != NULL
+ && !is_first_match(compl_shown_match->cp_prev)) {
+ compl_shown_match = compl_shown_match->cp_prev;
+ }
+ }
+}
+
/// Delete the old text being completed.
void ins_compl_delete(void)
{
@@ -2943,7 +3302,7 @@ void ins_compl_delete(void)
// In insert mode: Delete the typed part.
// In replace mode: Put the old characters back, if any.
- col = compl_col + (compl_cont_status & CONT_ADDING ? compl_length : 0);
+ col = compl_col + (compl_status_adding() ? compl_length : 0);
if ((int)curwin->w_cursor.col > col) {
if (stop_arrow() == FAIL) {
return;
@@ -2962,8 +3321,8 @@ void ins_compl_delete(void)
/// "in_compl_func" is true when called from complete_check().
void ins_compl_insert(bool in_compl_func)
{
- ins_bytes(compl_shown_match->cp_str + get_compl_len());
- compl_used_match = !(compl_shown_match->cp_flags & CP_ORIGINAL_TEXT);
+ ins_bytes((char *)compl_shown_match->cp_str + get_compl_len());
+ compl_used_match = !match_at_original_text(compl_shown_match);
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
@@ -2972,97 +3331,69 @@ void ins_compl_insert(bool in_compl_func)
}
}
-/// Fill in the next completion in the current direction.
-/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to
-/// get more completions. If it is false, then we just do nothing when there
-/// are no more completions in a given direction. The latter case is used when
-/// we are still in the middle of finding completions, to allow browsing
-/// through the ones found so far.
-/// @return the total number of matches, or -1 if still unknown -- webb.
-///
-/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use
-/// compl_shown_match here.
-///
-/// Note that this function may be called recursively once only. First with
-/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn
-/// calls this function with "allow_get_expansion" false.
-///
-/// @param count Repeat completion this many times; should be at least 1
-/// @param insert_match Insert the newly selected match
-/// @param in_compl_func Called from complete_check()
-static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match,
- bool in_compl_func)
+/// show the file name for the completion match (if any). Truncate the file
+/// name to avoid a wait for return.
+static void ins_compl_show_filename(void)
{
- int num_matches = -1;
- int todo = count;
- compl_T *found_compl = NULL;
- bool found_end = false;
- const bool started = compl_started;
-
- // When user complete function return -1 for findstart which is next
- // time of 'always', compl_shown_match become NULL.
- if (compl_shown_match == NULL) {
- return -1;
+ char *const lead = _("match in file");
+ int space = sc_col - vim_strsize(lead) - 2;
+ if (space <= 0) {
+ return;
}
- if (compl_leader != NULL
- && (compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0) {
- // Set "compl_shown_match" to the actually shown match, it may differ
- // when "compl_leader" is used to omit some of the matches.
- while (!ins_compl_equal(compl_shown_match,
- compl_leader, STRLEN(compl_leader))
- && compl_shown_match->cp_next != NULL
- && compl_shown_match->cp_next != compl_first_match) {
- compl_shown_match = compl_shown_match->cp_next;
- }
-
- // If we didn't find it searching forward, and compl_shows_dir is
- // backward, find the last match.
- if (compl_shows_dir == BACKWARD
- && !ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
- && (compl_shown_match->cp_next == NULL
- || compl_shown_match->cp_next == compl_first_match)) {
- while (!ins_compl_equal(compl_shown_match, compl_leader, STRLEN(compl_leader))
- && compl_shown_match->cp_prev != NULL
- && compl_shown_match->cp_prev != compl_first_match) {
- compl_shown_match = compl_shown_match->cp_prev;
- }
+ // We need the tail that fits. With double-byte encoding going
+ // back from the end is very slow, thus go from the start and keep
+ // the text that fits in "space" between "s" and "e".
+ char *s;
+ char *e;
+ for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
+ space -= ptr2cells(e);
+ while (space < 0) {
+ space += ptr2cells(s);
+ MB_PTR_ADV(s);
}
}
+ msg_hist_off = true;
+ vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
+ (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s);
+ msg((char *)IObuff);
+ msg_hist_off = false;
+ redraw_cmdline = false; // don't overwrite!
+}
- if (allow_get_expansion && insert_match
- && (!(compl_get_longest || compl_restarting) || compl_used_match)) {
- // Delete old text to be replaced
- ins_compl_delete();
- }
-
- // When finding the longest common text we stick at the original text,
- // don't let CTRL-N or CTRL-P move to the first match.
- bool advance = count != 1 || !allow_get_expansion || !compl_get_longest;
-
- // When restarting the search don't insert the first match either.
- if (compl_restarting) {
- advance = false;
- compl_restarting = false;
- }
+/// Find the next set of matches for completion. Repeat the completion "todo"
+/// times. The number of matches found is returned in 'num_matches'.
+///
+/// @param allow_get_expansion If true, then ins_compl_get_exp() may be called to
+/// get more completions.
+/// If false, then do nothing when there are no more
+/// completions in the given direction.
+/// @param todo repeat completion this many times
+/// @param advance If true, then completion will move to the first match.
+/// Otherwise, the original text will be shown.
+///
+/// @return OK on success and -1 if the number of matches are unknown.
+static int find_next_completion_match(bool allow_get_expansion, int todo, bool advance,
+ int *num_matches)
+{
+ bool found_end = false;
+ compl_T *found_compl = NULL;
- // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
- // around.
while (--todo >= 0) {
- if (compl_shows_dir == FORWARD && compl_shown_match->cp_next != NULL) {
+ if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) {
compl_shown_match = compl_shown_match->cp_next;
found_end = (compl_first_match != NULL
- && (compl_shown_match->cp_next == compl_first_match
- || compl_shown_match == compl_first_match));
- } else if (compl_shows_dir == BACKWARD
+ && (is_first_match(compl_shown_match->cp_next)
+ || is_first_match(compl_shown_match)));
+ } else if (compl_shows_dir_backward()
&& compl_shown_match->cp_prev != NULL) {
- found_end = (compl_shown_match == compl_first_match);
+ found_end = is_first_match(compl_shown_match);
compl_shown_match = compl_shown_match->cp_prev;
- found_end |= (compl_shown_match == compl_first_match);
+ found_end |= is_first_match(compl_shown_match);
} else {
if (!allow_get_expansion) {
if (advance) {
- if (compl_shows_dir == BACKWARD) {
+ if (compl_shows_dir_backward()) {
compl_pending -= todo + 1;
} else {
compl_pending += todo + 1;
@@ -3072,7 +3403,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
if (!compl_no_select && advance) {
- if (compl_shows_dir == BACKWARD) {
+ if (compl_shows_dir_backward()) {
compl_pending--;
} else {
compl_pending++;
@@ -3080,7 +3411,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Find matches.
- num_matches = ins_compl_get_exp(&compl_startpos);
+ *num_matches = ins_compl_get_exp(&compl_startpos);
// handle any pending completions
while (compl_pending != 0 && compl_direction == compl_shows_dir
@@ -3098,7 +3429,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
found_end = false;
}
- if ((compl_shown_match->cp_flags & CP_ORIGINAL_TEXT) == 0
+ if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL
&& !ins_compl_equal(compl_shown_match,
compl_leader, STRLEN(compl_leader))) {
@@ -3118,15 +3449,77 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
}
+ return OK;
+}
+
+/// Fill in the next completion in the current direction.
+/// If "allow_get_expansion" is true, then we may call ins_compl_get_exp() to
+/// get more completions. If it is false, then we just do nothing when there
+/// are no more completions in a given direction. The latter case is used when
+/// we are still in the middle of finding completions, to allow browsing
+/// through the ones found so far.
+/// @return the total number of matches, or -1 if still unknown -- webb.
+///
+/// compl_curr_match is currently being used by ins_compl_get_exp(), so we use
+/// compl_shown_match here.
+///
+/// Note that this function may be called recursively once only. First with
+/// "allow_get_expansion" true, which calls ins_compl_get_exp(), which in turn
+/// calls this function with "allow_get_expansion" false.
+///
+/// @param count Repeat completion this many times; should be at least 1
+/// @param insert_match Insert the newly selected match
+/// @param in_compl_func Called from complete_check()
+static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match,
+ bool in_compl_func)
+{
+ int num_matches = -1;
+ int todo = count;
+ const bool started = compl_started;
+
+ // When user complete function return -1 for findstart which is next
+ // time of 'always', compl_shown_match become NULL.
+ if (compl_shown_match == NULL) {
+ return -1;
+ }
+
+ if (compl_leader != NULL && !match_at_original_text(compl_shown_match)) {
+ // Update "compl_shown_match" to the actually shown match
+ ins_compl_update_shown_match();
+ }
+
+ if (allow_get_expansion && insert_match
+ && (!(compl_get_longest || compl_restarting) || compl_used_match)) {
+ // Delete old text to be replaced
+ ins_compl_delete();
+ }
+
+ // When finding the longest common text we stick at the original text,
+ // don't let CTRL-N or CTRL-P move to the first match.
+ bool advance = count != 1 || !allow_get_expansion || !compl_get_longest;
+
+ // When restarting the search don't insert the first match either.
+ if (compl_restarting) {
+ advance = false;
+ compl_restarting = false;
+ }
+
+ // Repeat this for when <PageUp> or <PageDown> is typed. But don't wrap
+ // around.
+ if (find_next_completion_match(allow_get_expansion, todo, advance,
+ &num_matches) == -1) {
+ return -1;
+ }
+
// Insert the text of the new completion, or the compl_leader.
if (compl_no_insert && !started) {
- ins_bytes(compl_orig_text + get_compl_len());
+ ins_bytes((char *)compl_orig_text + get_compl_len());
compl_used_match = false;
} else if (insert_match) {
if (!compl_get_longest || compl_used_match) {
ins_compl_insert(in_compl_func);
} else {
- ins_bytes(compl_leader + get_compl_len());
+ ins_bytes((char *)compl_leader + get_compl_len());
}
} else {
compl_used_match = false;
@@ -3153,31 +3546,8 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Show the file name for the match (if any)
- // Truncate the file name to avoid a wait for return.
if (compl_shown_match->cp_fname != NULL) {
- char *lead = _("match in file");
- int space = sc_col - vim_strsize(lead) - 2;
- char *s;
- char *e;
-
- if (space > 0) {
- // We need the tail that fits. With double-byte encoding going
- // back from the end is very slow, thus go from the start and keep
- // the text that fits in "space" between "s" and "e".
- for (s = e = (char *)compl_shown_match->cp_fname; *e != NUL; MB_PTR_ADV(e)) {
- space -= ptr2cells(e);
- while (space < 0) {
- space += ptr2cells(s);
- MB_PTR_ADV(s);
- }
- }
- msg_hist_off = true;
- vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
- (char_u *)s > compl_shown_match->cp_fname ? "<" : "", s);
- msg((char *)IObuff);
- msg_hist_off = false;
- redraw_cmdline = false; // don't overwrite!
- }
+ ins_compl_show_filename();
}
return num_matches;
@@ -3330,7 +3700,7 @@ static bool ins_compl_use_match(int c)
static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
{
if ((compl_cont_status & CONT_SOL) || ctrl_x_mode_path_defines()) {
- if (!(compl_cont_status & CONT_ADDING)) {
+ if (!compl_status_adding()) {
while (--startcol >= 0 && vim_isIDc(line[startcol])) {}
compl_col += ++startcol;
compl_length = curs_col - startcol;
@@ -3340,7 +3710,7 @@ static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
} else {
compl_pattern = (char *)vim_strnsave(line + compl_col, (size_t)compl_length);
}
- } else if (compl_cont_status & CONT_ADDING) {
+ } else if (compl_status_adding()) {
char_u *prefix = (char_u *)"\\<";
// we need up to 2 extra chars for the prefix
@@ -3397,7 +3767,7 @@ static int get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
/// Sets the global variables: compl_col, compl_length and compl_pattern.
static int get_wholeline_compl_info(char_u *line, colnr_T curs_col)
{
- compl_col = (colnr_T)getwhitecols(line);
+ compl_col = (colnr_T)getwhitecols((char *)line);
compl_length = (int)curs_col - (int)compl_col;
if (compl_length < 0) { // cursor in indent: empty pattern
compl_length = 0;
@@ -3509,7 +3879,7 @@ static int get_userdefined_compl_info(colnr_T curs_col)
return FAIL;
}
- // Reset extended parameters of completion, when start new
+ // Reset extended parameters of completion, when starting new
// completion.
compl_opt_refresh_always = false;
@@ -3556,6 +3926,14 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
}
/// Get the completion pattern, column and length.
+///
+/// @param startcol start column number of the completion pattern/text
+/// @param cur_col current cursor column
+///
+/// On return, "line_invalid" is set to true, if the current line may have
+/// become invalid and needs to be fetched again.
+///
+/// @return OK on success.
static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *line_invalid)
{
if (ctrl_x_mode_normal()
@@ -3573,12 +3951,12 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
if (get_userdefined_compl_info(curs_col) == FAIL) {
return FAIL;
}
- *line_invalid = true; // 'line' may have become invalid
+ *line_invalid = true; // "line" may have become invalid
} else if (ctrl_x_mode_spell()) {
if (get_spell_compl_info(startcol, curs_col) == FAIL) {
return FAIL;
}
- *line_invalid = true; // 'line' may have become invalid
+ *line_invalid = true; // "line" may have become invalid
} else {
internal_error("ins_complete()");
return FAIL;
@@ -3587,232 +3965,191 @@ static int compl_get_info(char_u *line, int startcol, colnr_T curs_col, bool *li
return OK;
}
-/// Do Insert mode completion.
-/// Called when character "c" was typed, which has a meaning for completion.
-/// Returns OK if completion was done, FAIL if something failed.
-int ins_complete(int c, bool enable_pum)
+/// Continue an interrupted completion mode search in "line".
+///
+/// If this same ctrl_x_mode has been interrupted use the text from
+/// "compl_startpos" to the cursor as a pattern to add a new word instead of
+/// expand the one before the cursor, in word-wise if "compl_startpos" is not in
+/// the same line as the cursor then fix it (the line has been split because it
+/// was longer than 'tw'). if SOL is set then skip the previous pattern, a word
+/// at the beginning of the line has been inserted, we'll look for that.
+static void ins_compl_continue_search(char_u *line)
{
- char_u *line;
- int startcol = 0; // column where searched text starts
- colnr_T curs_col; // cursor column
- int n;
- int save_w_wrow;
- int save_w_leftcol;
- int insert_match;
- const bool save_did_ai = did_ai;
- int flags = CP_ORIGINAL_TEXT;
- bool line_invalid = false;
-
- compl_direction = ins_compl_key2dir(c);
- insert_match = ins_compl_use_match(c);
-
- if (!compl_started) {
- // First time we hit ^N or ^P (in a row, I mean)
-
- did_ai = false;
- did_si = false;
- can_si = false;
- can_si_back = false;
- if (stop_arrow() == FAIL) {
- return FAIL;
- }
-
- line = ml_get(curwin->w_cursor.lnum);
- curs_col = curwin->w_cursor.col;
- compl_pending = 0;
-
- // If this same ctrl_x_mode has been interrupted use the text from
- // "compl_startpos" to the cursor as a pattern to add a new word
- // instead of expand the one before the cursor, in word-wise if
- // "compl_startpos" is not in the same line as the cursor then fix it
- // (the line has been split because it was longer than 'tw'). if SOL
- // is set then skip the previous pattern, a word at the beginning of
- // the line has been inserted, we'll look for that -- Acevedo.
- if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
- && compl_cont_mode == ctrl_x_mode) {
- // it is a continued search
- compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
- if (ctrl_x_mode_normal()
- || ctrl_x_mode_path_patterns()
- || ctrl_x_mode_path_defines()) {
- if (compl_startpos.lnum != curwin->w_cursor.lnum) {
- // line (probably) wrapped, set compl_startpos to the
- // first non_blank in the line, if it is not a wordchar
- // include it to get a better pattern, but then we don't
- // want the "\\<" prefix, check it below.
- compl_col = (colnr_T)getwhitecols(line);
- compl_startpos.col = compl_col;
- compl_startpos.lnum = curwin->w_cursor.lnum;
- compl_cont_status &= ~CONT_SOL; // clear SOL if present
- } else {
- // S_IPOS was set when we inserted a word that was at the
- // beginning of the line, which means that we'll go to SOL
- // mode but first we need to redefine compl_startpos
- if (compl_cont_status & CONT_S_IPOS) {
- compl_cont_status |= CONT_SOL;
- compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line
- + compl_length
- + compl_startpos.col) - line);
- }
- compl_col = compl_startpos.col;
- }
- compl_length = curwin->w_cursor.col - (int)compl_col;
- // IObuff is used to add a "word from the next line" would we
- // have enough space? just being paranoid
-#define MIN_SPACE 75
- if (compl_length > (IOSIZE - MIN_SPACE)) {
- compl_cont_status &= ~CONT_SOL;
- compl_length = (IOSIZE - MIN_SPACE);
- compl_col = curwin->w_cursor.col - compl_length;
- }
- compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
- if (compl_length < 1) {
- compl_cont_status &= CONT_LOCAL;
- }
- } else if (ctrl_x_mode_line_or_eval()) {
- compl_cont_status = CONT_ADDING | CONT_N_ADDS;
- } else {
- compl_cont_status = 0;
- }
+ // it is a continued search
+ compl_cont_status &= ~CONT_INTRPT; // remove INTRPT
+ if (ctrl_x_mode_normal()
+ || ctrl_x_mode_path_patterns()
+ || ctrl_x_mode_path_defines()) {
+ if (compl_startpos.lnum != curwin->w_cursor.lnum) {
+ // line (probably) wrapped, set compl_startpos to the
+ // first non_blank in the line, if it is not a wordchar
+ // include it to get a better pattern, but then we don't
+ // want the "\\<" prefix, check it below.
+ compl_col = (colnr_T)getwhitecols((char *)line);
+ compl_startpos.col = compl_col;
+ compl_startpos.lnum = curwin->w_cursor.lnum;
+ compl_cont_status &= ~CONT_SOL; // clear SOL if present
} else {
- compl_cont_status &= CONT_LOCAL;
- }
-
- if (!(compl_cont_status & CONT_ADDING)) { // normal expansion
- compl_cont_mode = ctrl_x_mode;
- if (ctrl_x_mode_not_default()) {
- // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
- compl_cont_status = 0;
+ // S_IPOS was set when we inserted a word that was at the
+ // beginning of the line, which means that we'll go to SOL
+ // mode but first we need to redefine compl_startpos
+ if (compl_cont_status & CONT_S_IPOS) {
+ compl_cont_status |= CONT_SOL;
+ compl_startpos.col = (colnr_T)((char_u *)skipwhite((char *)line
+ + compl_length
+ + compl_startpos.col) - line);
}
- compl_cont_status |= CONT_N_ADDS;
- compl_startpos = curwin->w_cursor;
- startcol = (int)curs_col;
- compl_col = 0;
- }
-
- // Work out completion pattern and original text -- webb
- if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
- if (ctrl_x_mode_function() || ctrl_x_mode_omni()
- || thesaurus_func_complete(ctrl_x_mode)) {
- // restore did_ai, so that adding comment leader works
- did_ai = save_did_ai;
- }
- return FAIL;
+ compl_col = compl_startpos.col;
}
- // If "line" was changed while getting completion info get it again.
- if (line_invalid) {
- line = ml_get(curwin->w_cursor.lnum);
+ compl_length = curwin->w_cursor.col - (int)compl_col;
+ // IObuff is used to add a "word from the next line" would we
+ // have enough space? just being paranoid
+#define MIN_SPACE 75
+ if (compl_length > (IOSIZE - MIN_SPACE)) {
+ compl_cont_status &= ~CONT_SOL;
+ compl_length = (IOSIZE - MIN_SPACE);
+ compl_col = curwin->w_cursor.col - compl_length;
+ }
+ compl_cont_status |= CONT_ADDING | CONT_N_ADDS;
+ if (compl_length < 1) {
+ compl_cont_status &= CONT_LOCAL;
}
+ } else if (ctrl_x_mode_line_or_eval()) {
+ compl_cont_status = CONT_ADDING | CONT_N_ADDS;
+ } else {
+ compl_cont_status = 0;
+ }
+}
- if (compl_cont_status & CONT_ADDING) {
- edit_submode_pre = (char_u *)_(" Adding");
- if (ctrl_x_mode_line_or_eval()) {
- // Insert a new line, keep indentation but ignore 'comments'.
- char_u *old = curbuf->b_p_com;
+/// start insert mode completion
+static int ins_compl_start(void)
+{
+ const bool save_did_ai = did_ai;
- curbuf->b_p_com = (char_u *)"";
- compl_startpos.lnum = curwin->w_cursor.lnum;
- compl_startpos.col = compl_col;
- ins_eol('\r');
- curbuf->b_p_com = old;
- compl_length = 0;
- compl_col = curwin->w_cursor.col;
- }
- } else {
- edit_submode_pre = NULL;
- compl_startpos.col = compl_col;
- }
+ // First time we hit ^N or ^P (in a row, I mean)
- if (compl_cont_status & CONT_LOCAL) {
- edit_submode = (char_u *)_(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
- } else {
- edit_submode = (char_u *)_(CTRL_X_MSG(ctrl_x_mode));
- }
+ did_ai = false;
+ did_si = false;
+ can_si = false;
+ can_si_back = false;
+ if (stop_arrow() == FAIL) {
+ return FAIL;
+ }
- // If any of the original typed text has been changed we need to fix
- // the redo buffer.
- ins_compl_fixRedoBufForLeader(NULL);
+ char_u *line = ml_get(curwin->w_cursor.lnum);
+ colnr_T curs_col = curwin->w_cursor.col;
+ compl_pending = 0;
+
+ if ((compl_cont_status & CONT_INTRPT) == CONT_INTRPT
+ && compl_cont_mode == ctrl_x_mode) {
+ // this same ctrl-x_mode was interrupted previously. Continue the
+ // completion.
+ ins_compl_continue_search(line);
+ } else {
+ compl_cont_status &= CONT_LOCAL;
+ }
- // Always add completion for the original text.
- xfree(compl_orig_text);
- compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
- if (p_ic) {
- flags |= CP_ICASE;
- }
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
- flags, false) != OK) {
- XFREE_CLEAR(compl_pattern);
- XFREE_CLEAR(compl_orig_text);
- return FAIL;
+ int startcol = 0; // column where searched text starts
+ if (!compl_status_adding()) { // normal expansion
+ compl_cont_mode = ctrl_x_mode;
+ if (ctrl_x_mode_not_default()) {
+ // Remove LOCAL if ctrl_x_mode != CTRL_X_NORMAL
+ compl_cont_status = 0;
}
+ compl_cont_status |= CONT_N_ADDS;
+ compl_startpos = curwin->w_cursor;
+ startcol = (int)curs_col;
+ compl_col = 0;
+ }
- // showmode might reset the internal line pointers, so it must
- // be called before line = ml_get(), or when this address is no
- // longer needed. -- Acevedo.
- edit_submode_extra = (char_u *)_("-- Searching...");
- edit_submode_highl = HLF_COUNT;
- showmode();
- edit_submode_extra = NULL;
- ui_flush();
- } else if (insert_match && stop_arrow() == FAIL) {
+ // Work out completion pattern and original text -- webb
+ bool line_invalid = false;
+ if (compl_get_info(line, startcol, curs_col, &line_invalid) == FAIL) {
+ if (ctrl_x_mode_function() || ctrl_x_mode_omni()
+ || thesaurus_func_complete(ctrl_x_mode)) {
+ // restore did_ai, so that adding comment leader works
+ did_ai = save_did_ai;
+ }
return FAIL;
}
+ // If "line" was changed while getting completion info get it again.
+ if (line_invalid) {
+ line = ml_get(curwin->w_cursor.lnum);
+ }
- compl_shown_match = compl_curr_match;
- compl_shows_dir = compl_direction;
+ if (compl_status_adding()) {
+ edit_submode_pre = _(" Adding");
+ if (ctrl_x_mode_line_or_eval()) {
+ // Insert a new line, keep indentation but ignore 'comments'.
+ char *old = curbuf->b_p_com;
- // Find next match (and following matches).
- save_w_wrow = curwin->w_wrow;
- save_w_leftcol = curwin->w_leftcol;
- n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
+ curbuf->b_p_com = "";
+ compl_startpos.lnum = curwin->w_cursor.lnum;
+ compl_startpos.col = compl_col;
+ ins_eol('\r');
+ curbuf->b_p_com = old;
+ compl_length = 0;
+ compl_col = curwin->w_cursor.col;
+ }
+ } else {
+ edit_submode_pre = NULL;
+ compl_startpos.col = compl_col;
+ }
- if (n > 1) { // all matches have been found
- compl_matches = n;
+ if (compl_cont_status & CONT_LOCAL) {
+ edit_submode = _(ctrl_x_msgs[CTRL_X_LOCAL_MSG]);
+ } else {
+ edit_submode = _(CTRL_X_MSG(ctrl_x_mode));
}
- compl_curr_match = compl_shown_match;
- compl_direction = compl_shows_dir;
- // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
- // mode.
- if (got_int && !global_busy) {
- (void)vgetc();
- got_int = false;
+ // If any of the original typed text has been changed we need to fix
+ // the redo buffer.
+ ins_compl_fixRedoBufForLeader(NULL);
+
+ // Always add completion for the original text.
+ xfree(compl_orig_text);
+ compl_orig_text = vim_strnsave(line + compl_col, (size_t)compl_length);
+ int flags = CP_ORIGINAL_TEXT;
+ if (p_ic) {
+ flags |= CP_ICASE;
}
+ if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
+ flags, false) != OK) {
+ XFREE_CLEAR(compl_pattern);
+ XFREE_CLEAR(compl_orig_text);
+ return FAIL;
+ }
+
+ // showmode might reset the internal line pointers, so it must
+ // be called before line = ml_get(), or when this address is no
+ // longer needed. -- Acevedo.
+ edit_submode_extra = _("-- Searching...");
+ edit_submode_highl = HLF_COUNT;
+ showmode();
+ edit_submode_extra = NULL;
+ ui_flush();
+
+ return OK;
+}
+/// display the completion status message
+static void ins_compl_show_statusmsg(void)
+{
// we found no match if the list has only the "compl_orig_text"-entry
- if (compl_first_match == compl_first_match->cp_next) {
- edit_submode_extra = (compl_cont_status & CONT_ADDING)
- && compl_length > 1
- ? (char_u *)_(e_hitend) : (char_u *)_(e_patnotf);
+ if (is_first_match(compl_first_match->cp_next)) {
+ edit_submode_extra = compl_status_adding() && compl_length > 1 ? _(e_hitend) : _(e_patnotf);
edit_submode_highl = HLF_E;
- // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
- // because we couldn't expand anything at first place, but if we used
- // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
- // (such as M in M'exico) if not tried already. -- Acevedo
- if (compl_length > 1
- || (compl_cont_status & CONT_ADDING)
- || (ctrl_x_mode_not_default()
- && !ctrl_x_mode_path_patterns()
- && !ctrl_x_mode_path_defines())) {
- compl_cont_status &= ~CONT_N_ADDS;
- }
- }
-
- if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) {
- compl_cont_status |= CONT_S_IPOS;
- } else {
- compl_cont_status &= ~CONT_S_IPOS;
}
if (edit_submode_extra == NULL) {
- if (compl_curr_match->cp_flags & CP_ORIGINAL_TEXT) {
- edit_submode_extra = (char_u *)_("Back at original");
+ if (match_at_original_text(compl_curr_match)) {
+ edit_submode_extra = _("Back at original");
edit_submode_highl = HLF_W;
} else if (compl_cont_status & CONT_S_IPOS) {
- edit_submode_extra = (char_u *)_("Word from other line");
+ edit_submode_extra = _("Word from other line");
edit_submode_highl = HLF_COUNT;
} else if (compl_curr_match->cp_next == compl_curr_match->cp_prev) {
- edit_submode_extra = (char_u *)_("The only match");
+ edit_submode_extra = _("The only match");
edit_submode_highl = HLF_COUNT;
compl_curr_match->cp_number = 1;
} else {
@@ -3837,7 +4174,7 @@ int ins_complete(int c, bool enable_pum)
_("match %d"),
compl_curr_match->cp_number);
}
- edit_submode_extra = match_ref;
+ edit_submode_extra = (char *)match_ref;
edit_submode_highl = HLF_R;
if (dollar_vcol >= 0) {
curs_columns(curwin, false);
@@ -3861,6 +4198,72 @@ int ins_complete(int c, bool enable_pum)
msg_clr_cmdline(); // necessary for "noshowmode"
}
}
+}
+
+/// Do Insert mode completion.
+/// Called when character "c" was typed, which has a meaning for completion.
+/// Returns OK if completion was done, FAIL if something failed.
+int ins_complete(int c, bool enable_pum)
+{
+ int n;
+ int save_w_wrow;
+ int save_w_leftcol;
+ int insert_match;
+
+ compl_direction = ins_compl_key2dir(c);
+ insert_match = ins_compl_use_match(c);
+
+ if (!compl_started) {
+ if (ins_compl_start() == FAIL) {
+ return FAIL;
+ }
+ } else if (insert_match && stop_arrow() == FAIL) {
+ return FAIL;
+ }
+
+ compl_shown_match = compl_curr_match;
+ compl_shows_dir = compl_direction;
+
+ // Find next match (and following matches).
+ save_w_wrow = curwin->w_wrow;
+ save_w_leftcol = curwin->w_leftcol;
+ n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
+
+ if (n > 1) { // all matches have been found
+ compl_matches = n;
+ }
+ compl_curr_match = compl_shown_match;
+ compl_direction = compl_shows_dir;
+
+ // Eat the ESC that vgetc() returns after a CTRL-C to avoid leaving Insert
+ // mode.
+ if (got_int && !global_busy) {
+ (void)vgetc();
+ got_int = false;
+ }
+
+ // we found no match if the list has only the "compl_orig_text"-entry
+ if (is_first_match(compl_first_match->cp_next)) {
+ // remove N_ADDS flag, so next ^X<> won't try to go to ADDING mode,
+ // because we couldn't expand anything at first place, but if we used
+ // ^P, ^N, ^X^I or ^X^D we might want to add-expand a single-char-word
+ // (such as M in M'exico) if not tried already. -- Acevedo
+ if (compl_length > 1
+ || compl_status_adding()
+ || (ctrl_x_mode_not_default()
+ && !ctrl_x_mode_path_patterns()
+ && !ctrl_x_mode_path_defines())) {
+ compl_cont_status &= ~CONT_N_ADDS;
+ }
+ }
+
+ if (compl_curr_match->cp_flags & CP_CONT_S_IPOS) {
+ compl_cont_status |= CONT_S_IPOS;
+ } else {
+ compl_cont_status &= ~CONT_S_IPOS;
+ }
+
+ ins_compl_show_statusmsg();
// Show the popup menu, unless we got interrupted.
if (enable_pum && !compl_interrupted) {
@@ -3872,6 +4275,7 @@ int ins_complete(int c, bool enable_pum)
return OK;
}
+/// Remove (if needed) and show the popup menu
static void show_pum(int prev_w_wrow, int prev_w_leftcol)
{
// RedrawingDisabled may be set when invoked through complete().
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index 5a5257efb2..2b00d371f0 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -57,15 +57,15 @@ static char_u modifier_keys_table[] =
MOD_MASK_SHIFT, '*', '4', 'k', 'D', // delete char
MOD_MASK_SHIFT, '*', '5', 'k', 'L', // delete line
MOD_MASK_SHIFT, '*', '7', '@', '7', // end
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_END, '@', '7', // end
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_END, '@', '7', // end
MOD_MASK_SHIFT, '*', '9', '@', '9', // exit
MOD_MASK_SHIFT, '*', '0', '@', '0', // find
MOD_MASK_SHIFT, '#', '1', '%', '1', // help
MOD_MASK_SHIFT, '#', '2', 'k', 'h', // home
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_HOME, 'k', 'h', // home
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_HOME, 'k', 'h', // home
MOD_MASK_SHIFT, '#', '3', 'k', 'I', // insert
MOD_MASK_SHIFT, '#', '4', 'k', 'l', // left arrow
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_LEFT, 'k', 'l', // left arrow
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_LEFT, 'k', 'l', // left arrow
MOD_MASK_SHIFT, '%', 'a', '%', '3', // message
MOD_MASK_SHIFT, '%', 'b', '%', '4', // move
MOD_MASK_SHIFT, '%', 'c', '%', '5', // next
@@ -75,63 +75,63 @@ static char_u modifier_keys_table[] =
MOD_MASK_SHIFT, '%', 'g', '%', '0', // redo
MOD_MASK_SHIFT, '%', 'h', '&', '3', // replace
MOD_MASK_SHIFT, '%', 'i', 'k', 'r', // right arr.
- MOD_MASK_CTRL, KS_EXTRA, (int)KE_C_RIGHT, 'k', 'r', // right arr.
+ MOD_MASK_CTRL, KS_EXTRA, KE_C_RIGHT, 'k', 'r', // right arr.
MOD_MASK_SHIFT, '%', 'j', '&', '5', // resume
MOD_MASK_SHIFT, '!', '1', '&', '6', // save
MOD_MASK_SHIFT, '!', '2', '&', '7', // suspend
MOD_MASK_SHIFT, '!', '3', '&', '8', // undo
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_UP, 'k', 'u', // up arrow
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_DOWN, 'k', 'd', // down arrow
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_UP, 'k', 'u', // up arrow
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_DOWN, 'k', 'd', // down arrow
// vt100 F1
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF1, KS_EXTRA, (int)KE_XF1,
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF2, KS_EXTRA, (int)KE_XF2,
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF3, KS_EXTRA, (int)KE_XF3,
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_XF4, KS_EXTRA, (int)KE_XF4,
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F1, 'k', '1', // F1
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F2, 'k', '2',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F3, 'k', '3',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F4, 'k', '4',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F5, 'k', '5',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F6, 'k', '6',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F7, 'k', '7',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F8, 'k', '8',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F9, 'k', '9',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F10, 'k', ';', // F10
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F11, 'F', '1',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F12, 'F', '2',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F13, 'F', '3',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F14, 'F', '4',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F15, 'F', '5',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F16, 'F', '6',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F17, 'F', '7',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F18, 'F', '8',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F19, 'F', '9',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F20, 'F', 'A',
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F21, 'F', 'B',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F22, 'F', 'C',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F23, 'F', 'D',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F24, 'F', 'E',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F25, 'F', 'F',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F26, 'F', 'G',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F27, 'F', 'H',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F28, 'F', 'I',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F29, 'F', 'J',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F30, 'F', 'K',
-
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F31, 'F', 'L',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F32, 'F', 'M',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F33, 'F', 'N',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F34, 'F', 'O',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F35, 'F', 'P',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F36, 'F', 'Q',
- MOD_MASK_SHIFT, KS_EXTRA, (int)KE_S_F37, 'F', 'R',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF1, KS_EXTRA, KE_XF1,
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF2, KS_EXTRA, KE_XF2,
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF3, KS_EXTRA, KE_XF3,
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_XF4, KS_EXTRA, KE_XF4,
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F1, 'k', '1', // F1
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F2, 'k', '2',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F3, 'k', '3',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F4, 'k', '4',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F5, 'k', '5',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F6, 'k', '6',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F7, 'k', '7',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F8, 'k', '8',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F9, 'k', '9',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F10, 'k', ';', // F10
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F11, 'F', '1',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F12, 'F', '2',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F13, 'F', '3',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F14, 'F', '4',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F15, 'F', '5',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F16, 'F', '6',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F17, 'F', '7',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F18, 'F', '8',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F19, 'F', '9',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F20, 'F', 'A',
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F21, 'F', 'B',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F22, 'F', 'C',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F23, 'F', 'D',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F24, 'F', 'E',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F25, 'F', 'F',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F26, 'F', 'G',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F27, 'F', 'H',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F28, 'F', 'I',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F29, 'F', 'J',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F30, 'F', 'K',
+
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F31, 'F', 'L',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F32, 'F', 'M',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F33, 'F', 'N',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F34, 'F', 'O',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F35, 'F', 'P',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F36, 'F', 'Q',
+ MOD_MASK_SHIFT, KS_EXTRA, KE_S_F37, 'F', 'R',
// TAB pseudo code
- MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, (int)KE_TAB,
+ MOD_MASK_SHIFT, 'k', 'B', KS_EXTRA, KE_TAB,
NUL
};
@@ -349,26 +349,26 @@ static struct mousetable {
bool is_drag; // Is it a mouse drag event?
} mouse_table[] =
{
- { (int)KE_LEFTMOUSE, MOUSE_LEFT, true, false },
- { (int)KE_LEFTDRAG, MOUSE_LEFT, false, true },
- { (int)KE_LEFTRELEASE, MOUSE_LEFT, false, false },
- { (int)KE_MIDDLEMOUSE, MOUSE_MIDDLE, true, false },
- { (int)KE_MIDDLEDRAG, MOUSE_MIDDLE, false, true },
- { (int)KE_MIDDLERELEASE, MOUSE_MIDDLE, false, false },
- { (int)KE_RIGHTMOUSE, MOUSE_RIGHT, true, false },
- { (int)KE_RIGHTDRAG, MOUSE_RIGHT, false, true },
- { (int)KE_RIGHTRELEASE, MOUSE_RIGHT, false, false },
- { (int)KE_X1MOUSE, MOUSE_X1, true, false },
- { (int)KE_X1DRAG, MOUSE_X1, false, true },
- { (int)KE_X1RELEASE, MOUSE_X1, false, false },
- { (int)KE_X2MOUSE, MOUSE_X2, true, false },
- { (int)KE_X2DRAG, MOUSE_X2, false, true },
- { (int)KE_X2RELEASE, MOUSE_X2, false, false },
+ { KE_LEFTMOUSE, MOUSE_LEFT, true, false },
+ { KE_LEFTDRAG, MOUSE_LEFT, false, true },
+ { KE_LEFTRELEASE, MOUSE_LEFT, false, false },
+ { KE_MIDDLEMOUSE, MOUSE_MIDDLE, true, false },
+ { KE_MIDDLEDRAG, MOUSE_MIDDLE, false, true },
+ { KE_MIDDLERELEASE, MOUSE_MIDDLE, false, false },
+ { KE_RIGHTMOUSE, MOUSE_RIGHT, true, false },
+ { KE_RIGHTDRAG, MOUSE_RIGHT, false, true },
+ { KE_RIGHTRELEASE, MOUSE_RIGHT, false, false },
+ { KE_X1MOUSE, MOUSE_X1, true, false },
+ { KE_X1DRAG, MOUSE_X1, false, true },
+ { KE_X1RELEASE, MOUSE_X1, false, false },
+ { KE_X2MOUSE, MOUSE_X2, true, false },
+ { KE_X2DRAG, MOUSE_X2, false, true },
+ { KE_X2RELEASE, MOUSE_X2, false, false },
// DRAG without CLICK
- { (int)KE_MOUSEMOVE, MOUSE_RELEASE, false, true },
+ { KE_MOUSEMOVE, MOUSE_RELEASE, false, true },
// RELEASE without CLICK
- { (int)KE_IGNORE, MOUSE_RELEASE, false, false },
- { 0, 0, 0, 0 },
+ { KE_IGNORE, MOUSE_RELEASE, false, false },
+ { 0, 0, 0, 0 },
};
/// Return the modifier mask bit (#MOD_MASK_*) corresponding to mod name
@@ -926,8 +926,8 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
} else {
src += 5;
result[dlen++] = K_SPECIAL;
- result[dlen++] = (int)KS_EXTRA;
- result[dlen++] = (int)KE_SNR;
+ result[dlen++] = KS_EXTRA;
+ result[dlen++] = KE_SNR;
snprintf((char *)result + dlen, buf_len - dlen, "%" PRId64,
(int64_t)current_sctx.sc_sid);
dlen += STRLEN(result + dlen);
diff --git a/src/nvim/locale.c b/src/nvim/locale.c
new file mode 100644
index 0000000000..3e0774c096
--- /dev/null
+++ b/src/nvim/locale.c
@@ -0,0 +1,369 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// locale.c: functions for language/locale configuration
+
+#include "auto/config.h"
+
+#ifdef HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#include "nvim/ascii.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/eval.h"
+#include "nvim/garray.h"
+#include "nvim/locale.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
+#include "nvim/os/os.h"
+#include "nvim/os/shell.h"
+#include "nvim/path.h"
+#include "nvim/profile.h"
+#include "nvim/types.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "locale.c.generated.h"
+#endif
+
+#if defined(HAVE_LOCALE_H)
+# define HAVE_GET_LOCALE_VAL
+
+static char *get_locale_val(int what)
+{
+ // Obtain the locale value from the libraries.
+ char *loc = setlocale(what, NULL);
+
+ return loc;
+}
+#endif
+
+/// @return true when "lang" starts with a valid language name.
+/// Rejects NULL, empty string, "C", "C.UTF-8" and others.
+static bool is_valid_mess_lang(char *lang)
+{
+ return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]);
+}
+
+/// Obtain the current messages language. Used to set the default for
+/// 'helplang'. May return NULL or an empty string.
+char *get_mess_lang(void)
+{
+ char *p;
+
+#ifdef HAVE_GET_LOCALE_VAL
+# if defined(LC_MESSAGES)
+ p = get_locale_val(LC_MESSAGES);
+# else
+ // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG
+ // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME
+ // and LC_MONETARY may be set differently for a Japanese working in the
+ // US.
+ p = get_locale_val(LC_COLLATE);
+# endif
+#else
+ p = os_getenv("LC_ALL");
+ if (!is_valid_mess_lang(p)) {
+ p = os_getenv("LC_MESSAGES");
+ if (!is_valid_mess_lang(p)) {
+ p = os_getenv("LANG");
+ }
+ }
+#endif
+ return is_valid_mess_lang(p) ? p : NULL;
+}
+
+// Complicated #if; matches with where get_mess_env() is used below.
+#ifdef HAVE_WORKING_LIBINTL
+/// Get the language used for messages from the environment.
+static char *get_mess_env(void)
+{
+ char *p;
+
+ p = (char *)os_getenv("LC_ALL");
+ if (p == NULL) {
+ p = (char *)os_getenv("LC_MESSAGES");
+ if (p == NULL) {
+ p = (char *)os_getenv("LANG");
+ if (p != NULL && ascii_isdigit(*p)) {
+ p = NULL; // ignore something like "1043"
+ }
+# ifdef HAVE_GET_LOCALE_VAL
+ if (p == NULL) {
+ p = get_locale_val(LC_CTYPE);
+ }
+# endif
+ }
+ }
+ return p;
+}
+#endif
+
+/// Set the "v:lang" variable according to the current locale setting.
+/// Also do "v:lc_time"and "v:ctype".
+void set_lang_var(void)
+{
+ const char *loc;
+
+#ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_CTYPE);
+#else
+ // setlocale() not supported: use the default value
+ loc = "C";
+#endif
+ set_vim_var_string(VV_CTYPE, loc, -1);
+
+ // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall
+ // back to LC_CTYPE if it's empty.
+#ifdef HAVE_WORKING_LIBINTL
+ loc = get_mess_env();
+#elif defined(LC_MESSAGES)
+ loc = get_locale_val(LC_MESSAGES);
+#else
+ // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE
+ loc = get_locale_val(LC_CTYPE);
+#endif
+ set_vim_var_string(VV_LANG, loc, -1);
+
+#ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_TIME);
+#endif
+ set_vim_var_string(VV_LC_TIME, loc, -1);
+
+#ifdef HAVE_GET_LOCALE_VAL
+ loc = get_locale_val(LC_COLLATE);
+#else
+ // setlocale() not supported: use the default value
+ loc = "C";
+#endif
+ set_vim_var_string(VV_COLLATE, loc, -1);
+}
+
+#if defined(HAVE_LOCALE_H)
+/// Setup to use the current locale (for ctype() and many other things).
+void init_locale(void)
+{
+ setlocale(LC_ALL, "");
+
+# ifdef LC_NUMERIC
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+# endif
+
+ char localepath[MAXPATHL] = { 0 };
+ snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
+ char *tail = path_tail_with_sep(localepath);
+ *tail = NUL;
+ tail = path_tail(localepath);
+ xstrlcpy(tail, "share/locale",
+ sizeof(localepath) - (size_t)(tail - localepath));
+ bindtextdomain(PROJECT_NAME, localepath);
+ textdomain(PROJECT_NAME);
+ TIME_MSG("locale set");
+}
+#endif
+
+#ifdef HAVE_WORKING_LIBINTL
+
+/// ":language": Set the language (locale).
+///
+/// @param eap
+void ex_language(exarg_T *eap)
+{
+ char *loc;
+ char *p;
+ char *name;
+ int what = LC_ALL;
+ char *whatstr = "";
+# ifdef LC_MESSAGES
+# define VIM_LC_MESSAGES LC_MESSAGES
+# else
+# define VIM_LC_MESSAGES 6789
+# endif
+
+ name = eap->arg;
+
+ // Check for "messages {name}", "ctype {name}" or "time {name}" argument.
+ // Allow abbreviation, but require at least 3 characters to avoid
+ // confusion with a two letter language name "me" or "ct".
+ p = skiptowhite(eap->arg);
+ if ((*p == NUL || ascii_iswhite(*p)) && p - eap->arg >= 3) {
+ if (STRNICMP(eap->arg, "messages", p - eap->arg) == 0) {
+ what = VIM_LC_MESSAGES;
+ name = skipwhite(p);
+ whatstr = "messages ";
+ } else if (STRNICMP(eap->arg, "ctype", p - eap->arg) == 0) {
+ what = LC_CTYPE;
+ name = skipwhite(p);
+ whatstr = "ctype ";
+ } else if (STRNICMP(eap->arg, "time", p - eap->arg) == 0) {
+ what = LC_TIME;
+ name = skipwhite(p);
+ whatstr = "time ";
+ } else if (STRNICMP(eap->arg, "collate", p - eap->arg) == 0) {
+ what = LC_COLLATE;
+ name = skipwhite(p);
+ whatstr = "collate ";
+ }
+ }
+
+ if (*name == NUL) {
+ if (what == VIM_LC_MESSAGES) {
+ p = get_mess_env();
+ } else {
+ p = setlocale(what, NULL);
+ }
+ if (p == NULL || *p == NUL) {
+ p = "Unknown";
+ }
+ smsg(_("Current %slanguage: \"%s\""), whatstr, p);
+ } else {
+# ifndef LC_MESSAGES
+ if (what == VIM_LC_MESSAGES) {
+ loc = "";
+ } else {
+# endif
+ loc = setlocale(what, name);
+# ifdef LC_NUMERIC
+ // Make sure strtod() uses a decimal point, not a comma.
+ setlocale(LC_NUMERIC, "C");
+# endif
+# ifndef LC_MESSAGES
+ }
+# endif
+ if (loc == NULL) {
+ semsg(_("E197: Cannot set language to \"%s\""), name);
+ } else {
+# ifdef HAVE_NL_MSG_CAT_CNTR
+ // Need to do this for GNU gettext, otherwise cached translations
+ // will be used again.
+ extern int _nl_msg_cat_cntr;
+
+ _nl_msg_cat_cntr++;
+# endif
+ // Reset $LC_ALL, otherwise it would overrule everything.
+ os_setenv("LC_ALL", "", 1);
+
+ if (what != LC_TIME && what != LC_COLLATE) {
+ // Tell gettext() what to translate to. It apparently doesn't
+ // use the currently effective locale.
+ if (what == LC_ALL) {
+ os_setenv("LANG", name, 1);
+
+ // Clear $LANGUAGE because GNU gettext uses it.
+ os_setenv("LANGUAGE", "", 1);
+ }
+ if (what != LC_CTYPE) {
+ os_setenv("LC_MESSAGES", name, 1);
+ set_helplang_default(name);
+ }
+ }
+
+ // Set v:lang, v:lc_time, v:collate and v:ctype to the final result.
+ set_lang_var();
+ maketitle();
+ }
+ }
+}
+
+static char **locales = NULL; // Array of all available locales
+
+# ifndef WIN32
+static bool did_init_locales = false;
+
+/// @return an array of strings for all available locales + NULL for the
+/// last element or,
+/// NULL in case of error.
+static char **find_locales(void)
+{
+ garray_T locales_ga;
+ char *loc;
+ char *saveptr = NULL;
+
+ // Find all available locales by running command "locale -a". If this
+ // doesn't work we won't have completion.
+ char *locale_a = (char *)get_cmd_output((char_u *)"locale -a", NULL,
+ kShellOptSilent, NULL);
+ if (locale_a == NULL) {
+ return NULL;
+ }
+ ga_init(&locales_ga, sizeof(char_u *), 20);
+
+ // Transform locale_a string where each locale is separated by "\n"
+ // into an array of locale strings.
+ loc = os_strtok(locale_a, "\n", &saveptr);
+
+ while (loc != NULL) {
+ loc = xstrdup(loc);
+ GA_APPEND(char *, &locales_ga, loc);
+ loc = os_strtok(NULL, "\n", &saveptr);
+ }
+ xfree(locale_a);
+ // Guarantee that .ga_data is NULL terminated
+ ga_grow(&locales_ga, 1);
+ ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL;
+ return locales_ga.ga_data;
+}
+# endif
+
+/// Lazy initialization of all available locales.
+static void init_locales(void)
+{
+# ifndef WIN32
+ if (!did_init_locales) {
+ did_init_locales = true;
+ locales = find_locales();
+ }
+# endif
+}
+
+# if defined(EXITFREE)
+void free_locales(void)
+{
+ int i;
+ if (locales != NULL) {
+ for (i = 0; locales[i] != NULL; i++) {
+ xfree(locales[i]);
+ }
+ XFREE_CLEAR(locales);
+ }
+}
+# endif
+
+/// Function given to ExpandGeneric() to obtain the possible arguments of the
+/// ":language" command.
+char *get_lang_arg(expand_T *xp, int idx)
+{
+ if (idx == 0) {
+ return "messages";
+ }
+ if (idx == 1) {
+ return "ctype";
+ }
+ if (idx == 2) {
+ return "time";
+ }
+ if (idx == 3) {
+ return "collate";
+ }
+
+ init_locales();
+ if (locales == NULL) {
+ return NULL;
+ }
+ return locales[idx - 4];
+}
+
+/// Function given to ExpandGeneric() to obtain the available locales.
+char *get_locales(expand_T *xp, int idx)
+{
+ init_locales();
+ if (locales == NULL) {
+ return NULL;
+ }
+ return locales[idx];
+}
+
+#endif
diff --git a/src/nvim/locale.h b/src/nvim/locale.h
new file mode 100644
index 0000000000..39735d371f
--- /dev/null
+++ b/src/nvim/locale.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_LOCALE_H
+#define NVIM_LOCALE_H
+
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/types.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "locale.h.generated.h"
+#endif
+#endif // NVIM_LOCALE_H
diff --git a/src/nvim/log.c b/src/nvim/log.c
index 99b17a612b..9bdf327430 100644
--- a/src/nvim/log.c
+++ b/src/nvim/log.c
@@ -60,16 +60,16 @@ static bool log_try_create(char *fname)
static void log_path_init(void)
{
size_t size = sizeof(log_file_path);
- expand_env((char_u *)"$" ENV_LOGFILE, (char_u *)log_file_path, (int)size - 1);
+ expand_env("$" ENV_LOGFILE, log_file_path, (int)size - 1);
if (strequal("$" ENV_LOGFILE, log_file_path)
|| log_file_path[0] == '\0'
- || os_isdir((char_u *)log_file_path)
+ || os_isdir(log_file_path)
|| !log_try_create(log_file_path)) {
// Make $XDG_STATE_HOME if it does not exist.
char *loghome = get_xdg_home(kXDGStateHome);
char *failed_dir = NULL;
bool log_dir_failure = false;
- if (!os_isdir((char_u *)loghome)) {
+ if (!os_isdir(loghome)) {
log_dir_failure = (os_mkdir_recurse(loghome, 0700, &failed_dir) != 0);
}
XFREE_CLEAR(loghome);
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 4d6e6090b8..49d49f76b9 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -59,7 +59,7 @@ static LuaTableProps nlua_traverse_table(lua_State *const lstate)
size_t other_keys_num = 0; // Number of keys that are not string, integral
// or type keys.
LuaTableProps ret;
- memset(&ret, 0, sizeof(ret));
+ CLEAR_FIELD(ret);
if (!lua_checkstack(lstate, lua_gettop(lstate) + 3)) {
semsg(_("E1502: Lua failed to grow stack to %i"), lua_gettop(lstate) + 2);
ret.type = kObjectTypeNil;
diff --git a/src/nvim/lua/converter.h b/src/nvim/lua/converter.h
index 1c9e60e4b2..f6a85900ba 100644
--- a/src/nvim/lua/converter.h
+++ b/src/nvim/lua/converter.h
@@ -6,7 +6,7 @@
#include <stdint.h>
#include "nvim/api/private/defs.h"
-#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/func_attr.h"
typedef struct {
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 661dbfc4c2..42aa13cfc1 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -15,12 +15,14 @@
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/event/loop.h"
#include "nvim/event/time.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_cmds.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/func_attr.h"
@@ -37,7 +39,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/os.h"
#include "nvim/profile.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/undo.h"
#include "nvim/usercmd.h"
#include "nvim/version.h"
@@ -447,7 +449,7 @@ static nlua_ref_state_t *nlua_new_ref_state(lua_State *lstate, bool is_thread)
FUNC_ATTR_NONNULL_ALL
{
nlua_ref_state_t *ref_state = lua_newuserdata(lstate, sizeof(*ref_state));
- memset(ref_state, 0, sizeof(*ref_state));
+ CLEAR_POINTER(ref_state);
ref_state->nil_ref = LUA_NOREF;
ref_state->empty_dict_ref = LUA_NOREF;
if (!is_thread) {
@@ -988,7 +990,7 @@ int nlua_in_fast_event(lua_State *lstate)
static bool viml_func_is_fast(const char *name)
{
- const EvalFuncDef *const fdef = find_internal_func((const char *)name);
+ const EvalFuncDef *const fdef = find_internal_func(name);
if (fdef) {
return fdef->fast;
}
@@ -1025,7 +1027,7 @@ int nlua_call(lua_State *lstate)
// TODO(bfredl): this should be simplified in error handling refactor
force_abort = false;
suppress_errthrow = false;
- current_exception = NULL;
+ did_throw = false;
did_emsg = false;
try_start();
@@ -1091,7 +1093,7 @@ static int nlua_rpc(lua_State *lstate, bool request)
Object result = rpc_send_call(chan_id, name, args, &res_mem, &err);
if (!ERROR_SET(&err)) {
nlua_push_Object(lstate, result, false);
- arena_mem_free(res_mem, NULL);
+ arena_mem_free(res_mem);
}
} else {
if (!rpc_send_event(chan_id, name, args)) {
@@ -1313,12 +1315,11 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name
int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
{
- const linenr_T save_sourcing_lnum = sourcing_lnum;
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SCRIPT, name, 0);
garray_T ga;
char_u *line = NULL;
@@ -1331,7 +1332,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name)
size_t len = strlen(code);
nlua_typval_exec(code, len, name, NULL, 0, false, NULL);
- sourcing_lnum = save_sourcing_lnum;
+ estack_pop();
current_sctx = save_current_sctx;
ga_clear_strings(&ga);
xfree(code);
@@ -1577,7 +1578,7 @@ void ex_luado(exarg_T *const eap)
}
lua_pop(lstate, 1);
check_cursor();
- update_screen(NOT_VALID);
+ update_screen(UPD_NOT_VALID);
}
/// Run lua file
@@ -1661,6 +1662,9 @@ static void nlua_add_treesitter(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
lua_pushcfunction(lstate, tslua_has_language);
lua_setfield(lstate, -2, "_ts_has_language");
+ lua_pushcfunction(lstate, tslua_remove_lang);
+ lua_setfield(lstate, -2, "_ts_remove_language");
+
lua_pushcfunction(lstate, tslua_inspect_lang);
lua_setfield(lstate, -2, "_ts_inspect_language");
@@ -1906,7 +1910,7 @@ void nlua_set_sctx(sctx_T *current)
break;
}
char *source_path = fix_fname(info->source + 1);
- get_current_script_id((char_u *)source_path, current);
+ get_current_script_id(&source_path, current);
xfree(source_path);
current->sc_lnum = info->currentline;
current->sc_seq = -1;
@@ -1939,8 +1943,13 @@ int nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap, bool preview)
// Split args by unescaped whitespace |<f-args>| (nargs dependent)
if (cmd->uc_argt & EX_NOSPC) {
- // Commands where nargs = 1 or "?" fargs is the same as args
- lua_rawseti(lstate, -2, 1);
+ if ((cmd->uc_argt & EX_NEEDARG) || STRLEN(eap->arg)) {
+ // For commands where nargs is 1 or "?" and argument is passed, fargs = { args }
+ lua_rawseti(lstate, -2, 1);
+ } else {
+ // if nargs = "?" and no argument is passed, fargs = {}
+ lua_pop(lstate, 1); // Pop the reference of opts.args
+ }
} else if (eap->args == NULL) {
// For commands with more than one possible argument, split if argument list isn't available.
lua_pop(lstate, 1); // Pop the reference of opts.args
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 2afbbebfe7..78346fd81f 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -24,6 +24,8 @@ typedef struct {
#endif
} nlua_ref_state_t;
+#define NLUA_EXEC_STATIC(cstr, arg, err) nlua_exec(STATIC_CSTR_AS_STRING(cstr), arg, err)
+
#define NLUA_CLEAR_REF(x) \
do { \
/* Take the address to avoid double evaluation. #1375 */ \
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index 6ba0056f48..1b874e673a 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -16,10 +16,11 @@
#include "nvim/buffer_defs.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/event/loop.h"
#include "nvim/event/time.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/func_attr.h"
@@ -473,6 +474,52 @@ static int nlua_stricmp(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
return 1;
}
+#if defined(HAVE_ICONV)
+
+/// Convert string from one encoding to another
+static int nlua_iconv(lua_State *lstate)
+{
+ int narg = lua_gettop(lstate);
+
+ if (narg < 3) {
+ return luaL_error(lstate, "Expected at least 3 arguments");
+ }
+
+ for (int i = 1; i <= 3; i++) {
+ if (lua_type(lstate, i) != LUA_TSTRING) {
+ return luaL_argerror(lstate, i, "expected string");
+ }
+ }
+
+ size_t str_len = 0;
+ const char *str = lua_tolstring(lstate, 1, &str_len);
+
+ char_u *from = (char_u *)enc_canonize(enc_skip((char *)lua_tolstring(lstate, 2, NULL)));
+ char_u *to = (char_u *)enc_canonize(enc_skip((char *)lua_tolstring(lstate, 3, NULL)));
+
+ vimconv_T vimconv;
+ vimconv.vc_type = CONV_NONE;
+ convert_setup_ext(&vimconv, from, false, to, false);
+
+ char_u *ret = (char_u *)string_convert(&vimconv, (char *)str, &str_len);
+
+ convert_setup(&vimconv, NULL, NULL);
+
+ xfree(from);
+ xfree(to);
+
+ if (ret == NULL) {
+ lua_pushnil(lstate);
+ } else {
+ lua_pushlstring(lstate, (char *)ret, str_len);
+ xfree(ret);
+ }
+
+ return 1;
+}
+
+#endif
+
void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
{
if (!is_thread) {
@@ -518,6 +565,13 @@ void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
// vim.spell
luaopen_spell(lstate);
lua_setfield(lstate, -2, "spell");
+
+#if defined(HAVE_ICONV)
+ // vim.iconv
+ // depends on p_ambw, p_emoji
+ lua_pushcfunction(lstate, &nlua_iconv);
+ lua_setfield(lstate, -2, "iconv");
+#endif
}
// vim.mpack
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index f0d847e352..7ff4fbbff4 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -14,11 +14,14 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <uv.h>
#include "nvim/api/private/helpers.h"
#include "nvim/buffer.h"
#include "nvim/lib/kvec.h"
+#include "nvim/log.h"
#include "nvim/lua/treesitter.h"
+#include "nvim/map.h"
#include "nvim/memline.h"
#include "tree_sitter/api.h"
@@ -85,6 +88,10 @@ static struct luaL_Reg node_meta[] = {
{ "prev_sibling", node_prev_sibling },
{ "next_named_sibling", node_next_named_sibling },
{ "prev_named_sibling", node_prev_named_sibling },
+ { "named_children", node_named_children },
+ { "root", node_root },
+ { "byte_length", node_byte_length },
+
{ NULL, NULL }
};
@@ -145,18 +152,27 @@ int tslua_has_language(lua_State *L)
return 1;
}
+// Creates the language into the internal language map.
+//
+// Returns true if the language is correctly loaded in the language map
int tslua_add_language(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
const char *lang_name = luaL_checkstring(L, 2);
+ const char *symbol_name = lang_name;
+
+ if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) {
+ symbol_name = luaL_checkstring(L, 3);
+ }
if (pmap_has(cstr_t)(&langs, lang_name)) {
- return 0;
+ lua_pushboolean(L, true);
+ return 1;
}
#define BUFSIZE 128
char symbol_buf[BUFSIZE];
- snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", lang_name);
+ snprintf(symbol_buf, BUFSIZE, "tree_sitter_%s", symbol_name);
#undef BUFSIZE
uv_lib_t lib;
@@ -179,6 +195,7 @@ int tslua_add_language(lua_State *L)
TSLanguage *lang = lang_parser();
if (lang == NULL) {
+ uv_dlclose(&lib);
return luaL_error(L, "Failed to load parser %s: internal error", path);
}
@@ -198,6 +215,19 @@ int tslua_add_language(lua_State *L)
return 1;
}
+int tslua_remove_lang(lua_State *L)
+{
+ const char *lang_name = luaL_checkstring(L, 1);
+ bool present = pmap_has(cstr_t)(&langs, lang_name);
+ if (present) {
+ char *key = (char *)pmap_key(cstr_t)(&langs, lang_name);
+ pmap_del(cstr_t)(&langs, lang_name);
+ xfree(key);
+ }
+ lua_pushboolean(L, present);
+ return 1;
+}
+
int tslua_inspect_lang(lua_State *L)
{
const char *lang_name = luaL_checkstring(L, 1);
@@ -593,7 +623,7 @@ void push_tree(lua_State *L, TSTree *tree, bool do_copy)
lua_setfenv(L, -2); // [udata]
}
-static TSTree **tree_check(lua_State *L, uint16_t index)
+static TSTree **tree_check(lua_State *L, int index)
{
TSTree **ud = luaL_checkudata(L, index, TS_META_TREE);
return ud;
@@ -1036,6 +1066,57 @@ static int node_prev_named_sibling(lua_State *L)
return 1;
}
+static int node_named_children(lua_State *L)
+{
+ TSNode source;
+ if (!node_check(L, 1, &source)) {
+ return 0;
+ }
+ TSTreeCursor cursor = ts_tree_cursor_new(source);
+
+ lua_newtable(L);
+ int curr_index = 0;
+
+ if (ts_tree_cursor_goto_first_child(&cursor)) {
+ do {
+ TSNode node = ts_tree_cursor_current_node(&cursor);
+ if (ts_node_is_named(node)) {
+ push_node(L, node, 1);
+ lua_rawseti(L, -2, ++curr_index);
+ }
+ } while (ts_tree_cursor_goto_next_sibling(&cursor));
+ }
+
+ ts_tree_cursor_delete(&cursor);
+ return 1;
+}
+
+static int node_root(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+
+ TSNode root = ts_tree_root_node(node.tree);
+ push_node(L, root, 1);
+ return 1;
+}
+
+static int node_byte_length(lua_State *L)
+{
+ TSNode node;
+ if (!node_check(L, 1, &node)) {
+ return 0;
+ }
+
+ uint32_t start_byte = ts_node_start_byte(node);
+ uint32_t end_byte = ts_node_end_byte(node);
+
+ lua_pushnumber(L, end_byte - start_byte);
+ return 1;
+}
+
/// assumes the match table being on top of the stack
static void set_match(lua_State *L, TSQueryMatch *match, int nodeidx)
{
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index 71f85385b6..b2b5dfedee 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -269,9 +269,9 @@ int nlua_xdl_diff(lua_State *lstate)
xpparam_t params;
xdemitcb_t ecb;
- memset(&cfg, 0, sizeof(cfg));
- memset(&params, 0, sizeof(params));
- memset(&ecb, 0, sizeof(ecb));
+ CLEAR_FIELD(cfg);
+ CLEAR_FIELD(params);
+ CLEAR_FIELD(ecb);
NluaXdiffMode mode = kNluaXdiffModeUnified;
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index a896a406d1..5601db274b 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -31,7 +31,7 @@
/// @return `s, sizeof(s) - 1`
#define S_LEN(s) (s), (sizeof(s) - 1)
-/// LINEEMPTY() - return TRUE if the line is empty
+/// LINEEMPTY() - return true if the line is empty
#define LINEEMPTY(p) (*ml_get(p) == NUL)
// toupper() and tolower() that use the current locale.
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 494ff0b4af..883f946cad 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
@@ -16,6 +17,7 @@
#include "nvim/decoration.h"
#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
@@ -23,23 +25,19 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/hashtab.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/iconv.h"
#include "nvim/if_cscope.h"
#include "nvim/insexpand.h"
+#include "nvim/locale.h"
+#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
-#include "nvim/ui_client.h"
-#include "nvim/vim.h"
-#ifdef HAVE_LOCALE_H
-# include <locale.h>
-#endif
-#include "nvim/garray.h"
-#include "nvim/grid.h"
-#include "nvim/log.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -50,6 +48,7 @@
#include "nvim/normal.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/fileio.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
@@ -57,18 +56,20 @@
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/profile.h"
#include "nvim/quickfix.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/shada.h"
#include "nvim/sign.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
+#include "nvim/ui_client.h"
#include "nvim/ui_compositor.h"
#include "nvim/version.h"
+#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef WIN32
# include "nvim/os/os_win_console.h"
@@ -159,6 +160,7 @@ bool event_teardown(void)
void early_init(mparm_T *paramp)
{
env_init();
+ estack_init();
cmdline_init();
eval_init(); // init global variables
init_path(argv0 ? argv0 : "nvim");
@@ -475,7 +477,7 @@ int main(int argc, char **argv)
if (exmode_active || use_remote_ui || use_builtin_ui) {
// Don't clear the screen when starting in Ex mode, or when a UI might have
// displayed messages.
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
} else {
screenclear(); // clear screen
TIME_MSG("clearing screen");
@@ -504,7 +506,7 @@ int main(int argc, char **argv)
// When started with "-q errorfile" jump to first error now.
if (params.edit_type == EDIT_QF) {
- qf_jump(NULL, 0, 0, FALSE);
+ qf_jump(NULL, 0, 0, false);
TIME_MSG("jump to first error");
}
@@ -516,7 +518,7 @@ int main(int argc, char **argv)
if (params.diff_mode) {
// set options in each window for "nvim -d".
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- diff_win_options(wp, TRUE);
+ diff_win_options(wp, true);
}
}
@@ -535,7 +537,7 @@ int main(int argc, char **argv)
starting = 0;
RedrawingDisabled = 0;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
no_wait_return = false;
// 'autochdir' has been postponed.
@@ -709,8 +711,8 @@ void getout(int exitval)
if (did_emsg) {
// give the user a chance to read the (error) message
- no_wait_return = FALSE;
- wait_return(FALSE);
+ no_wait_return = false;
+ wait_return(false);
}
// Position the cursor again, the autocommands may have moved it
@@ -797,30 +799,6 @@ static int get_number_arg(const char *p, int *idx, int def)
return def;
}
-#if defined(HAVE_LOCALE_H)
-/// Setup to use the current locale (for ctype() and many other things).
-static void init_locale(void)
-{
- setlocale(LC_ALL, "");
-
-# ifdef LC_NUMERIC
- // Make sure strtod() uses a decimal point, not a comma.
- setlocale(LC_NUMERIC, "C");
-# endif
-
- char localepath[MAXPATHL] = { 0 };
- snprintf(localepath, sizeof(localepath), "%s", get_vim_var_str(VV_PROGPATH));
- char *tail = path_tail_with_sep(localepath);
- *tail = NUL;
- tail = path_tail(localepath);
- xstrlcpy(tail, "share/locale",
- sizeof(localepath) - (size_t)(tail - localepath));
- bindtextdomain(PROJECT_NAME, localepath);
- textdomain(PROJECT_NAME);
- TIME_MSG("locale set");
-}
-#endif
-
static uint64_t server_connect(char *server_addr, const char **errmsg)
{
if (server_addr == NULL) {
@@ -1048,7 +1026,7 @@ static void command_line_scan(mparm_T *parmp)
} else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) {
parmp->use_vimrc = "NONE";
parmp->clean = true;
- set_option_value("shadafile", 0L, "NONE", 0);
+ set_option_value_give_err("shadafile", 0L, "NONE", 0);
} else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) {
nlua_disable_preload = true;
} else {
@@ -1062,7 +1040,7 @@ static void command_line_scan(mparm_T *parmp)
}
break;
case 'A': // "-A" start in Arabic mode.
- set_option_value("arabic", 1L, NULL, 0);
+ set_option_value_give_err("arabic", 1L, NULL, 0);
break;
case 'b': // "-b" binary mode.
// Needs to be effective before expanding file names, because
@@ -1093,10 +1071,10 @@ static void command_line_scan(mparm_T *parmp)
os_exit(0);
case 'H': // "-H" start in Hebrew mode: rl + hkmap set.
p_hkmap = true;
- set_option_value("rl", 1L, NULL, 0);
+ set_option_value_give_err("rl", 1L, NULL, 0);
break;
case 'l': // "-l" lisp mode, 'lisp' and 'showmatch' on.
- set_option_value("lisp", 1L, NULL, 0);
+ set_option_value_give_err("lisp", 1L, NULL, 0);
p_sm = true;
break;
case 'M': // "-M" no changes or writing of files
@@ -1177,7 +1155,7 @@ static void command_line_scan(mparm_T *parmp)
// default is 10: a little bit verbose
p_verbose = get_number_arg(argv[0], &argv_idx, 10);
if (argv[0][argv_idx] != NUL) {
- set_option_value("verbosefile", 0L, argv[0] + argv_idx, 0);
+ set_option_value_give_err("verbosefile", 0L, argv[0] + argv_idx, 0);
argv_idx = (int)STRLEN(argv[0]);
}
break;
@@ -1185,7 +1163,7 @@ static void command_line_scan(mparm_T *parmp)
// "-w {scriptout}" write to script
if (ascii_isdigit(((char_u *)argv[0])[argv_idx])) {
n = get_number_arg(argv[0], &argv_idx, 10);
- set_option_value("window", n, NULL, 0);
+ set_option_value_give_err("window", n, NULL, 0);
break;
}
want_argument = true;
@@ -1242,8 +1220,8 @@ static void command_line_scan(mparm_T *parmp)
} else if (argv[0][0] == '-') {
// "-S" followed by another option: use default session file.
a = SESSION_FILE;
- ++argc;
- --argv;
+ argc++;
+ argv--;
} else {
a = argv[0];
}
@@ -1280,7 +1258,7 @@ static void command_line_scan(mparm_T *parmp)
break;
case 'i': // "-i {shada}" use for shada
- set_option_value("shadafile", 0L, argv[0], 0);
+ set_option_value_give_err("shadafile", 0L, argv[0], 0);
break;
case 's': { // "-s {scriptin}" read from script file
@@ -1330,7 +1308,7 @@ scripterror:
if (ascii_isdigit(*((char_u *)argv[0]))) {
argv_idx = 0;
n = get_number_arg(argv[0], &argv_idx, 10);
- set_option_value("window", n, NULL, 0);
+ set_option_value_give_err("window", n, NULL, 0);
argv_idx = -1;
break;
}
@@ -1364,8 +1342,8 @@ scripterror:
ga_grow(&global_alist.al_ga, 1);
char *p = xstrdup(argv[0]);
- if (parmp->diff_mode && os_isdir((char_u *)p) && GARGCOUNT > 0
- && !os_isdir((char_u *)alist_name(&GARGLIST[0]))) {
+ if (parmp->diff_mode && os_isdir(p) && GARGCOUNT > 0
+ && !os_isdir(alist_name(&GARGLIST[0]))) {
char *r = concat_fnames(p, path_tail(alist_name(&GARGLIST[0])), true);
xfree(p);
p = r;
@@ -1418,7 +1396,7 @@ scripterror:
* copied, so that they can be changed. */
static void init_params(mparm_T *paramp, int argc, char **argv)
{
- memset(paramp, 0, sizeof(*paramp));
+ CLEAR_POINTER(paramp);
paramp->argc = argc;
paramp->argv = argv;
paramp->use_debug_break_level = -1;
@@ -1512,7 +1490,7 @@ static void handle_quickfix(mparm_T *paramp)
set_string_option_direct("ef", -1, paramp->use_ef, OPT_FREE, SID_CARG);
}
vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef);
- if (qf_init(NULL, (char *)p_ef, p_efm, true, (char *)IObuff, (char *)p_menc) < 0) {
+ if (qf_init(NULL, (char *)p_ef, p_efm, true, (char *)IObuff, p_menc) < 0) {
msg_putchar('\n');
os_exit(3);
}
@@ -1613,9 +1591,9 @@ static void create_windows(mparm_T *parmp)
// Watch out for autocommands that delete a window.
//
// Don't execute Win/Buf Enter/Leave autocommands here
- ++autocmd_no_enter;
- ++autocmd_no_leave;
- dorewind = TRUE;
+ autocmd_no_enter++;
+ autocmd_no_leave++;
+ dorewind = true;
while (done++ < 1000) {
if (dorewind) {
if (parmp->window_layout == WIN_TABS) {
@@ -1634,7 +1612,7 @@ static void create_windows(mparm_T *parmp)
}
curwin = curwin->w_next;
}
- dorewind = FALSE;
+ dorewind = false;
curbuf = curwin->w_buffer;
if (curbuf->b_ml.ml_mfp == NULL) {
// Set 'foldlevel' to 'foldlevelstart' if it's not negative..
@@ -1643,15 +1621,15 @@ static void create_windows(mparm_T *parmp)
}
// When getting the ATTENTION prompt here, use a dialog.
swap_exists_action = SEA_DIALOG;
- set_buflisted(TRUE);
+ set_buflisted(true);
// create memfile, read file
- (void)open_buffer(FALSE, NULL, 0);
+ (void)open_buffer(false, NULL, 0);
if (swap_exists_action == SEA_QUIT) {
if (got_int || only_one_window()) {
// abort selected or quit and only one window
- did_emsg = FALSE; // avoid hit-enter prompt
+ did_emsg = false; // avoid hit-enter prompt
getout(1);
}
// We can't close the window, it would disturb what
@@ -1663,7 +1641,7 @@ static void create_windows(mparm_T *parmp)
} else {
handle_swap_exists(NULL);
}
- dorewind = TRUE; // start again
+ dorewind = true; // start again
}
os_breakcheck();
if (got_int) {
@@ -1677,8 +1655,8 @@ static void create_windows(mparm_T *parmp)
curwin = firstwin;
}
curbuf = curwin->w_buffer;
- --autocmd_no_enter;
- --autocmd_no_leave;
+ autocmd_no_enter--;
+ autocmd_no_leave--;
}
}
@@ -1695,8 +1673,8 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
/*
* Don't execute Win/Buf Enter/Leave autocommands here
*/
- ++autocmd_no_enter;
- ++autocmd_no_leave;
+ autocmd_no_enter++;
+ autocmd_no_leave++;
// When w_arg_idx is -1 remove the window (see create_windows()).
if (curwin->w_arg_idx == -1) {
@@ -1705,7 +1683,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
arg_idx = 1;
- for (i = 1; i < parmp->window_count; ++i) {
+ for (i = 1; i < parmp->window_count; i++) {
if (cwd != NULL) {
os_chdir((char *)cwd);
}
@@ -1729,9 +1707,9 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
if (i == 1) {
char buf[100];
- p_shm_save = xstrdup((char *)p_shm);
+ p_shm_save = xstrdup(p_shm);
snprintf(buf, sizeof(buf), "F%s", p_shm);
- set_option_value("shm", 0L, buf, 0);
+ set_option_value_give_err("shm", 0L, buf, 0);
}
} else {
if (curwin->w_next == NULL) { // just checking
@@ -1756,7 +1734,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
// abort or quit selected
if (got_int || only_one_window()) {
// abort selected and only one window
- did_emsg = FALSE; // avoid hit-enter prompt
+ did_emsg = false; // avoid hit-enter prompt
getout(1);
}
win_close(curwin, true, false);
@@ -1775,14 +1753,14 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
if (p_shm_save != NULL) {
- set_option_value("shm", 0L, p_shm_save, 0);
+ set_option_value_give_err("shm", 0L, p_shm_save, 0);
xfree(p_shm_save);
}
if (parmp->window_layout == WIN_TABS) {
goto_tabpage(1);
}
- --autocmd_no_enter;
+ autocmd_no_enter--;
// make the first window the current window
win = firstwin;
@@ -1796,7 +1774,7 @@ static void edit_buffers(mparm_T *parmp, char_u *cwd)
}
win_enter(win, false);
- --autocmd_no_leave;
+ autocmd_no_leave--;
TIME_MSG("editing files in windows");
if (parmp->window_count > 1 && parmp->window_layout != WIN_TABS) {
win_equal(curwin, false, 'b'); // adjust heights
@@ -1814,12 +1792,12 @@ static void exe_pre_commands(mparm_T *parmp)
if (cnt > 0) {
curwin->w_cursor.lnum = 0; // just in case..
- sourcing_name = _("pre-vimrc command line");
+ estack_push(ETYPE_ARGS, _("pre-vimrc command line"), 0);
current_sctx.sc_sid = SID_CMDARG;
for (i = 0; i < cnt; i++) {
do_cmdline_cmd(cmds[i]);
}
- sourcing_name = NULL;
+ estack_pop();
current_sctx.sc_sid = 0;
TIME_MSG("--cmd commands");
}
@@ -1837,11 +1815,11 @@ static void exe_commands(mparm_T *parmp)
* pattern on line 1. But don't move the cursor when an autocommand
* with g`" was used.
*/
- msg_scroll = TRUE;
+ msg_scroll = true;
if (parmp->tagname == NULL && curwin->w_cursor.lnum <= 1) {
curwin->w_cursor.lnum = 0;
}
- sourcing_name = "command line";
+ estack_push(ETYPE_ARGS, "command line", 0);
current_sctx.sc_sid = SID_CARG;
current_sctx.sc_seq = 0;
for (i = 0; i < parmp->n_commands; i++) {
@@ -1850,19 +1828,19 @@ static void exe_commands(mparm_T *parmp)
xfree(parmp->commands[i]);
}
}
- sourcing_name = NULL;
+ estack_pop();
current_sctx.sc_sid = 0;
if (curwin->w_cursor.lnum == 0) {
curwin->w_cursor.lnum = 1;
}
if (!exmode_active) {
- msg_scroll = FALSE;
+ msg_scroll = false;
}
// When started with "-q errorfile" jump to first error again.
if (parmp->edit_type == EDIT_QF) {
- qf_jump(NULL, 0, 0, FALSE);
+ qf_jump(NULL, 0, 0, false);
}
TIME_MSG("executing command arguments");
}
@@ -2059,17 +2037,14 @@ static int execute_env(char *env)
{
const char *initstr = os_getenv(env);
if (initstr != NULL) {
- char_u *save_sourcing_name = (char_u *)sourcing_name;
- linenr_T save_sourcing_lnum = sourcing_lnum;
- sourcing_name = env;
- sourcing_lnum = 0;
+ estack_push(ETYPE_ENV, env, 0);
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_ENV;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
do_cmdline_cmd((char *)initstr);
- sourcing_name = (char *)save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+
+ estack_pop();
current_sctx = save_current_sctx;
return OK;
}
diff --git a/src/nvim/main.h b/src/nvim/main.h
index d5384ecc95..0c497a7c0e 100644
--- a/src/nvim/main.h
+++ b/src/nvim/main.h
@@ -2,7 +2,6 @@
#define NVIM_MAIN_H
#include "nvim/event/loop.h"
-#include "nvim/normal.h"
// Maximum number of commands from + or -c arguments.
#define MAX_ARG_CMDS 10
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 342b1b0d47..333fb847f6 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -29,6 +29,7 @@
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/regexp.h"
+#include "nvim/runtime.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
@@ -149,8 +150,8 @@ static void showmap(mapblock_T *mp, bool local)
{
size_t len = 1;
- if (message_filtered(mp->m_keys) && message_filtered(mp->m_str)
- && (mp->m_desc == NULL || message_filtered((char_u *)mp->m_desc))) {
+ if (message_filtered((char *)mp->m_keys) && message_filtered(mp->m_str)
+ && (mp->m_desc == NULL || message_filtered(mp->m_desc))) {
return;
}
@@ -202,7 +203,7 @@ static void showmap(mapblock_T *mp, bool local)
} else if (mp->m_str[0] == NUL) {
msg_puts_attr("<Nop>", HL_ATTR(HLF_8));
} else {
- msg_outtrans_special(mp->m_str, false, 0);
+ msg_outtrans_special((char_u *)mp->m_str, false, 0);
}
if (mp->m_desc != NULL) {
@@ -292,7 +293,7 @@ static bool set_maparg_lhs_rhs(const char *const orig_lhs, const size_t orig_lhs
replaced = replace_termcodes(orig_rhs, orig_rhs_len, &rhs_buf, REPTERM_DO_LT, NULL,
cpo_flags);
mapargs->rhs_len = STRLEN(replaced);
- // XXX: replace_termcodes may produce an empty string even if orig_rhs is non-empty
+ // NB: replace_termcodes may produce an empty string even if orig_rhs is non-empty
// (e.g. a single ^V, see :h map-empty-rhs)
mapargs->rhs_is_noop = orig_rhs_len != 0 && mapargs->rhs_len == 0;
mapargs->rhs = (char_u *)replaced;
@@ -333,7 +334,7 @@ static int str_to_mapargs(const char_u *strargs, bool is_unmap, MapArguments *ma
{
const char_u *to_parse = strargs;
to_parse = (char_u *)skipwhite((char *)to_parse);
- memset(mapargs, 0, sizeof(*mapargs));
+ CLEAR_POINTER(mapargs);
// Accept <buffer>, <nowait>, <silent>, <expr>, <script>, and <unique> in
// any order.
@@ -448,8 +449,8 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
}
mp->m_keys = vim_strsave(keys);
- mp->m_str = args->rhs;
- mp->m_orig_str = args->orig_rhs;
+ mp->m_str = (char *)args->rhs;
+ mp->m_orig_str = (char *)args->orig_rhs;
mp->m_luaref = args->rhs_lua;
if (!simplified) {
args->rhs = NULL;
@@ -469,7 +470,7 @@ static void map_add(buf_T *buf, mapblock_T **map_table, mapblock_T **abbr_table,
mp->m_script_ctx.sc_lnum = lnum;
} else {
mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&mp->m_script_ctx);
}
mp->m_desc = NULL;
@@ -703,7 +704,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
} else { // do we have a match?
if (round) { // second round: Try unmap "rhs" string
n = (int)STRLEN(mp->m_str);
- p = mp->m_str;
+ p = (char_u *)mp->m_str;
} else {
n = mp->m_keylen;
p = mp->m_keys;
@@ -760,8 +761,8 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
XFREE_CLEAR(mp->m_str);
XFREE_CLEAR(mp->m_orig_str);
}
- mp->m_str = args->rhs;
- mp->m_orig_str = args->orig_rhs;
+ mp->m_str = (char *)args->rhs;
+ mp->m_orig_str = (char *)args->orig_rhs;
mp->m_luaref = args->rhs_lua;
if (!keyround1_simplified) {
args->rhs = NULL;
@@ -776,7 +777,7 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
mp->m_expr = args->expr;
mp->m_replace_keycodes = args->replace_keycodes;
mp->m_script_ctx = current_sctx;
- mp->m_script_ctx.sc_lnum += sourcing_lnum;
+ mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&mp->m_script_ctx);
if (args->desc != NULL) {
mp->m_desc = xstrdup(args->desc);
@@ -964,7 +965,7 @@ static int get_map_mode(char **cmdp, bool forceit)
/// Clear all mappings (":mapclear") or abbreviations (":abclear").
/// "abbr" should be false for mappings, true for abbreviations.
/// This function used to be called map_clear().
-static void do_mapclear(char_u *cmdp, char_u *arg, int forceit, int abbr)
+static void do_mapclear(char *cmdp, char_u *arg, int forceit, int abbr)
{
int mode;
int local;
@@ -975,7 +976,7 @@ static void do_mapclear(char_u *cmdp, char_u *arg, int forceit, int abbr)
return;
}
- mode = get_map_mode((char **)&cmdp, forceit);
+ mode = get_map_mode(&cmdp, forceit);
map_clear_mode(curbuf, mode, local, abbr);
}
@@ -1051,9 +1052,9 @@ bool map_to_exists(const char *const str, const char *const modechars, const boo
int mode = 0;
int retval;
- char_u *buf = NULL;
+ char *buf = NULL;
const char_u *const rhs = (char_u *)replace_termcodes(str, strlen(str),
- (char **)&buf, REPTERM_DO_LT,
+ &buf, REPTERM_DO_LT,
NULL, CPO_TO_CPO_FLAGS);
#define MAPMODE(mode, modechars, chr, modeflags) \
@@ -1112,7 +1113,7 @@ int map_to_exists_mode(const char *const rhs, const int mode, const bool abbr)
mp = maphash[hash];
}
for (; mp; mp = mp->m_next) {
- if ((mp->m_mode & mode) && strstr((char *)mp->m_str, rhs) != NULL) {
+ if ((mp->m_mode & mode) && strstr(mp->m_str, rhs) != NULL) {
return true;
}
}
@@ -1194,14 +1195,14 @@ static char_u *translate_mapping(char_u *str, int cpo_flags)
/// @param forceit true if '!' given
/// @param isabbrev true if abbreviation
/// @param isunmap true if unmap/unabbrev command
-char_u *set_context_in_map_cmd(expand_T *xp, char_u *cmd, char_u *arg, bool forceit, bool isabbrev,
+char_u *set_context_in_map_cmd(expand_T *xp, char *cmd, char_u *arg, bool forceit, bool isabbrev,
bool isunmap, cmdidx_T cmdidx)
{
if (forceit && cmdidx != CMD_map && cmdidx != CMD_unmap) {
xp->xp_context = EXPAND_NOTHING;
} else {
if (isunmap) {
- expand_mapmodes = get_map_mode((char **)&cmd, forceit || isabbrev);
+ expand_mapmodes = get_map_mode(&cmd, forceit || isabbrev);
} else {
expand_mapmodes = MODE_INSERT | MODE_CMDLINE;
if (!isabbrev) {
@@ -1505,7 +1506,7 @@ bool check_abbr(int c, char_u *ptr, int col, int mincol)
if (mp->m_expr) {
s = eval_map_expr(mp, c);
} else {
- s = mp->m_str;
+ s = (char_u *)mp->m_str;
}
if (s != NULL) {
// insert the to string
@@ -1541,7 +1542,7 @@ char_u *eval_map_expr(mapblock_T *mp, int c)
// Remove escaping of K_SPECIAL, because "str" is in a format to be used as
// typeahead.
if (mp->m_luaref == LUA_NOREF) {
- expr = vim_strsave(mp->m_str);
+ expr = vim_strsave((char_u *)mp->m_str);
vim_unescape_ks(expr);
}
@@ -1638,7 +1639,7 @@ int makemap(FILE *fd, buf_T *buf)
if (mp->m_luaref != LUA_NOREF) {
continue;
}
- for (p = mp->m_str; *p != NUL; p++) {
+ for (p = (char_u *)mp->m_str; *p != NUL; p++) {
if (p[0] == K_SPECIAL && p[1] == KS_EXTRA
&& p[2] == KE_SNR) {
break;
@@ -1784,7 +1785,7 @@ int makemap(FILE *fd, buf_T *buf)
if (putc(' ', fd) < 0
|| put_escstr(fd, mp->m_keys, 0) == FAIL
|| putc(' ', fd) < 0
- || put_escstr(fd, mp->m_str, 1) == FAIL
+ || put_escstr(fd, (char_u *)mp->m_str, 1) == FAIL
|| put_eol(fd) < 0) {
return FAIL;
}
@@ -1955,7 +1956,7 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb
*local_ptr = local;
}
*rhs_lua = mp->m_luaref;
- return mp->m_luaref == LUA_NOREF ? mp->m_str : NULL;
+ return mp->m_luaref == LUA_NOREF ? (char_u *)mp->m_str : NULL;
}
}
}
@@ -1966,7 +1967,7 @@ char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapb
}
/// "hasmapto()" function
-void f_hasmapto(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *mode;
const char *const name = tv_get_string(&argvars[0]);
@@ -2079,7 +2080,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
}
char *keys_buf = NULL;
- char_u *alt_keys_buf = NULL;
+ char *alt_keys_buf = NULL;
bool did_simplify = false;
const int flags = REPTERM_FROM_PART | REPTERM_DO_LT;
const int mode = get_map_mode((char **)&which, 0);
@@ -2096,9 +2097,9 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
// preferred for printing, like in do_map().
(void)replace_termcodes(keys,
STRLEN(keys),
- (char **)&alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL,
+ &alt_keys_buf, flags | REPTERM_NO_SIMPLIFY, NULL,
CPO_TO_CPO_FLAGS);
- rhs = check_map(alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
+ rhs = check_map((char_u *)alt_keys_buf, mode, exact, false, abbr, &mp, &buffer_local, &rhs_lua);
}
if (!get_dict) {
@@ -2126,7 +2127,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
}
/// "mapset()" function
-void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char buf[NUMBUFLEN];
const char *which = tv_get_string_buf_chk(&argvars[0], buf);
@@ -2194,13 +2195,13 @@ void f_mapset(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "maparg()" function
-void f_maparg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_maparg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_maparg(argvars, rettv, true);
}
/// "mapcheck()" function
-void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_mapcheck(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
get_maparg(argvars, rettv, false);
}
@@ -2321,7 +2322,7 @@ void langmap_set(void)
ga_clear(&langmap_mapga); // clear the previous map first
langmap_init(); // back to one-to-one map
- for (p = p_langmap; p[0] != NUL;) {
+ for (p = (char_u *)p_langmap; p[0] != NUL;) {
for (p2 = p; p2[0] != NUL && p2[0] != ',' && p2[0] != ';';
MB_PTR_ADV(p2)) {
if (p2[0] == '\\' && p2[1] != NUL) {
@@ -2439,13 +2440,13 @@ void ex_unmap(exarg_T *eap)
/// ":mapclear" and friends.
void ex_mapclear(exarg_T *eap)
{
- do_mapclear((char_u *)eap->cmd, (char_u *)eap->arg, eap->forceit, false);
+ do_mapclear(eap->cmd, (char_u *)eap->arg, eap->forceit, false);
}
/// ":abclear" and friends.
void ex_abclear(exarg_T *eap)
{
- do_mapclear((char_u *)eap->cmd, (char_u *)eap->arg, true, true);
+ do_mapclear(eap->cmd, (char_u *)eap->arg, true, true);
}
/// Set, tweak, or remove a mapping in a mode. Acts as the implementation for
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 344d55a473..b430c167ce 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -20,7 +20,6 @@
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
-#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
@@ -36,9 +35,9 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/quickfix.h"
-#include "nvim/search.h"
#include "nvim/sign.h"
#include "nvim/strings.h"
+#include "nvim/textobject.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
#include "nvim/map.h"
@@ -116,7 +115,7 @@ void clear_fmark(fmark_T *fm)
FUNC_ATTR_NONNULL_ALL
{
free_fmark(*fm);
- memset(fm, 0, sizeof(*fm));
+ CLEAR_POINTER(fm);
}
/*
@@ -832,7 +831,7 @@ static void fname2fnum(xfmark_T *fm)
)) {
int len;
- expand_env((char_u *)"~/", NameBuff, MAXPATHL);
+ expand_env("~/", NameBuff, MAXPATHL);
len = (int)STRLEN(NameBuff);
STRLCPY(NameBuff + len, fm->fname + 2, MAXPATHL - len);
} else {
@@ -841,7 +840,7 @@ static void fname2fnum(xfmark_T *fm)
// Try to shorten the file name.
os_dirname(IObuff, IOSIZE);
- p = path_shorten_fname(NameBuff, IObuff);
+ p = path_shorten_fname((char_u *)NameBuff, IObuff);
// buflist_new() will call fmarks_check_names()
(void)buflist_new((char *)NameBuff, (char *)p, (linenr_T)1, 0);
@@ -862,12 +861,12 @@ void fmarks_check_names(buf_T *buf)
return;
}
- for (i = 0; i < NGLOBALMARKS; ++i) {
+ for (i = 0; i < NGLOBALMARKS; i++) {
fmarks_check_one(&namedfm[i], name, buf);
}
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- for (i = 0; i < wp->w_jumplistlen; ++i) {
+ for (i = 0; i < wp->w_jumplistlen; i++) {
fmarks_check_one(&wp->w_jumplist[i], name, buf);
}
}
@@ -1006,10 +1005,10 @@ void ex_marks(exarg_T *eap)
}
show_one_mark('\'', arg, &curwin->w_pcmark, NULL, true);
- for (i = 0; i < NMARKS; ++i) {
+ for (i = 0; i < NMARKS; i++) {
show_one_mark(i + 'a', arg, &curbuf->b_namedm[i].mark, NULL, true);
}
- for (i = 0; i < NGLOBALMARKS; ++i) {
+ for (i = 0; i < NGLOBALMARKS; i++) {
if (namedfm[i].fmark.fnum != 0) {
name = fm_getname(&namedfm[i].fmark, 15);
} else {
@@ -1069,7 +1068,7 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu
name = mark_line(p, 15);
mustfree = true;
}
- if (!message_filtered(name)) {
+ if (!message_filtered((char *)name)) {
if (!did_title) {
// Highlight title
msg_puts_title(_("\nmark line col file/text"));
@@ -1080,7 +1079,7 @@ static void show_one_mark(int c, char_u *arg, pos_T *p, char_u *name_arg, int cu
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);
+ msg_outtrans_attr((char *)name, current ? HL_ATTR(HLF_D) : 0);
}
}
ui_flush(); // show one line at a time
@@ -1133,7 +1132,7 @@ void ex_delmarks(exarg_T *eap)
from = to = *p;
}
- for (i = from; i <= to; ++i) {
+ for (i = from; i <= to; i++) {
if (lower) {
curbuf->b_namedm[i - 'a'].mark.lnum = 0;
} else {
@@ -1185,7 +1184,7 @@ void ex_jumps(exarg_T *eap)
cleanup_jumplist(curwin, true);
// Highlight title
msg_puts_title(_("\n jump line col file/text"));
- for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i) {
+ for (i = 0; i < curwin->w_jumplistlen && !got_int; i++) {
if (curwin->w_jumplist[i].fmark.mark.lnum != 0) {
name = fm_getname(&curwin->w_jumplist[i].fmark, 16);
@@ -1195,7 +1194,7 @@ void ex_jumps(exarg_T *eap)
name = vim_strsave((char_u *)"-invalid-");
}
// apply :filter /pat/ or file name not available
- if (name == NULL || message_filtered(name)) {
+ if (name == NULL || message_filtered((char *)name)) {
xfree(name);
continue;
}
@@ -1210,7 +1209,7 @@ void ex_jumps(exarg_T *eap)
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,
+ msg_outtrans_attr((char *)name,
curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
xfree(name);
@@ -1241,7 +1240,7 @@ void ex_changes(exarg_T *eap)
// Highlight title
msg_puts_title(_("\nchange line col text"));
- for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i) {
+ for (i = 0; i < curbuf->b_changelistlen && !got_int; i++) {
if (curbuf->b_changelist[i].mark.lnum != 0) {
msg_putchar('\n');
if (got_int) {
@@ -1255,7 +1254,7 @@ void ex_changes(exarg_T *eap)
curbuf->b_changelist[i].mark.col);
msg_outtrans((char *)IObuff);
name = mark_line(&curbuf->b_changelist[i].mark, 17);
- msg_outtrans_attr(name, HL_ATTR(HLF_D));
+ msg_outtrans_attr((char *)name, HL_ATTR(HLF_D));
xfree(name);
os_breakcheck();
}
@@ -1549,7 +1548,7 @@ void mark_col_adjust(linenr_T lnum, colnr_T mincol, linenr_T lnum_amount, long c
*/
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
// marks in the jumplist
- for (i = 0; i < win->w_jumplistlen; ++i) {
+ for (i = 0; i < win->w_jumplistlen; i++) {
if (win->w_jumplist[i].fmark.fnum == fnum) {
COL_ADJUST(&(win->w_jumplist[i].fmark.mark));
}
@@ -1651,7 +1650,7 @@ void copy_jumplist(win_T *from, win_T *to)
{
int i;
- for (i = 0; i < from->w_jumplistlen; ++i) {
+ 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 = xstrdup(from->w_jumplist[i].fname);
@@ -1875,7 +1874,7 @@ void free_jumplist(win_T *wp)
{
int i;
- for (i = 0; i < wp->w_jumplistlen; ++i) {
+ for (i = 0; i < wp->w_jumplistlen; i++) {
free_xfmark(wp->w_jumplist[i]);
}
wp->w_jumplistlen = 0;
@@ -1898,7 +1897,7 @@ void free_all_marks(void)
free_xfmark(namedfm[i]);
}
}
- memset(&namedfm[0], 0, sizeof(namedfm));
+ CLEAR_FIELD(namedfm);
}
#endif
diff --git a/src/nvim/match.c b/src/nvim/match.c
index ba587c4141..48f0d0fc05 100644
--- a/src/nvim/match.c
+++ b/src/nvim/match.c
@@ -7,14 +7,19 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/fold.h"
+#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/match.h"
#include "nvim/memline.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
-#include "nvim/screen.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "match.c.generated.h"
@@ -42,7 +47,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
matchitem_T *m;
int hlg_id;
regprog_T *regprog = NULL;
- int rtype = SOME_VALID;
+ int rtype = UPD_SOME_VALID;
if (*grp == NUL || (pat != NULL && *pat == NUL)) {
return -1;
@@ -188,7 +193,7 @@ static int match_add(win_T *wp, const char *const grp, const char *const pat, in
}
m->pos.toplnum = toplnum;
m->pos.botlnum = botlnum;
- rtype = VALID;
+ rtype = UPD_VALID;
}
}
@@ -222,7 +227,7 @@ static int match_delete(win_T *wp, int id, bool perr)
{
matchitem_T *cur = wp->w_match_head;
matchitem_T *prev = cur;
- int rtype = SOME_VALID;
+ int rtype = UPD_SOME_VALID;
if (id < 1) {
if (perr) {
@@ -263,7 +268,7 @@ static int match_delete(win_T *wp, int id, bool perr)
wp->w_buffer->b_mod_bot = cur->pos.botlnum;
wp->w_buffer->b_mod_xlines = 0;
}
- rtype = VALID;
+ rtype = UPD_VALID;
}
xfree(cur);
redraw_later(wp, rtype);
@@ -282,7 +287,7 @@ void clear_matches(win_T *wp)
xfree(wp->w_match_head);
wp->w_match_head = m;
}
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
}
/// Get match from ID 'id' in window 'wp'.
@@ -692,7 +697,7 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match
}
// Highlight the match were the cursor is using the CurSearch
// group.
- if (shl == search_hl && shl->has_cursor && (HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC])) {
+ if (shl == search_hl && shl->has_cursor && (HL_ATTR(HLF_LC) || win_hl_attr(wp, HLF_LC))) {
shl->attr_cur = win_hl_attr(wp, HLF_LC) ? win_hl_attr(wp, HLF_LC) : HL_ATTR(HLF_LC);
} else {
shl->attr_cur = shl->attr;
@@ -854,7 +859,7 @@ static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, win_T **wi
}
/// "clearmatches()" function
-void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_clearmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *win = get_optional_window(argvars, 0);
@@ -864,7 +869,7 @@ void f_clearmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "getmatches()" function
-void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_getmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
matchitem_T *cur;
int i;
@@ -919,7 +924,7 @@ void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "setmatches()" function
-void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_setmatches(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
dict_T *d;
list_T *s = NULL;
@@ -1022,7 +1027,7 @@ void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matchadd()" function
-void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
char grpbuf[NUMBUFLEN];
char patbuf[NUMBUFLEN];
@@ -1064,7 +1069,7 @@ void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matchaddpo()" function
-void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchaddpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = -1;
@@ -1115,7 +1120,7 @@ void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matcharg()" function
-void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matcharg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const int id = (int)tv_get_number(&argvars[0]);
@@ -1138,7 +1143,7 @@ void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "matchdelete()" function
-void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchdelete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
win_T *win = get_optional_window(argvars, 1);
if (win == NULL) {
@@ -1178,7 +1183,7 @@ void ex_match(exarg_T *eap)
&& (ascii_iswhite(eap->arg[4]) || ends_excmd(eap->arg[4])))) {
end = (char_u *)eap->arg + 4;
} else {
- p = skiptowhite((char_u *)eap->arg);
+ p = (char_u *)skiptowhite(eap->arg);
if (!eap->skip) {
g = vim_strnsave((char_u *)eap->arg, (size_t)(p - (char_u *)eap->arg));
}
@@ -1189,7 +1194,7 @@ void ex_match(exarg_T *eap)
semsg(_(e_invarg2), eap->arg);
return;
}
- end = skip_regexp(p + 1, *p, true, NULL);
+ end = (char_u *)skip_regexp((char *)p + 1, *p, true, NULL);
if (!eap->skip) {
if (*end != NUL && !ends_excmd(*skipwhite((char *)end + 1))) {
xfree(g);
@@ -1210,5 +1215,5 @@ void ex_match(exarg_T *eap)
*end = (char_u)c;
}
}
- eap->nextcmd = (char *)find_nextcmd(end);
+ eap->nextcmd = find_nextcmd((char *)end);
}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 223b4d6845..b874f0dc94 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -39,6 +39,7 @@
#include "nvim/arabic.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
@@ -49,7 +50,6 @@
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/screen.h"
@@ -74,6 +74,19 @@ struct interval {
# include "unicode_tables.generated.h"
#endif
+static char e_list_item_nr_is_not_list[]
+ = N_("E1109: List item %d is not a List");
+static char e_list_item_nr_does_not_contain_3_numbers[]
+ = N_("E1110: List item %d does not contain 3 numbers");
+static char e_list_item_nr_range_invalid[]
+ = N_("E1111: List item %d range invalid");
+static char e_list_item_nr_cell_width_invalid[]
+ = N_("E1112: List item %d cell width invalid");
+static char e_overlapping_ranges_for_nr[]
+ = N_("E1113: Overlapping ranges for 0x%lx");
+static char e_only_values_of_0x100_and_higher_supported[]
+ = N_("E1114: Only values of 0x100 and higher supported");
+
// To speed up BYTELEN(); keep a lookup table to quickly get the length in
// bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes
// which are illegal when used as the first byte have a 1. The NUL byte has
@@ -472,13 +485,18 @@ static bool intable(const struct interval *table, size_t n_items, int c)
int utf_char2cells(int c)
{
if (c >= 0x100) {
+ int n = cw_value(c);
+ if (n != 0) {
+ return n;
+ }
+
if (!utf_printable(c)) {
return 6; // unprintable, displays <xxxx>
}
if (intable(doublewidth, ARRAY_SIZE(doublewidth), c)) {
return 2;
}
- if (p_emoji && intable(emoji_width, ARRAY_SIZE(emoji_width), c)) {
+ if (p_emoji && intable(emoji_wide, ARRAY_SIZE(emoji_wide), c)) {
return 2;
}
} else if (c >= 0x80 && !vim_isprintc(c)) {
@@ -736,21 +754,19 @@ bool utf_composinglike(const char_u *p1, const char_u *p2)
/// space at least for #MAX_MCO + 1 elements.
///
/// @return leading character.
-int utfc_ptr2char(const char_u *p, int *pcc)
+int utfc_ptr2char(const char *p_in, int *pcc)
{
- int len;
- int c;
- int cc;
+ uint8_t *p = (uint8_t *)p_in;
int i = 0;
- c = utf_ptr2char((char *)p);
- len = utf_ptr2len((char *)p);
+ int c = utf_ptr2char((char *)p);
+ int len = utf_ptr2len((char *)p);
// Only accept a composing char when the first char isn't illegal.
if ((len > 1 || *p < 0x80)
&& p[len] >= 0x80
&& utf_composinglike(p, p + len)) {
- cc = utf_ptr2char((char *)p + len);
+ int cc = utf_ptr2char((char *)p + len);
for (;;) {
pcc[i++] = cc;
if (i == MAX_MCO) {
@@ -864,7 +880,7 @@ int utf_ptr2len_len(const char_u *p, int size)
} else {
m = len;
}
- for (i = 1; i < m; ++i) {
+ for (i = 1; i < m; i++) {
if ((p[i] & 0xc0) != 0x80) {
return 1;
}
@@ -872,9 +888,9 @@ int utf_ptr2len_len(const char_u *p, int size)
return len;
}
-/// Return the number of bytes occupied by a UTF-8 character in a string
-///
+/// Return the number of bytes occupied by a UTF-8 character in a string.
/// This includes following composing characters.
+/// Returns zero for NUL.
int utfc_ptr2len(const char *const p_in)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
@@ -988,8 +1004,9 @@ int utf_char2len(const int c)
/// Convert Unicode character to UTF-8 string
///
-/// @param c character to convert to \p buf
-/// @param[out] buf UTF-8 string generated from \p c, does not add \0
+/// @param c character to convert to UTF-8 string in \p buf
+/// @param[out] buf UTF-8 string generated from \p c, does not add \0
+/// must have room for at least 6 bytes
/// @return Number of bytes (1-6).
int utf_char2bytes(const int c, char *const buf)
{
@@ -1164,6 +1181,11 @@ int utf_class_tab(const int c, const uint64_t *const chartab)
return 1; // punctuation
}
+ // emoji
+ if (intable(emoji_all, ARRAY_SIZE(emoji_all), c)) {
+ return 3;
+ }
+
// binary search in table
while (top >= bot) {
mid = (bot + top) / 2;
@@ -1176,11 +1198,6 @@ int utf_class_tab(const int c, const uint64_t *const chartab)
}
}
- // emoji
- if (intable(emoji_all, ARRAY_SIZE(emoji_all), c)) {
- return 3;
- }
-
// most other characters are "word" characters
return 2;
}
@@ -1576,7 +1593,7 @@ void show_utf8(void)
}
clen = 0;
- for (i = 0; i < len; ++i) {
+ for (i = 0; i < len; i++) {
if (clen == 0) {
// start of (composing) character, get its length
if (i > 0) {
@@ -1587,7 +1604,7 @@ void show_utf8(void)
}
sprintf((char *)IObuff + rlen, "%02x ",
(line[i] == NL) ? NUL : line[i]); // NUL is stored as NL
- --clen;
+ clen--;
rlen += (int)STRLEN(IObuff + rlen);
if (rlen > IOSIZE - 20) {
break;
@@ -1613,14 +1630,14 @@ int utf_head_off(const char_u *base, const char_u *p)
// Skip backwards over trailing bytes: 10xx.xxxx
// Skip backwards again if on a composing char.
const char_u *q;
- for (q = p;; --q) {
+ for (q = p;; q--) {
// Move s to the last byte of this char.
const char_u *s;
- for (s = q; (s[1] & 0xc0) == 0x80; ++s) {}
+ for (s = q; (s[1] & 0xc0) == 0x80; s++) {}
// Move q to the first byte of this char.
while (q > base && (*q & 0xc0) == 0x80) {
- --q;
+ q--;
}
// Check for illegal sequence. Do allow an illegal byte after where we
// started.
@@ -1641,10 +1658,10 @@ int utf_head_off(const char_u *base, const char_u *p)
if (arabic_maycombine(c)) {
// Advance to get a sneak-peak at the next char
const char_u *j = q;
- --j;
+ j--;
// Move j to the first byte of this char.
while (j > base && (*j & 0xc0) == 0x80) {
- --j;
+ j--;
}
if (arabic_combine(utf_ptr2char((char *)j), c)) {
continue;
@@ -1800,9 +1817,9 @@ bool utf_allow_break(int cc, int ncc)
///
/// @param[in,out] fp Source of the character to copy.
/// @param[in,out] tp Destination to copy to.
-void mb_copy_char(const char_u **const fp, char_u **const tp)
+void mb_copy_char(const char **const fp, char **const tp)
{
- const size_t l = (size_t)utfc_ptr2len((char *)(*fp));
+ const size_t l = (size_t)utfc_ptr2len(*fp);
memmove(*tp, *fp, l);
*tp += l;
@@ -1913,7 +1930,7 @@ void utf_find_illegal(void)
char_u *tofree = NULL;
vimconv.vc_type = CONV_NONE;
- if (enc_canon_props(curbuf->b_p_fenc) & ENC_8BIT) {
+ if (enc_canon_props((char_u *)curbuf->b_p_fenc) & ENC_8BIT) {
// 'encoding' is "utf-8" but we are editing a 8-bit encoded file,
// possibly a utf-8 file with illegal bytes. Setup for conversion
// from utf-8 to 'fileencoding'.
@@ -1925,7 +1942,7 @@ void utf_find_illegal(void)
p = get_cursor_pos_ptr();
if (vimconv.vc_type != CONV_NONE) {
xfree(tofree);
- tofree = string_convert(&vimconv, p, NULL);
+ tofree = (char_u *)string_convert(&vimconv, (char *)p, NULL);
if (tofree == NULL) {
break;
}
@@ -1956,7 +1973,7 @@ void utf_find_illegal(void)
if (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
break;
}
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
}
@@ -1970,8 +1987,7 @@ theend:
}
/// @return true if string "s" is a valid utf-8 string.
-/// When "end" is NULL stop at the first NUL.
-/// When "end" is positive stop there.
+/// When "end" is NULL stop at the first NUL. Otherwise stop at "end".
bool utf_valid_string(const char_u *s, const char_u *end)
{
const char_u *p = s;
@@ -2128,10 +2144,8 @@ const char *mb_unescape(const char **const pp)
return NULL;
}
-/*
- * Skip the Vim specific head of a 'encoding' name.
- */
-char_u *enc_skip(char_u *p)
+/// Skip the Vim specific head of a 'encoding' name.
+char *enc_skip(char *p)
{
if (STRNCMP(p, "2byte-", 6) == 0) {
return p + 6;
@@ -2142,27 +2156,25 @@ char_u *enc_skip(char_u *p)
return p;
}
-/*
- * Find the canonical name for encoding "enc".
- * When the name isn't recognized, returns "enc" itself, but with all lower
- * case characters and '_' replaced with '-'.
- * Returns an allocated string.
- */
-char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
+/// Find the canonical name for encoding "enc".
+/// When the name isn't recognized, returns "enc" itself, but with all lower
+/// case characters and '_' replaced with '-'.
+///
+/// @return an allocated string.
+char *enc_canonize(char *enc)
+ FUNC_ATTR_NONNULL_RET
{
char_u *p, *s;
- int i;
-
if (STRCMP(enc, "default") == 0) {
// Use the default encoding as found by set_init_1().
- return vim_strsave(fenc_default);
+ return (char *)vim_strsave(fenc_default);
}
// copy "enc" to allocated memory, with room for two '-'
char_u *r = xmalloc(STRLEN(enc) + 3);
// Make it all lower case and replace '_' with '-'.
p = r;
- for (s = enc; *s != NUL; ++s) {
+ for (s = (char_u *)enc; *s != NUL; s++) {
if (*s == '_') {
*p++ = '-';
} else {
@@ -2172,7 +2184,7 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
*p = NUL;
// Skip "2byte-" and "8bit-".
- p = enc_skip(r);
+ p = (char_u *)enc_skip((char *)r);
// Change "microsoft-cp" to "cp". Used in some spell files.
if (STRNCMP(p, "microsoft-cp", 12) == 0) {
@@ -2196,6 +2208,7 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
STRMOVE(p + 5, p + 6);
}
+ int i;
if (enc_canon_search(p) >= 0) {
// canonical name can be used unmodified
if (p != r) {
@@ -2206,7 +2219,7 @@ char_u *enc_canonize(char_u *enc) FUNC_ATTR_NONNULL_RET
xfree(r);
r = vim_strsave((char_u *)enc_canon_table[i].name);
}
- return r;
+ return (char *)r;
}
/// Search for an encoding alias of "name".
@@ -2215,7 +2228,7 @@ static int enc_alias_search(const char_u *name)
{
int i;
- for (i = 0; enc_alias_table[i].name != NULL; ++i) {
+ for (i = 0; enc_alias_table[i].name != NULL; i++) {
if (STRCMP(name, enc_alias_table[i].name) == 0) {
return enc_alias_table[i].canon;
}
@@ -2291,7 +2304,7 @@ enc_locale_copy_enc:
buf[i] = NUL;
}
- return enc_canonize((char_u *)buf);
+ return (char_u *)enc_canonize(buf);
}
#if defined(HAVE_ICONV)
@@ -2314,7 +2327,7 @@ void *my_iconv_open(char_u *to, char_u *from)
if (iconv_working == kBroken) {
return (void *)-1; // detected a broken iconv() previously
}
- fd = iconv_open((char *)enc_skip(to), (char *)enc_skip(from));
+ fd = iconv_open(enc_skip((char *)to), enc_skip((char *)from));
if (fd != (iconv_t)-1 && iconv_working == kUnknown) {
/*
@@ -2425,18 +2438,17 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen
#endif // HAVE_ICONV
-/*
- * Setup "vcp" for conversion from "from" to "to".
- * The names must have been made canonical with enc_canonize().
- * vcp->vc_type must have been initialized to CONV_NONE.
- * Note: cannot be used for conversion from/to ucs-2 and ucs-4 (will use utf-8
- * instead).
- * Afterwards invoke with "from" and "to" equal to NULL to cleanup.
- * Return FAIL when conversion is not supported, OK otherwise.
- */
-int convert_setup(vimconv_T *vcp, char_u *from, char_u *to)
+/// Setup "vcp" for conversion from "from" to "to".
+/// The names must have been made canonical with enc_canonize().
+/// vcp->vc_type must have been initialized to CONV_NONE.
+/// Note: cannot be used for conversion from/to ucs-2 and ucs-4 (will use utf-8
+/// instead).
+/// Afterwards invoke with "from" and "to" equal to NULL to cleanup.
+///
+/// @return FAIL when conversion is not supported, OK otherwise.
+int convert_setup(vimconv_T *vcp, char *from, char *to)
{
- return convert_setup_ext(vcp, from, true, to, true);
+ return convert_setup_ext(vcp, (char_u *)from, true, (char_u *)to, true);
}
/// As convert_setup(), but only when from_unicode_is_utf8 is true will all
@@ -2509,16 +2521,14 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c
return OK;
}
-/*
- * Convert text "ptr[*lenp]" according to "vcp".
- * Returns the result in allocated memory and sets "*lenp".
- * When "lenp" is NULL, use NUL terminated strings.
- * Illegal chars are often changed to "?", unless vcp->vc_fail is set.
- * When something goes wrong, NULL is returned and "*lenp" is unchanged.
- */
-char_u *string_convert(const vimconv_T *const vcp, char_u *ptr, size_t *lenp)
+/// Convert text "ptr[*lenp]" according to "vcp".
+/// Returns the result in allocated memory and sets "*lenp".
+/// When "lenp" is NULL, use NUL terminated strings.
+/// Illegal chars are often changed to "?", unless vcp->vc_fail is set.
+/// When something goes wrong, NULL is returned and "*lenp" is unchanged.
+char *string_convert(const vimconv_T *const vcp, char *ptr, size_t *lenp)
{
- return string_convert_ext(vcp, ptr, lenp, NULL);
+ return (char *)string_convert_ext(vcp, (char_u *)ptr, lenp, NULL);
}
/*
@@ -2548,7 +2558,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
case CONV_TO_UTF8: // latin1 to utf-8 conversion
retval = xmalloc(len * 2 + 1);
d = retval;
- for (size_t i = 0; i < len; ++i) {
+ for (size_t i = 0; i < len; i++) {
c = ptr[i];
if (c < 0x80) {
*d++ = (char_u)c;
@@ -2566,7 +2576,7 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
case CONV_9_TO_UTF8: // latin9 to utf-8 conversion
retval = xmalloc(len * 3 + 1);
d = retval;
- for (size_t i = 0; i < len; ++i) {
+ for (size_t i = 0; i < len; i++) {
c = ptr[i];
switch (c) {
case 0xa4:
@@ -2678,3 +2688,174 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp
return retval;
}
+
+/// Table set by setcellwidths().
+typedef struct {
+ long first;
+ long last;
+ char width;
+} cw_interval_T;
+
+static cw_interval_T *cw_table = NULL;
+static size_t cw_table_size = 0;
+
+/// Return the value of the cellwidth table for the character `c`.
+///
+/// @param c The source character.
+/// @return 1 or 2 when `c` is in the cellwidth table, 0 if not.
+static int cw_value(int c)
+{
+ if (cw_table == NULL) {
+ return 0;
+ }
+
+ // first quick check for Latin1 etc. characters
+ if (c < cw_table[0].first) {
+ return 0;
+ }
+
+ // binary search in table
+ int bot = 0;
+ int top = (int)cw_table_size - 1;
+ while (top >= bot) {
+ int mid = (bot + top) / 2;
+ if (cw_table[mid].last < c) {
+ bot = mid + 1;
+ } else if (cw_table[mid].first > c) {
+ top = mid - 1;
+ } else {
+ return cw_table[mid].width;
+ }
+ }
+ return 0;
+}
+
+static int tv_nr_compare(const void *a1, const void *a2)
+{
+ const listitem_T *const li1 = tv_list_first(*(const list_T **)a1);
+ const listitem_T *const li2 = tv_list_first(*(const list_T **)a2);
+
+ return (int)(TV_LIST_ITEM_TV(li1)->vval.v_number - TV_LIST_ITEM_TV(li2)->vval.v_number);
+}
+
+/// "setcellwidths()" function
+void f_setcellwidths(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (argvars[0].v_type != VAR_LIST || argvars[0].vval.v_list == NULL) {
+ emsg(_(e_listreq));
+ return;
+ }
+ const list_T *const l = argvars[0].vval.v_list;
+ if (tv_list_len(l) == 0) {
+ // Clearing the table.
+ xfree(cw_table);
+ cw_table = NULL;
+ cw_table_size = 0;
+ return;
+ }
+
+ // Note: use list_T instead of listitem_T so that TV_LIST_ITEM_NEXT can be used properly below.
+ const list_T **ptrs = xmalloc(sizeof(const list_T *) * (size_t)tv_list_len(l));
+
+ // Check that all entries are a list with three numbers, the range is
+ // valid and the cell width is valid.
+ int item = 0;
+ TV_LIST_ITER_CONST(l, li, {
+ const typval_T *const li_tv = TV_LIST_ITEM_TV(li);
+
+ if (li_tv->v_type != VAR_LIST || li_tv->vval.v_list == NULL) {
+ semsg(_(e_list_item_nr_is_not_list), item);
+ xfree(ptrs);
+ return;
+ }
+
+ const list_T *const li_l = li_tv->vval.v_list;
+ ptrs[item] = li_l;
+ const listitem_T *lili = tv_list_first(li_l);
+ int i;
+ varnumber_T n1;
+ for (i = 0; lili != NULL; lili = TV_LIST_ITEM_NEXT(li_l, lili), i++) {
+ const typval_T *const lili_tv = TV_LIST_ITEM_TV(lili);
+ if (lili_tv->v_type != VAR_NUMBER) {
+ break;
+ }
+ if (i == 0) {
+ n1 = lili_tv->vval.v_number;
+ if (n1 < 0x100) {
+ emsg(_(e_only_values_of_0x100_and_higher_supported));
+ xfree(ptrs);
+ return;
+ }
+ } else if (i == 1 && lili_tv->vval.v_number < n1) {
+ semsg(_(e_list_item_nr_range_invalid), item);
+ xfree(ptrs);
+ return;
+ } else if (i == 2 && (lili_tv->vval.v_number < 1 || lili_tv->vval.v_number > 2)) {
+ semsg(_(e_list_item_nr_cell_width_invalid), item);
+ xfree(ptrs);
+ return;
+ }
+ }
+
+ if (i != 3) {
+ semsg(_(e_list_item_nr_does_not_contain_3_numbers), item);
+ xfree(ptrs);
+ return;
+ }
+
+ item++;
+ });
+
+ // Sort the list on the first number.
+ qsort((void *)ptrs, (size_t)tv_list_len(l), sizeof(const list_T *), tv_nr_compare);
+
+ cw_interval_T *table = xmalloc(sizeof(cw_interval_T) * (size_t)tv_list_len(l));
+
+ // Store the items in the new table.
+ for (item = 0; item < tv_list_len(l); item++) {
+ const list_T *const li_l = ptrs[item];
+ const listitem_T *lili = tv_list_first(li_l);
+ const varnumber_T n1 = TV_LIST_ITEM_TV(lili)->vval.v_number;
+ if (item > 0 && n1 <= table[item - 1].last) {
+ semsg(_(e_overlapping_ranges_for_nr), (long)n1);
+ xfree(ptrs);
+ xfree(table);
+ return;
+ }
+ table[item].first = n1;
+ lili = TV_LIST_ITEM_NEXT(li_l, lili);
+ table[item].last = TV_LIST_ITEM_TV(lili)->vval.v_number;
+ lili = TV_LIST_ITEM_NEXT(li_l, lili);
+ table[item].width = (char)TV_LIST_ITEM_TV(lili)->vval.v_number;
+ }
+
+ xfree(ptrs);
+
+ cw_interval_T *const cw_table_save = cw_table;
+ const size_t cw_table_size_save = cw_table_size;
+ cw_table = table;
+ cw_table_size = (size_t)tv_list_len(l);
+
+ // Check that the new value does not conflict with 'listchars' or
+ // 'fillchars'.
+ const char *const error = check_chars_options();
+ if (error != NULL) {
+ emsg(_(error));
+ cw_table = cw_table_save;
+ cw_table_size = cw_table_size_save;
+ xfree(table);
+ return;
+ }
+
+ xfree(cw_table_save);
+ redraw_all_later(UPD_NOT_VALID);
+}
+
+void f_charclass(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (tv_check_for_string(&argvars[0]) == FAIL
+ || argvars[0].vval.v_string == NULL) {
+ return;
+ }
+ rettv->vval.v_number = mb_get_class((const char_u *)argvars[0].vval.v_string);
+}
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index 1e5e332ad9..2a9afcbd03 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -5,8 +5,9 @@
#include <stdint.h>
#include <string.h>
+#include "nvim/eval/typval.h"
#include "nvim/func_attr.h"
-#include "nvim/iconv.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/os/os_defs.h" // For indirect
#include "nvim/types.h" // for char_u
@@ -19,52 +20,6 @@
#define MB_BYTE2LEN(b) utf8len_tab[b]
#define MB_BYTE2LEN_CHECK(b) (((b) < 0 || (b) > 255) ? 1 : utf8len_tab[b])
-// max length of an unicode char
-#define MB_MAXCHAR 6
-
-// properties used in enc_canon_table[] (first three mutually exclusive)
-#define ENC_8BIT 0x01
-#define ENC_DBCS 0x02
-#define ENC_UNICODE 0x04
-
-#define ENC_ENDIAN_B 0x10 // Unicode: Big endian
-#define ENC_ENDIAN_L 0x20 // Unicode: Little endian
-
-#define ENC_2BYTE 0x40 // Unicode: UCS-2
-#define ENC_4BYTE 0x80 // Unicode: UCS-4
-#define ENC_2WORD 0x100 // Unicode: UTF-16
-
-#define ENC_LATIN1 0x200 // Latin1
-#define ENC_LATIN9 0x400 // Latin9
-#define ENC_MACROMAN 0x800 // Mac Roman (not Macro Man! :-)
-
-/// Flags for vimconv_T
-typedef enum {
- CONV_NONE = 0,
- CONV_TO_UTF8 = 1,
- CONV_9_TO_UTF8 = 2,
- CONV_TO_LATIN1 = 3,
- CONV_TO_LATIN9 = 4,
- CONV_ICONV = 5,
-} ConvFlags;
-
-#define MBYTE_NONE_CONV { \
- .vc_type = CONV_NONE, \
- .vc_factor = 1, \
- .vc_fail = false, \
-}
-
-/// Structure used for string conversions
-typedef struct {
- int vc_type; ///< Zero or more ConvFlags.
- int vc_factor; ///< Maximal expansion factor.
-#ifdef HAVE_ICONV
- iconv_t vc_fd; ///< Value for CONV_ICONV.
-#endif
- bool vc_fail; ///< What to do with invalid characters: if true, fail,
- ///< otherwise use '?'.
-} vimconv_T;
-
extern const uint8_t utf8len_tab_zero[256];
extern const uint8_t utf8len_tab[256];
diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h
new file mode 100644
index 0000000000..53b01a211f
--- /dev/null
+++ b/src/nvim/mbyte_defs.h
@@ -0,0 +1,56 @@
+#ifndef NVIM_MBYTE_DEFS_H
+#define NVIM_MBYTE_DEFS_H
+
+#include <stdbool.h>
+
+#include "nvim/iconv.h"
+
+/// max length of an unicode char
+enum { MB_MAXCHAR = 6, };
+
+/// properties used in enc_canon_table[] (first three mutually exclusive)
+enum {
+ ENC_8BIT = 0x01,
+ ENC_DBCS = 0x02,
+ ENC_UNICODE = 0x04,
+
+ ENC_ENDIAN_B = 0x10, ///< Unicode: Big endian
+ ENC_ENDIAN_L = 0x20, ///< Unicode: Little endian
+
+ ENC_2BYTE = 0x40, ///< Unicode: UCS-2
+ ENC_4BYTE = 0x80, ///< Unicode: UCS-4
+ ENC_2WORD = 0x100, ///< Unicode: UTF-16
+
+ ENC_LATIN1 = 0x200, ///< Latin1
+ ENC_LATIN9 = 0x400, ///< Latin9
+ ENC_MACROMAN = 0x800, ///< Mac Roman (not Macro Man! :-)
+};
+
+/// Flags for vimconv_T
+typedef enum {
+ CONV_NONE = 0,
+ CONV_TO_UTF8 = 1,
+ CONV_9_TO_UTF8 = 2,
+ CONV_TO_LATIN1 = 3,
+ CONV_TO_LATIN9 = 4,
+ CONV_ICONV = 5,
+} ConvFlags;
+
+#define MBYTE_NONE_CONV { \
+ .vc_type = CONV_NONE, \
+ .vc_factor = 1, \
+ .vc_fail = false, \
+}
+
+/// Structure used for string conversions
+typedef struct {
+ int vc_type; ///< Zero or more ConvFlags.
+ int vc_factor; ///< Maximal expansion factor.
+#ifdef HAVE_ICONV
+ iconv_t vc_fd; ///< Value for CONV_ICONV.
+#endif
+ bool vc_fail; ///< What to do with invalid characters: if true, fail,
+ ///< otherwise use '?'.
+} vimconv_T;
+
+#endif // NVIM_MBYTE_DEFS_H
diff --git a/src/nvim/memfile.c b/src/nvim/memfile.c
index c828334eaf..9446aaee4f 100644
--- a/src/nvim/memfile.c
+++ b/src/nvim/memfile.c
@@ -77,7 +77,7 @@
///
/// @return - The open memory file, on success.
/// - NULL, on failure (e.g. file does not exist).
-memfile_T *mf_open(char_u *fname, int flags)
+memfile_T *mf_open(char *fname, int flags)
{
memfile_T *mfp = xmalloc(sizeof(memfile_T));
@@ -148,7 +148,7 @@ memfile_T *mf_open(char_u *fname, int flags)
///
/// @return OK On success.
/// FAIL If file could not be opened.
-int mf_open_file(memfile_T *mfp, char_u *fname)
+int mf_open_file(memfile_T *mfp, char *fname)
{
if (mf_do_open(mfp, fname, O_RDWR | O_CREAT | O_EXCL)) {
mfp->mf_dirty = true;
@@ -749,9 +749,9 @@ void mf_free_fnames(memfile_T *mfp)
///
/// Only called when creating or renaming the swapfile. Either way it's a new
/// name so we must work out the full path name.
-void mf_set_fnames(memfile_T *mfp, char_u *fname)
+void mf_set_fnames(memfile_T *mfp, char *fname)
{
- mfp->mf_fname = fname;
+ mfp->mf_fname = (char_u *)fname;
mfp->mf_ffname = (char_u *)FullName_save((char *)mfp->mf_fname, false);
}
@@ -779,7 +779,7 @@ bool mf_need_trans(memfile_T *mfp)
///
/// @param flags Flags for open().
/// @return A bool indicating success of the `open` call.
-static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
+static bool mf_do_open(memfile_T *mfp, char *fname, int flags)
{
// fname cannot be NameBuff, because it must have been allocated.
mf_set_fnames(mfp, fname);
@@ -821,7 +821,7 @@ static bool mf_do_open(memfile_T *mfp, char_u *fname, int flags)
/// Initialize an empty hash table.
static void mf_hash_init(mf_hashtab_T *mht)
{
- memset(mht, 0, sizeof(mf_hashtab_T));
+ CLEAR_POINTER(mht);
mht->mht_buckets = mht->mht_small_buckets;
mht->mht_mask = MHT_INIT_SIZE - 1;
}
@@ -924,7 +924,7 @@ static void mf_hash_grow(mf_hashtab_T *mht)
/// a power of two.
mf_hashitem_T *tails[MHT_GROWTH_FACTOR];
- memset(tails, 0, sizeof(tails));
+ CLEAR_FIELD(tails);
for (mf_hashitem_T *mhi = mht->mht_buckets[i];
mhi != NULL; mhi = mhi->mhi_next) {
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index fa3a400a68..b0b6b675cb 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -44,9 +44,11 @@
#include <string.h>
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/fileio.h"
#include "nvim/func_attr.h"
@@ -65,7 +67,6 @@
#include "nvim/os/process.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/screen.h"
#include "nvim/sha256.h"
#include "nvim/spell.h"
#include "nvim/strings.h"
@@ -383,7 +384,7 @@ void ml_setname(buf_T *buf)
bool success = false;
memfile_T *mfp;
char_u *fname;
- char_u *dirp;
+ char *dirp;
mfp = buf->b_ml.ml_mfp;
if (mfp->mf_fd < 0) { // there is no swap file yet
@@ -397,17 +398,14 @@ void ml_setname(buf_T *buf)
return;
}
- /*
- * Try all directories in the 'directory' option.
- */
+ // Try all directories in the 'directory' option.
dirp = p_dir;
bool found_existing_dir = false;
for (;;) {
if (*dirp == NUL) { // tried all directories, fail
break;
}
- fname = (char_u *)findswapname(buf, (char **)&dirp, (char *)mfp->mf_fname,
- &found_existing_dir);
+ fname = (char_u *)findswapname(buf, &dirp, (char *)mfp->mf_fname, &found_existing_dir);
// alloc's fname
if (dirp == NULL) { // out of memory
break;
@@ -432,7 +430,7 @@ void ml_setname(buf_T *buf)
if (vim_rename(mfp->mf_fname, fname) == 0) {
success = true;
mf_free_fnames(mfp);
- mf_set_fnames(mfp, fname);
+ mf_set_fnames(mfp, (char *)fname);
ml_upd_block0(buf, UB_SAME_DIR);
break;
}
@@ -472,7 +470,7 @@ void ml_open_file(buf_T *buf)
{
memfile_T *mfp;
char_u *fname;
- char_u *dirp;
+ char *dirp;
mfp = buf->b_ml.ml_mfp;
if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf
@@ -485,15 +483,13 @@ void ml_open_file(buf_T *buf)
if (buf->b_spell) {
fname = vim_tempname();
if (fname != NULL) {
- (void)mf_open_file(mfp, fname); // consumes fname!
+ (void)mf_open_file(mfp, (char *)fname); // consumes fname!
}
buf->b_may_swap = false;
return;
}
- /*
- * Try all directories in 'directory' option.
- */
+ // Try all directories in 'directory' option.
dirp = p_dir;
bool found_existing_dir = false;
for (;;) {
@@ -503,15 +499,14 @@ void ml_open_file(buf_T *buf)
// There is a small chance that between choosing the swap file name
// and creating it, another Vim creates the file. In that case the
// creation will fail and we will use another directory.
- fname = (char_u *)findswapname(buf, (char **)&dirp, NULL,
- &found_existing_dir);
+ fname = (char_u *)findswapname(buf, &dirp, NULL, &found_existing_dir);
if (dirp == NULL) {
break; // out of memory
}
if (fname == NULL) {
continue;
}
- if (mf_open_file(mfp, fname) == OK) { // consumes fname!
+ if (mf_open_file(mfp, (char *)fname) == OK) { // consumes fname!
ml_upd_block0(buf, UB_SAME_DIR);
// Flush block zero, so others can read it
@@ -528,7 +523,7 @@ void ml_open_file(buf_T *buf)
}
if (*p_dir != NUL && mfp->mf_fname == NULL) {
- need_wait_return = true; // call wait_return later
+ need_wait_return = true; // call wait_return() later
no_wait_return++;
(void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
@@ -557,7 +552,7 @@ void check_need_swap(bool newfile)
/// Close memline for buffer 'buf'.
///
-/// @param del_file if TRUE, delete the swap file
+/// @param del_file if true, delete the swap file
void ml_close(buf_T *buf, int del_file)
{
if (buf->b_ml.ml_mfp == NULL) { // not open
@@ -595,7 +590,7 @@ void ml_close_notmod(void)
{
FOR_ALL_BUFFERS(buf) {
if (!bufIsChanged(buf)) {
- ml_close(buf, TRUE); // close all not-modified buffers
+ ml_close(buf, true); // close all not-modified buffers
}
}
}
@@ -726,7 +721,7 @@ static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf)
b0p->b0_flags &= (uint8_t) ~B0_HAS_FENC;
} else {
memmove((char *)b0p->b0_fname + size - n,
- (char *)buf->b_p_fenc, (size_t)n);
+ buf->b_p_fenc, (size_t)n);
*(b0p->b0_fname + size - n - 1) = NUL;
b0p->b0_flags |= B0_HAS_FENC;
}
@@ -769,7 +764,7 @@ void ml_recover(bool checkext)
int attr;
int orig_file_status = NOTDONE;
- recoverymode = TRUE;
+ recoverymode = true;
called_from_main = (curbuf->b_ml.ml_mfp == NULL);
attr = HL_ATTR(HLF_E);
@@ -790,7 +785,7 @@ void ml_recover(bool checkext)
directly = false;
// count the number of matching swap files
- len = recover_names(fname, FALSE, 0, NULL);
+ len = recover_names(fname, false, 0, NULL);
if (len == 0) { // no swap files found
semsg(_("E305: No swap file found for %s"), fname);
goto theend;
@@ -799,16 +794,16 @@ void ml_recover(bool checkext)
i = 1;
} else { // several swap files found, choose
// list the names of the swap files
- (void)recover_names(fname, TRUE, 0, NULL);
+ (void)recover_names(fname, true, 0, NULL);
msg_putchar('\n');
msg_puts(_("Enter number of swap file to use (0 to quit): "));
- i = get_number(FALSE, NULL);
+ i = get_number(false, NULL);
if (i < 1 || i > len) {
goto theend;
}
}
// get the swap file name that will be used
- (void)recover_names(fname, FALSE, i, &fname_used);
+ (void)recover_names(fname, false, i, &fname_used);
}
if (fname_used == NULL) {
goto theend; // user chose invalid number.
@@ -840,7 +835,7 @@ void ml_recover(bool checkext)
*/
p = vim_strsave(fname_used); // save "fname_used" for the message:
// mf_open() will consume "fname_used"!
- mfp = mf_open(fname_used, O_RDONLY);
+ mfp = mf_open((char *)fname_used, O_RDONLY);
fname_used = p;
if (mfp == NULL || mfp->mf_fd < 0) {
semsg(_("E306: Cannot open %s"), fname_used);
@@ -862,7 +857,7 @@ void ml_recover(bool checkext)
if ((hp = mf_get(mfp, 0, 1)) == NULL) {
msg_start();
msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
- msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
+ msg_outtrans_attr((char *)mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
attr | MSG_HIST);
msg_end();
@@ -871,7 +866,7 @@ void ml_recover(bool checkext)
b0p = hp->bh_data;
if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0) {
msg_start();
- msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
+ msg_outtrans_attr((char *)mfp->mf_fname, MSG_HIST);
msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
MSG_HIST);
msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
@@ -884,7 +879,7 @@ void ml_recover(bool checkext)
}
if (b0_magic_wrong(b0p)) {
msg_start();
- msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
+ msg_outtrans_attr((char *)mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_(" cannot be used on this computer.\n"),
attr | MSG_HIST);
msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
@@ -906,7 +901,7 @@ void ml_recover(bool checkext)
mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
if (mfp->mf_page_size < previous_page_size) {
msg_start();
- msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
+ msg_outtrans_attr((char *)mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
attr | MSG_HIST);
msg_end();
@@ -927,11 +922,9 @@ void ml_recover(bool checkext)
b0p = hp->bh_data;
}
- /*
- * If .swp file name given directly, use name from swap file for buffer.
- */
+ // If .swp file name given directly, use name from swap file for buffer.
if (directly) {
- expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
+ expand_env((char *)b0p->b0_fname, NameBuff, MAXPATHL);
if (setfname(curbuf, (char *)NameBuff, NULL, true) == FAIL) {
goto theend;
}
@@ -998,7 +991,7 @@ void ml_recover(bool checkext)
set_fileformat(b0_ff - 1, OPT_LOCAL);
}
if (b0_fenc != NULL) {
- set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL);
+ set_option_value_give_err("fenc", 0L, (char *)b0_fenc, OPT_LOCAL);
xfree(b0_fenc);
}
unchanged(curbuf, true, true);
@@ -1040,7 +1033,7 @@ void ml_recover(bool checkext)
if (pp->pb_id == PTR_ID) { // it is a pointer block
// check line count when using pointer block first time
if (idx == 0 && line_count != 0) {
- for (i = 0; i < (int)pp->pb_count; ++i) {
+ for (i = 0; i < (int)pp->pb_count; i++) {
line_count -= pp->pb_pointer[i].pe_line_count;
}
if (line_count != 0) {
@@ -1076,7 +1069,7 @@ void ml_recover(bool checkext)
ml_append(lnum++, _("???LINES MISSING"),
(colnr_T)0, true);
}
- ++idx; // get same block again for next index
+ idx++; // get same block again for next index
continue;
}
@@ -1136,12 +1129,12 @@ void ml_recover(bool checkext)
has_error = true;
}
- for (i = 0; i < dp->db_line_count; ++i) {
+ for (i = 0; i < dp->db_line_count; i++) {
txt_start = (dp->db_index[i] & DB_INDEX_MASK);
if (txt_start <= (int)HEADER_SIZE
|| txt_start >= (int)dp->db_txt_end) {
p = (char_u *)"???";
- ++error;
+ error++;
} else {
p = (char_u *)dp + txt_start;
}
@@ -1182,7 +1175,7 @@ void ml_recover(bool checkext)
buf_inc_changedtick(curbuf);
}
} else {
- for (idx = 1; idx <= lnum; ++idx) {
+ for (idx = 1; idx <= lnum; idx++) {
// Need to copy one line, fetching the other one may flush it.
p = vim_strsave(ml_get(idx));
i = STRCMP(p, ml_get(idx + lnum));
@@ -1206,14 +1199,14 @@ void ml_recover(bool checkext)
curbuf->b_flags |= BF_RECOVERED;
check_cursor();
- recoverymode = FALSE;
+ recoverymode = false;
if (got_int) {
emsg(_("E311: Recovery Interrupted"));
} else if (error) {
- ++no_wait_return;
+ no_wait_return++;
msg(">>>>>>>>>>>>>");
emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
- --no_wait_return;
+ no_wait_return--;
msg(_("See \":help E312\" for more information."));
msg(">>>>>>>>>>>>>");
} else {
@@ -1227,11 +1220,11 @@ void ml_recover(bool checkext)
msg_puts(_("\nYou may want to delete the .swp file now.\n\n"));
cmdline_row = msg_row;
}
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
theend:
xfree(fname_used);
- recoverymode = FALSE;
+ recoverymode = false;
if (mfp != NULL) {
if (hp != NULL) {
mf_put(mfp, hp, false, false);
@@ -1243,7 +1236,7 @@ theend:
xfree(buf);
}
if (serious_error && called_from_main) {
- ml_close(curbuf, TRUE);
+ ml_close(curbuf, true);
} else {
apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, false, curbuf);
apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, false, curbuf);
@@ -1260,7 +1253,7 @@ theend:
/// - find the name of the n'th swap file when recovering
///
/// @param fname base for swap file name
-/// @param list when TRUE, list the swap file names
+/// @param list when true, list the swap file names
/// @param nr when non-zero, return nr'th swap file name
/// @param fname_out result when "nr" > 0
int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
@@ -1272,7 +1265,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
int num_files;
int file_count = 0;
char **files;
- char_u *dirp;
+ char *dirp;
char_u *dir_name;
char_u *fname_res = NULL;
#ifdef HAVE_READLINK
@@ -1304,7 +1297,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
// Isolate a directory name from *dirp and put it in dir_name (we know
// it is large enough, so use 31000 for length).
// Advance dirp to next directory name.
- (void)copy_option_part((char **)&dirp, (char *)dir_name, 31000, ",");
+ (void)copy_option_part(&dirp, (char *)dir_name, 31000, ",");
if (dir_name[0] == '.' && dir_name[1] == NUL) { // check current dir
if (fname == NULL) {
@@ -1315,7 +1308,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
names[2] = xstrdup(".sw?");
num_names = 3;
} else {
- num_names = recov_file_names(names, fname_res, TRUE);
+ num_names = recov_file_names(names, fname_res, true);
}
} else { // check directory dir_name
if (fname == NULL) {
@@ -1338,7 +1331,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
tail = (char_u *)path_tail((char *)fname_res);
tail = (char_u *)concat_fnames((char *)dir_name, (char *)tail, true);
}
- num_names = recov_file_names(names, tail, FALSE);
+ num_names = recov_file_names(names, tail, false);
xfree(tail);
}
}
@@ -1384,7 +1377,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
if (--num_files == 0) {
xfree(files);
} else {
- for (; i < num_files; ++i) {
+ for (; i < num_files; i++) {
files[i] = files[i + 1];
}
}
@@ -1395,7 +1388,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
file_count += num_files;
if (nr <= file_count) {
*fname_out = vim_strsave((char_u *)files[nr - 1 + num_files - file_count]);
- dirp = (char_u *)""; // stop searching
+ dirp = ""; // stop searching
}
} else if (list) {
if (dir_name[0] == '.' && dir_name[1] == NUL) {
@@ -1411,7 +1404,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
}
if (num_files) {
- for (int i = 0; i < num_files; ++i) {
+ for (int i = 0; i < num_files; i++) {
// print the swap file name
msg_outnum((long)++file_count);
msg_puts(". ");
@@ -1427,7 +1420,7 @@ int recover_names(char_u *fname, int list, int nr, char_u **fname_out)
file_count += num_files;
}
- for (int i = 0; i < num_names; ++i) {
+ for (int i = 0; i < num_names; i++) {
xfree(names[i]);
}
if (num_files > 0) {
@@ -1452,7 +1445,7 @@ char *make_percent_swname(const char *dir, const char *name)
*d = '%';
}
}
- d = concat_fnames(dir, s, TRUE);
+ d = concat_fnames(dir, s, true);
xfree(s);
xfree(f);
}
@@ -1660,12 +1653,12 @@ static int recov_file_names(char **names, char_u *path, int prepend_dot)
p += i; // file name has been expanded to full path
}
if (STRCMP(p, names[num_names]) != 0) {
- ++num_names;
+ num_names++;
} else {
xfree(names[num_names]);
}
} else {
- ++num_names;
+ num_names++;
}
return num_names;
@@ -1673,8 +1666,8 @@ static int recov_file_names(char **names, char_u *path, int prepend_dot)
/// sync all memlines
///
-/// @param check_file if TRUE, check if original file exists and was not changed.
-/// @param check_char if TRUE, stop syncing when character becomes available, but
+/// @param check_file if true, check if original file exists and was not changed.
+/// @param check_char if true, stop syncing when character becomes available, but
///
/// always sync at least one block.
void ml_sync_all(int check_file, int check_char, bool do_fsync)
@@ -1719,7 +1712,7 @@ void ml_sync_all(int check_file, int check_char, bool do_fsync)
/// Used for the :preserve command and when the original file has been
/// changed or deleted.
///
-/// @param message if TRUE, the success of preserving is reported.
+/// @param message if true, the success of preserving is reported.
void ml_preserve(buf_T *buf, int message, bool do_fsync)
{
bhdr_T *hp;
@@ -1913,7 +1906,7 @@ int ml_line_alloced(void)
/// "line" does not need to be allocated, but can't be another line in a
/// buffer, unlocking may make it invalid.
///
-/// newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
+/// newfile: true when starting to edit a new file, meaning that pe_old_lnum
/// will be set for recovery
/// Check: The caller of this function should probably also call
/// appended_lines().
@@ -1927,7 +1920,7 @@ int ml_line_alloced(void)
int ml_append(linenr_T lnum, char *line, colnr_T len, bool newfile)
{
// When starting up, we might still need to create the memfile
- if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL) {
+ if (curbuf->b_ml.ml_mfp == NULL && open_buffer(false, NULL, 0) == FAIL) {
return FAIL;
}
@@ -1954,7 +1947,7 @@ int ml_append_buf(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, bool new
if (buf->b_ml.ml_line_lnum != 0) {
ml_flush_line(buf);
}
- return ml_append_int(buf, lnum, line, len, newfile, FALSE);
+ return ml_append_int(buf, lnum, line, len, newfile, false);
}
/// @param lnum append after this line (can be 0)
@@ -2043,7 +2036,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
dp = hp->bh_data;
}
- ++buf->b_ml.ml_line_count;
+ buf->b_ml.ml_line_count++;
if ((int)dp->db_free >= space_needed) { // enough room in data block
/*
@@ -2184,7 +2177,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
memmove((char *)dp_right + dp_right->db_txt_start,
line, (size_t)len);
- ++line_count_right;
+ line_count_right++;
}
/*
* may move lines from the left/old block to the right/new one.
@@ -2224,7 +2217,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
}
memmove((char *)dp_left + dp_left->db_txt_start,
line, (size_t)len);
- ++line_count_left;
+ line_count_left++;
}
if (db_idx < 0) { // left block is new
@@ -2291,7 +2284,7 @@ static int ml_append_int(buf_T *buf, linenr_T lnum, char_u *line, colnr_T len, b
&pp->pb_pointer[pb_idx + 1],
(size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
}
- ++pp->pb_count;
+ pp->pb_count++;
pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
@@ -2458,7 +2451,7 @@ int ml_replace(linenr_T lnum, char *line, bool copy)
/// Do not use it after calling ml_replace().
///
/// Check: The caller of this function should probably also call
-/// changed_lines(), unless update_screen(NOT_VALID) is used.
+/// changed_lines(), unless update_screen(UPD_NOT_VALID) is used.
///
/// @return FAIL for failure, OK otherwise
int ml_replace_buf(buf_T *buf, linenr_T lnum, char_u *line, bool copy)
@@ -2566,7 +2559,7 @@ static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message)
count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 2;
idx = lnum - buf->b_ml.ml_locked_low;
- --buf->b_ml.ml_line_count;
+ buf->b_ml.ml_line_count--;
line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
if (idx == 0) { // first line in block, text at the end
@@ -2710,7 +2703,7 @@ linenr_T ml_firstmarked(void)
dp = hp->bh_data;
for (i = lnum - curbuf->b_ml.ml_locked_low;
- lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) {
+ lnum <= curbuf->b_ml.ml_locked_high; i++, lnum++) {
if ((dp->db_index[i]) & DB_MARKED) {
(dp->db_index[i]) &= DB_INDEX_MASK;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
@@ -2750,7 +2743,7 @@ void ml_clearmarked(void)
dp = hp->bh_data;
for (i = lnum - curbuf->b_ml.ml_locked_low;
- lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum) {
+ lnum <= curbuf->b_ml.ml_locked_high; i++, lnum++) {
if ((dp->db_index[i]) & DB_MARKED) {
(dp->db_index[i]) &= DB_INDEX_MASK;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
@@ -2970,7 +2963,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
high = buf->b_ml.ml_line_count;
if (action == ML_FIND) { // first try stack entries
- for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top) {
+ for (top = buf->b_ml.ml_stack_top - 1; top >= 0; top--) {
ip = &(buf->b_ml.ml_stack[top]);
if (ip->ip_low <= lnum && ip->ip_high >= lnum) {
bnum = ip->ip_bnum;
@@ -2998,9 +2991,9 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
* update high for insert/delete
*/
if (action == ML_INSERT) {
- ++high;
+ high++;
} else if (action == ML_DELETE) {
- --high;
+ high--;
}
dp = hp->bh_data;
@@ -3026,8 +3019,8 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
ip->ip_high = high;
ip->ip_index = -1; // index not known yet
- dirty = FALSE;
- for (idx = 0; idx < (int)pp->pb_count; ++idx) {
+ dirty = false;
+ for (idx = 0; idx < (int)pp->pb_count; idx++) {
t = pp->pb_pointer[idx].pe_line_count;
CHECK(t == 0, _("pe_line_count is zero"));
if ((low += t) > lnum) {
@@ -3045,7 +3038,7 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
if (bnum != bnum2) {
bnum = bnum2;
pp->pb_pointer[idx].pe_bnum = bnum;
- dirty = TRUE;
+ dirty = true;
}
}
@@ -3063,10 +3056,10 @@ static bhdr_T *ml_find_line(buf_T *buf, linenr_T lnum, int action)
}
if (action == ML_DELETE) {
pp->pb_pointer[idx].pe_line_count--;
- dirty = TRUE;
+ dirty = true;
} else if (action == ML_INSERT) {
pp->pb_pointer[idx].pe_line_count++;
- dirty = TRUE;
+ dirty = true;
}
mf_put(mfp, hp, dirty, false);
}
@@ -3124,7 +3117,7 @@ static void ml_lineadd(buf_T *buf, int count)
memfile_T *mfp = buf->b_ml.ml_mfp;
bhdr_T *hp;
- for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx) {
+ for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; idx--) {
ip = &(buf->b_ml.ml_stack[idx]);
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL) {
break;
@@ -3208,7 +3201,7 @@ int resolve_symlink(const char_u *fname, char_u *buf)
* be consistent even when opening a relative symlink from different
* working directories.
*/
- return vim_FullName((char *)tmp, (char *)buf, MAXPATHL, TRUE);
+ return vim_FullName((char *)tmp, (char *)buf, MAXPATHL, true);
}
#endif
@@ -3280,7 +3273,7 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname)
retval = vim_strsave(fname);
} else if (dname[0] == '.' && vim_ispathsep(dname[1])) {
if (tail == fname) { // no path before file name
- retval = (char_u *)concat_fnames((char *)dname + 2, (char *)tail, TRUE);
+ retval = (char_u *)concat_fnames((char *)dname + 2, (char *)tail, true);
} else {
save_char = *tail;
*tail = NUL;
@@ -3290,7 +3283,7 @@ char_u *get_file_in_dir(char_u *fname, char_u *dname)
xfree(t);
}
} else {
- retval = (char_u *)concat_fnames((char *)dname, (char *)tail, TRUE);
+ retval = (char_u *)concat_fnames((char *)dname, (char *)tail, true);
}
return retval;
@@ -3304,7 +3297,7 @@ static void attention_message(buf_T *buf, char_u *fname)
{
assert(buf->b_fname != NULL);
- ++no_wait_return;
+ no_wait_return++;
(void)emsg(_("E325: ATTENTION"));
msg_puts(_("\nFound a swap file by the name \""));
msg_home_replace(fname);
@@ -3339,7 +3332,7 @@ static void attention_message(buf_T *buf, char_u *fname)
msg_outtrans((char *)fname);
msg_puts(_("\"\n to avoid this message.\n"));
cmdline_row = msg_row;
- --no_wait_return;
+ no_wait_return--;
}
/// Trigger the SwapExists autocommands.
@@ -3460,7 +3453,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
&& !buf->b_help && !(buf->b_flags & BF_DUMMY)) {
int fd;
struct block0 b0;
- int differ = FALSE;
+ int differ = false;
// Try to read block 0 from the swap file to get the original
// file name (and inode number).
@@ -3477,19 +3470,19 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// Symlinks may point to the same file even
// when the name differs, need to check the
// inode too.
- expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
+ expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL);
+ if (fnamecmp_ino((char_u *)buf->b_ffname, (char_u *)NameBuff,
char_to_long(b0.b0_ino))) {
- differ = TRUE;
+ differ = true;
}
}
} else {
// The name in the swap file may be
// "~user/path/file". Expand it first.
- expand_env(b0.b0_fname, NameBuff, MAXPATHL);
- if (fnamecmp_ino((char_u *)buf->b_ffname, NameBuff,
+ expand_env((char *)b0.b0_fname, NameBuff, MAXPATHL);
+ if (fnamecmp_ino((char_u *)buf->b_ffname, (char_u *)NameBuff,
char_to_long(b0.b0_ino))) {
- differ = TRUE;
+ differ = true;
}
}
}
@@ -3499,7 +3492,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
// give the ATTENTION message when there is an old swap file
// for the current file, and the buffer was not recovered.
if (differ == false && !(curbuf->b_flags & BF_RECOVERED)
- && vim_strchr((char *)p_shm, SHM_ATTENTION) == NULL) {
+ && vim_strchr(p_shm, SHM_ATTENTION) == NULL) {
int choice = 0;
process_still_running = false;
@@ -3569,7 +3562,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
if (choice > 0) {
switch (choice) {
case 1:
- buf->b_p_ro = TRUE;
+ buf->b_p_ro = true;
break;
case 2:
break;
@@ -3584,7 +3577,7 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
break;
case 6:
swap_exists_action = SEA_QUIT;
- got_int = TRUE;
+ got_int = true;
break;
}
@@ -3615,13 +3608,13 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, bool *found_
XFREE_CLEAR(fname);
break;
}
- --fname[n - 2]; // ".svz", ".suz", etc.
+ fname[n - 2]--; // ".svz", ".suz", etc.
fname[n - 1] = 'z' + 1;
}
- --fname[n - 1]; // ".swo", ".swn", etc.
+ fname[n - 1]--; // ".swo", ".swn", etc.
}
- if (os_isdir((char_u *)dir_name)) {
+ if (os_isdir(dir_name)) {
*found_existing_dir = true;
} else if (!*found_existing_dir && **dirp == NUL) {
int ret;
@@ -3672,18 +3665,18 @@ static int b0_magic_wrong(ZERO_BL *b0p)
///
/// current file doesn't exist, file for swap file exist, file name(s) not
/// available -> probably different
-/// == 0 != 0 FAIL X TRUE
-/// == 0 != 0 X FAIL TRUE
+/// == 0 != 0 FAIL X true
+/// == 0 != 0 X FAIL true
///
/// current file exists, inode for swap unknown, file name(s) not
/// available -> probably different
-/// != 0 == 0 FAIL X TRUE
-/// != 0 == 0 X FAIL TRUE
+/// != 0 == 0 FAIL X true
+/// != 0 == 0 X FAIL true
///
/// current file doesn't exist, inode for swap unknown, one file name not
/// available -> probably different
-/// == 0 == 0 FAIL OK TRUE
-/// == 0 == 0 OK FAIL TRUE
+/// == 0 == 0 FAIL OK true
+/// == 0 == 0 OK FAIL true
///
/// current file doesn't exist, inode for swap unknown, both file names not
/// available -> compare file names
@@ -3727,8 +3720,8 @@ static bool fnamecmp_ino(char_u *fname_c, char_u *fname_s, long ino_block0)
* One of the inode numbers is unknown, try a forced vim_FullName() and
* compare the file names.
*/
- retval_c = vim_FullName((char *)fname_c, (char *)buf_c, MAXPATHL, TRUE);
- retval_s = vim_FullName((char *)fname_s, (char *)buf_s, MAXPATHL, TRUE);
+ retval_c = vim_FullName((char *)fname_c, (char *)buf_c, MAXPATHL, true);
+ retval_s = vim_FullName((char *)fname_s, (char *)buf_s, MAXPATHL, true);
if (retval_c == OK && retval_s == OK) {
return STRCMP(buf_c, buf_s) != 0;
}
@@ -4168,7 +4161,7 @@ void goto_byte(long cnt)
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = (colnr_T)boff;
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
check_cursor();
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 5ae7f7bbe3..a9785fcb7c 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -9,6 +9,7 @@
#include <string.h>
#include "nvim/api/extmark.h"
+#include "nvim/arglist.h"
#include "nvim/context.h"
#include "nvim/decoration_provider.h"
#include "nvim/eval.h"
@@ -59,6 +60,8 @@ void try_to_free_memory(void)
// Try to save all buffers and release as many blocks as possible
mf_release_all();
+ arena_free_reuse_blks();
+
trying_to_free = false;
}
@@ -527,21 +530,19 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
}
#define ARENA_BLOCK_SIZE 4096
+#define REUSE_MAX 4
+
+static struct consumed_blk *arena_reuse_blk;
+static size_t arena_reuse_blk_count = 0;
-void arena_start(Arena *arena, ArenaMem *reuse_blk)
+static void arena_free_reuse_blks(void)
{
- if (reuse_blk && *reuse_blk) {
- arena->cur_blk = (char *)(*reuse_blk);
- *reuse_blk = NULL;
- } else {
- arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
+ while (arena_reuse_blk_count > 0) {
+ struct consumed_blk *blk = arena_reuse_blk;
+ arena_reuse_blk = arena_reuse_blk->prev;
+ xfree(blk);
+ arena_reuse_blk_count--;
}
- arena->pos = 0;
- arena->size = ARENA_BLOCK_SIZE;
- // address is the same as as (struct consumed_blk *)arena->cur_blk
- struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
- assert((char *)blk == (char *)arena->cur_blk);
- blk->prev = NULL;
}
/// Finnish the allocations in an arena.
@@ -557,17 +558,45 @@ ArenaMem arena_finish(Arena *arena)
return res;
}
+void alloc_block(Arena *arena)
+{
+ struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk;
+ if (arena_reuse_blk_count > 0) {
+ arena->cur_blk = (char *)arena_reuse_blk;
+ arena_reuse_blk = arena_reuse_blk->prev;
+ arena_reuse_blk_count--;
+ } else {
+ arena_alloc_count++;
+ arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
+ }
+ arena->pos = 0;
+ arena->size = ARENA_BLOCK_SIZE;
+ struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
+ blk->prev = prev_blk;
+}
+
+/// @param arena if NULL, do a global allocation. caller must then free the value!
+/// @param size if zero, will still return a non-null pointer, but not a unique one
void *arena_alloc(Arena *arena, size_t size, bool align)
{
+ if (!arena) {
+ return xmalloc(size);
+ }
if (align) {
arena->pos = (arena->pos + (ARENA_ALIGN - 1)) & ~(ARENA_ALIGN - 1);
}
- if (arena->pos + size > arena->size) {
- if (size > (arena->size - sizeof(struct consumed_blk)) >> 1) {
+ if (arena->pos + size > arena->size || !arena->cur_blk) {
+ if (size > (ARENA_BLOCK_SIZE - sizeof(struct consumed_blk)) >> 1) {
// if allocation is too big, allocate a large block with the requested
// size, but still with block pointer head. We do this even for
// arena->size / 2, as there likely is space left for the next
// small allocation in the current block.
+ if (!arena->cur_blk) {
+ // to simplify free-list management, arena->cur_blk must
+ // always be a normal, ARENA_BLOCK_SIZE sized, block
+ alloc_block(arena);
+ }
+ arena_alloc_count++;
char *alloc = xmalloc(size + sizeof(struct consumed_blk));
struct consumed_blk *cur_blk = (struct consumed_blk *)arena->cur_blk;
struct consumed_blk *fix_blk = (struct consumed_blk *)alloc;
@@ -575,12 +604,7 @@ void *arena_alloc(Arena *arena, size_t size, bool align)
cur_blk->prev = fix_blk;
return (alloc + sizeof(struct consumed_blk));
} else {
- struct consumed_blk *prev_blk = (struct consumed_blk *)arena->cur_blk;
- arena->cur_blk = xmalloc(ARENA_BLOCK_SIZE);
- arena->pos = 0;
- arena->size = ARENA_BLOCK_SIZE;
- struct consumed_blk *blk = arena_alloc(arena, sizeof(struct consumed_blk), true);
- blk->prev = prev_blk;
+ alloc_block(arena);
}
}
@@ -589,15 +613,17 @@ void *arena_alloc(Arena *arena, size_t size, bool align)
return mem;
}
-void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk)
+void arena_mem_free(ArenaMem mem)
{
struct consumed_blk *b = mem;
// peel of the first block, as it is guaranteed to be ARENA_BLOCK_SIZE,
// not a custom fix_blk
- if (reuse_blk && *reuse_blk == NULL && b != NULL) {
- *reuse_blk = b;
+ if (arena_reuse_blk_count < REUSE_MAX && b != NULL) {
+ struct consumed_blk *reuse_blk = b;
b = b->prev;
- (*reuse_blk)->prev = NULL;
+ reuse_blk->prev = arena_reuse_blk;
+ arena_reuse_blk = reuse_blk;
+ arena_reuse_blk_count++;
}
while (b) {
@@ -617,8 +643,10 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
#if defined(EXITFREE)
+# include "nvim/autocmd.h"
# include "nvim/buffer.h"
# include "nvim/charset.h"
+# include "nvim/cmdhist.h"
# include "nvim/diff.h"
# include "nvim/edit.h"
# include "nvim/eval/typval.h"
@@ -626,9 +654,9 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/ex_docmd.h"
# include "nvim/ex_getln.h"
# include "nvim/file_search.h"
-# include "nvim/fileio.h"
# include "nvim/fold.h"
# include "nvim/getchar.h"
+# include "nvim/grid.h"
# include "nvim/mark.h"
# include "nvim/mbyte.h"
# include "nvim/memline.h"
@@ -640,7 +668,6 @@ char *arena_memdupz(Arena *arena, const char *buf, size_t size)
# include "nvim/path.h"
# include "nvim/quickfix.h"
# include "nvim/regexp.h"
-# include "nvim/screen.h"
# include "nvim/search.h"
# include "nvim/spell.h"
# include "nvim/syntax.h"
@@ -797,6 +824,9 @@ void free_all_mem(void)
nlua_free_all_mem();
ui_free_all_mem();
+
+ // should be last, in case earlier free functions deallocates arenas
+ arena_free_reuse_blks();
}
#endif
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index 63d607c2ce..f407192331 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -6,6 +6,8 @@
#include <stdint.h> // for uint8_t
#include <time.h> // for time_t
+#include "nvim/macros.h"
+
/// `malloc()` function signature
typedef void *(*MemMalloc)(size_t);
@@ -37,6 +39,8 @@ extern MemRealloc mem_realloc;
extern bool entered_free_all_mem;
#endif
+EXTERN size_t arena_alloc_count INIT(=0);
+
typedef struct consumed_blk {
struct consumed_blk *prev;
} *ArenaMem;
@@ -48,7 +52,7 @@ typedef struct {
size_t pos, size;
} Arena;
-// inits an empty arena. use arena_start() to actually allocate space!
+// inits an empty arena.
#define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 }
#define kv_fixsize_arena(a, v, s) \
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 16802a4e50..cb6918cd27 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -23,7 +23,7 @@
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/message.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -419,7 +419,7 @@ static int add_menu_path(const char *const menu_path, vimmenu_T *menuarg, const
p = (call_data == NULL) ? NULL : xstrdup(call_data);
// loop over all modes, may add more than one
- for (i = 0; i < MENU_MODES; ++i) {
+ for (i = 0; i < MENU_MODES; i++) {
if (modes & (1 << i)) {
// free any old menu
free_menu_string(menu, i);
@@ -853,7 +853,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
msg_puts(" ");
}
// Same highlighting as for directories!?
- msg_outtrans_attr((char_u *)menu->name, HL_ATTR(HLF_D));
+ msg_outtrans_attr(menu->name, HL_ATTR(HLF_D));
}
if (menu != NULL && menu->children == NULL) {
@@ -914,7 +914,7 @@ static void show_menus_recursive(vimmenu_T *menu, int modes, int depth)
*/
static vimmenu_T *expand_menu = NULL;
static int expand_modes = 0x0;
-static int expand_emenu; // TRUE for ":emenu" command
+static int expand_emenu; // true for ":emenu" command
/*
* Work out what to complete when doing command line completion of menu names.
@@ -933,7 +933,7 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
xp->xp_context = EXPAND_UNSUCCESSFUL;
// Check for priority numbers, enable and disable
- for (p = arg; *p; ++p) {
+ for (p = arg; *p; p++) {
if (!ascii_isdigit(*p) && *p != '.') {
break;
}
@@ -952,12 +952,12 @@ char *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char *arg, bool for
}
while (*p != NUL && ascii_iswhite(*p)) {
- ++p;
+ p++;
}
arg = after_dot = p;
- for (; *p && !ascii_iswhite(*p); ++p) {
+ for (; *p && !ascii_iswhite(*p); p++) {
if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL) {
p++;
} else if (*p == '.') {
@@ -1056,7 +1056,7 @@ char *get_menu_name(expand_T *xp, int idx)
} else {
str = menu->dname;
if (menu->en_dname == NULL) {
- should_advance = TRUE;
+ should_advance = true;
}
}
} else {
@@ -1163,10 +1163,8 @@ char *menu_name_skip(char *const name)
return p;
}
-/*
- * Return TRUE when "name" matches with menu "menu". The name is compared in
- * two ways: raw menu name and menu name without '&'. ignore part after a TAB.
- */
+/// Return true when "name" matches with menu "menu". The name is compared in
+/// two ways: raw menu name and menu name without '&'. ignore part after a TAB.
static bool menu_name_equal(const char *const name, const vimmenu_T *const menu)
{
if (menu->en_name != NULL
@@ -1181,7 +1179,7 @@ static bool menu_namecmp(const char *const name, const char *const mname)
{
int i;
- for (i = 0; name[i] != NUL && name[i] != TAB; ++i) {
+ for (i = 0; name[i] != NUL && name[i] != TAB; i++) {
if (name[i] != mname[i]) {
break;
}
@@ -1405,10 +1403,8 @@ bool menu_is_toolbar(const char *const name)
return STRNCMP(name, "ToolBar", 7) == 0;
}
-/*
- * Return TRUE if the name is a menu separator identifier: Starts and ends
- * with '-'
- */
+/// Return true if the name is a menu separator identifier: Starts and ends
+/// with '-'
int menu_is_separator(char *name)
{
return name[0] == '-' && name[STRLEN(name) - 1] == '-';
@@ -1659,26 +1655,19 @@ void ex_emenu(exarg_T *eap)
if (arg[0] && ascii_iswhite(arg[1])) {
switch (arg[0]) {
case 'n':
- mode_idx = MENU_INDEX_NORMAL;
- break;
+ mode_idx = MENU_INDEX_NORMAL; break;
case 'v':
- mode_idx = MENU_INDEX_VISUAL;
- break;
+ mode_idx = MENU_INDEX_VISUAL; break;
case 's':
- mode_idx = MENU_INDEX_SELECT;
- break;
+ mode_idx = MENU_INDEX_SELECT; break;
case 'o':
- mode_idx = MENU_INDEX_OP_PENDING;
- break;
+ mode_idx = MENU_INDEX_OP_PENDING; break;
case 't':
- mode_idx = MENU_INDEX_TERMINAL;
- break;
+ mode_idx = MENU_INDEX_TERMINAL; break;
case 'i':
- mode_idx = MENU_INDEX_INSERT;
- break;
+ mode_idx = MENU_INDEX_INSERT; break;
case 'c':
- mode_idx = MENU_INDEX_CMDLINE;
- break;
+ mode_idx = MENU_INDEX_CMDLINE; break;
default:
semsg(_(e_invarg2), arg);
return;
@@ -1814,9 +1803,9 @@ static char *menu_skip_part(char *p)
{
while (*p != NUL && *p != '.' && !ascii_iswhite(*p)) {
if ((*p == '\\' || *p == Ctrl_V) && p[1] != NUL) {
- ++p;
+ p++;
}
- ++p;
+ p++;
}
return p;
}
@@ -1952,7 +1941,7 @@ static void menuitem_getinfo(const char *menu_name, const vimmenu_T *menu, int m
/// "menu_info()" function
/// Return information about a menu (including all the child menus)
-void f_menu_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_menu_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
tv_dict_alloc_ret(rettv);
dict_T *const retdict = rettv->vval.v_dict;
diff --git a/src/nvim/menu.h b/src/nvim/menu.h
index 9a60ebfb83..be294a1831 100644
--- a/src/nvim/menu.h
+++ b/src/nvim/menu.h
@@ -4,28 +4,9 @@
#include <stdbool.h> // for bool
#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/menu_defs.h"
#include "nvim/types.h" // for char_u and expand_T
-/// @}
-/// note MENU_INDEX_TIP is not a 'real' mode
-
-/// Menu modes
-/// \addtogroup MENU_MODES
-/// @{
-#define MENU_NORMAL_MODE (1 << MENU_INDEX_NORMAL)
-#define MENU_VISUAL_MODE (1 << MENU_INDEX_VISUAL)
-#define MENU_SELECT_MODE (1 << MENU_INDEX_SELECT)
-#define MENU_OP_PENDING_MODE (1 << MENU_INDEX_OP_PENDING)
-#define MENU_INSERT_MODE (1 << MENU_INDEX_INSERT)
-#define MENU_CMDLINE_MODE (1 << MENU_INDEX_CMDLINE)
-#define MENU_TERMINAL_MODE (1 << MENU_INDEX_TERMINAL)
-#define MENU_TIP_MODE (1 << MENU_INDEX_TIP)
-#define MENU_ALL_MODES ((1 << MENU_INDEX_TIP) - 1)
-/// @}
-
-/// Start a menu name with this to not include it on the main menu bar
-#define MNU_HIDDEN_CHAR ']'
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "menu.h.generated.h"
#endif
diff --git a/src/nvim/menu_defs.h b/src/nvim/menu_defs.h
new file mode 100644
index 0000000000..5fdb222bde
--- /dev/null
+++ b/src/nvim/menu_defs.h
@@ -0,0 +1,64 @@
+#ifndef NVIM_MENU_DEFS_H
+#define NVIM_MENU_DEFS_H
+
+#include <stdbool.h> // for bool
+
+/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
+/// \addtogroup MENU_INDEX
+/// @{
+enum {
+ MENU_INDEX_INVALID = -1,
+ MENU_INDEX_NORMAL = 0,
+ MENU_INDEX_VISUAL = 1,
+ MENU_INDEX_SELECT = 2,
+ MENU_INDEX_OP_PENDING = 3,
+ MENU_INDEX_INSERT = 4,
+ MENU_INDEX_CMDLINE = 5,
+ MENU_INDEX_TERMINAL = 6,
+ MENU_INDEX_TIP = 7,
+ MENU_MODES = 8,
+};
+/// @}
+
+/// Menu modes
+/// \addtogroup MENU_MODES
+/// @{
+enum {
+ MENU_NORMAL_MODE = 1 << MENU_INDEX_NORMAL,
+ MENU_VISUAL_MODE = 1 << MENU_INDEX_VISUAL,
+ MENU_SELECT_MODE = 1 << MENU_INDEX_SELECT,
+ MENU_OP_PENDING_MODE = 1 << MENU_INDEX_OP_PENDING,
+ MENU_INSERT_MODE = 1 << MENU_INDEX_INSERT,
+ MENU_CMDLINE_MODE = 1 << MENU_INDEX_CMDLINE,
+ MENU_TERMINAL_MODE = 1 << MENU_INDEX_TERMINAL,
+ MENU_TIP_MODE = 1 << MENU_INDEX_TIP,
+ MENU_ALL_MODES = (1 << MENU_INDEX_TIP) - 1,
+};
+/// @}
+/// note MENU_INDEX_TIP is not a 'real' mode
+
+/// Start a menu name with this to not include it on the main menu bar
+#define MNU_HIDDEN_CHAR ']'
+
+typedef struct VimMenu vimmenu_T;
+
+struct VimMenu {
+ int modes; ///< Which modes is this menu visible for
+ int enabled; ///< for which modes the menu is enabled
+ char *name; ///< Name of menu, possibly translated
+ char *dname; ///< Displayed Name ("name" without '&')
+ char *en_name; ///< "name" untranslated, NULL when
+ ///< was not translated
+ char *en_dname; ///< NULL when "dname" untranslated
+ int mnemonic; ///< mnemonic key (after '&')
+ char *actext; ///< accelerator text (after TAB)
+ long priority; ///< Menu order priority
+ char *strings[MENU_MODES]; ///< Mapped string for each mode
+ int noremap[MENU_MODES]; ///< A \ref REMAP_VALUES flag for each mode
+ bool silent[MENU_MODES]; ///< A silent flag for each mode
+ vimmenu_T *children; ///< Children of sub-menu
+ vimmenu_T *parent; ///< Parent of menu
+ vimmenu_T *next; ///< Next item in menu
+};
+
+#endif // NVIM_MENU_DEFS_H
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 7cccd046c9..80b5f53f4e 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -15,6 +15,7 @@
#include "nvim/ascii.h"
#include "nvim/assert.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
@@ -23,7 +24,9 @@
#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
+#include "nvim/indent.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
#include "nvim/main.h"
@@ -38,7 +41,7 @@
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -53,7 +56,7 @@ typedef struct msgchunk_S msgchunk_T;
struct msgchunk_S {
msgchunk_T *sb_next;
msgchunk_T *sb_prev;
- char sb_eol; // TRUE when line ends after this text
+ char sb_eol; // true when line ends after this text
int sb_msg_col; // column in which text starts
int sb_attr; // text attributes
char_u sb_text[1]; // text to be displayed, actually longer
@@ -63,7 +66,7 @@ struct msgchunk_S {
#define DLG_BUTTON_SEP '\n'
#define DLG_HOTKEY_CHAR '&'
-static int confirm_msg_used = FALSE; // displaying confirm_msg
+static int confirm_msg_used = false; // displaying confirm_msg
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "message.c.generated.h"
#endif
@@ -75,7 +78,7 @@ MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
static FILE *verbose_fd = NULL;
-static int verbose_did_open = FALSE;
+static int verbose_did_open = false;
bool keep_msg_more = false; // keep_msg was set by msgmore()
@@ -99,7 +102,7 @@ bool keep_msg_more = false; // keep_msg was set by msgmore()
* msg_scrolled How many lines the screen has been scrolled (because of
* messages). Used in update_screen() to scroll the screen
* back. Incremented each time the screen scrolls a line.
- * msg_scrolled_ign TRUE when msg_scrolled is non-zero and msg_puts_attr()
+ * msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_attr()
* writes something without scrolling should not make
* need_wait_return to be set. This is a hack to make ":ts"
* work without an extra prompt.
@@ -212,7 +215,7 @@ void msg_grid_validate(void)
/// Displays the string 's' on the status line
/// When terminal not initialized (yet) mch_errmsg(..) is used.
///
-/// @return TRUE if wait_return not called
+/// @return true if wait_return() not called
int msg(char *s)
{
return msg_attr_keep(s, 0, false, false);
@@ -262,7 +265,7 @@ void msg_multiline_attr(const char *s, int attr, bool check_int, bool *need_clea
// Print the rest of the message. We know there is no special
// character because strpbrk returned NULL
if (*s != NUL) {
- msg_outtrans_attr((char_u *)s, attr);
+ msg_outtrans_attr(s, attr);
}
}
@@ -302,7 +305,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
// Skip messages not match ":filter pattern".
// Don't filter when there is an error.
- if (!emsg_on_display && message_filtered((char_u *)s)) {
+ if (!emsg_on_display && message_filtered((char *)s)) {
return true;
}
@@ -316,13 +319,13 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
* break this loop, limit the recursiveness to 3 levels.
*/
if (entered >= 3) {
- return TRUE;
+ return true;
}
- ++entered;
+ entered++;
// Add message to history (unless it's a repeated kept message or a
// truncated message)
- if ((const char_u *)s != keep_msg
+ if (s != keep_msg
|| (*s != '<'
&& last_msg_hist != NULL
&& last_msg_hist->msg != NULL
@@ -332,7 +335,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
// Truncate the message if needed.
msg_start();
- buf = msg_strtrunc((char_u *)s, FALSE);
+ buf = msg_strtrunc((char_u *)s, false);
if (buf != NULL) {
s = (const char *)buf;
}
@@ -341,7 +344,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
if (multiline) {
msg_multiline_attr(s, attr, false, &need_clear);
} else {
- msg_outtrans_attr((char_u *)s, attr);
+ msg_outtrans_attr(s, attr);
}
if (need_clear) {
msg_clr_eos();
@@ -355,7 +358,7 @@ bool msg_attr_keep(const char *s, int attr, bool keep, bool multiline)
need_fileinfo = false;
xfree(buf);
- --entered;
+ entered--;
return retval;
}
@@ -417,7 +420,7 @@ void trunc_string(char *s, char *buf, int room_in, int buflen)
half = room / 2;
// First part: Start of the string.
- for (e = 0; len < half && e < buflen; ++e) {
+ for (e = 0; len < half && e < buflen; e++) {
if (s[e] == NUL) {
// text fits without truncating!
buf[e] = NUL;
@@ -524,7 +527,7 @@ int smsg_attr_keep(int attr, const char *s, ...)
* isn't printed each time when it didn't change.
*/
static int last_sourcing_lnum = 0;
-static char_u *last_sourcing_name = NULL;
+static char *last_sourcing_name = NULL;
/// Reset the last used sourcing name/lnum. Makes sure it is displayed again
/// for the next error message;
@@ -534,16 +537,16 @@ void reset_last_sourcing(void)
last_sourcing_lnum = 0;
}
-/// @return TRUE if "sourcing_name" differs from "last_sourcing_name".
-static int other_sourcing_name(void)
+/// @return true if "SOURCING_NAME" differs from "last_sourcing_name".
+static bool other_sourcing_name(void)
{
- if (sourcing_name != NULL) {
+ if (SOURCING_NAME != NULL) {
if (last_sourcing_name != NULL) {
- return STRCMP(sourcing_name, last_sourcing_name) != 0;
+ return strcmp(SOURCING_NAME, last_sourcing_name) != 0;
}
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Get the message about the source, as used for an error message
@@ -553,11 +556,19 @@ static int other_sourcing_name(void)
static char *get_emsg_source(void)
FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
- if (sourcing_name != NULL && other_sourcing_name()) {
+ if (SOURCING_NAME != NULL && other_sourcing_name()) {
+ char *sname = estack_sfile(ESTACK_NONE);
+ char *tofree = sname;
+
+ if (sname == NULL) {
+ sname = SOURCING_NAME;
+ }
+
const char *const p = _("Error detected while processing %s:");
- const size_t buf_len = STRLEN(sourcing_name) + strlen(p) + 1;
+ const size_t buf_len = STRLEN(sname) + strlen(p) + 1;
char *const buf = xmalloc(buf_len);
- snprintf(buf, buf_len, p, sourcing_name);
+ snprintf(buf, buf_len, p, sname);
+ xfree(tofree);
return buf;
}
return NULL;
@@ -572,13 +583,13 @@ static char *get_emsg_lnum(void)
{
// lnum is 0 when executing a command from the command line
// argument, we don't want a line number then
- if (sourcing_name != NULL
- && (other_sourcing_name() || sourcing_lnum != last_sourcing_lnum)
- && sourcing_lnum != 0) {
+ if (SOURCING_NAME != NULL
+ && (other_sourcing_name() || SOURCING_LNUM != last_sourcing_lnum)
+ && SOURCING_LNUM != 0) {
const char *const p = _("line %4ld:");
const size_t buf_len = 20 + strlen(p);
char *const buf = xmalloc(buf_len);
- snprintf(buf, buf_len, p, (long)sourcing_lnum);
+ snprintf(buf, buf_len, p, (long)SOURCING_LNUM);
return buf;
}
return NULL;
@@ -589,6 +600,15 @@ static char *get_emsg_lnum(void)
/// is only displayed if it changed.
void msg_source(int attr)
{
+ static bool recursive = false;
+
+ // Bail out if something called here causes an error.
+ if (recursive) {
+ return;
+ }
+ recursive = true;
+
+ msg_scroll = true; // this will take more than one line
no_wait_return++;
char *p = get_emsg_source();
if (p != NULL) {
@@ -599,33 +619,33 @@ void msg_source(int attr)
if (p != NULL) {
msg_attr(p, HL_ATTR(HLF_N));
xfree(p);
- last_sourcing_lnum = sourcing_lnum; // only once for each line
+ last_sourcing_lnum = SOURCING_LNUM; // only once for each line
}
// remember the last sourcing name printed, also when it's empty
- if (sourcing_name == NULL || other_sourcing_name()) {
- xfree(last_sourcing_name);
- if (sourcing_name == NULL) {
- last_sourcing_name = NULL;
- } else {
- last_sourcing_name = vim_strsave((char_u *)sourcing_name);
+ if (SOURCING_NAME == NULL || other_sourcing_name()) {
+ XFREE_CLEAR(last_sourcing_name);
+ if (SOURCING_NAME != NULL) {
+ last_sourcing_name = xstrdup(SOURCING_NAME);
}
}
- --no_wait_return;
+ no_wait_return--;
+
+ recursive = false;
}
-/// @return TRUE if not giving error messages right now:
+/// @return true if not giving error messages right now:
/// 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)
{
- if ((emsg_off > 0 && vim_strchr((char *)p_debug, 'm') == NULL
- && vim_strchr((char *)p_debug, 't') == NULL)
+ if ((emsg_off > 0 && vim_strchr(p_debug, 'm') == NULL
+ && vim_strchr(p_debug, 't') == NULL)
|| emsg_skip > 0) {
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
static bool emsg_multiline(const char *s, bool multiline)
@@ -645,7 +665,7 @@ static bool emsg_multiline(const char *s, bool multiline)
bool severe = emsg_severe;
emsg_severe = false;
- if (!emsg_off || vim_strchr((char *)p_debug, 't') != NULL) {
+ if (!emsg_off || vim_strchr(p_debug, 't') != NULL) {
// Cause a throw of an error exception if appropriate. Don't display
// the error message in this case. (If no matching catch clause will
// be found, the message will be displayed later on.) "ignore" is set
@@ -686,9 +706,9 @@ static bool emsg_multiline(const char *s, bool multiline)
}
// Log (silent) errors as debug messages.
- if (sourcing_name != NULL && sourcing_lnum != 0) {
+ if (SOURCING_NAME != NULL && SOURCING_LNUM != 0) {
DLOG("(:silent) %s (%s (line %ld))",
- s, sourcing_name, (long)sourcing_lnum);
+ s, SOURCING_NAME, (long)SOURCING_LNUM);
} else {
DLOG("(:silent) %s", s);
}
@@ -697,8 +717,8 @@ static bool emsg_multiline(const char *s, bool multiline)
}
// Log editor errors as INFO.
- if (sourcing_name != NULL && sourcing_lnum != 0) {
- ILOG("%s (%s (line %ld))", s, sourcing_name, (long)sourcing_lnum);
+ if (SOURCING_NAME != NULL && SOURCING_LNUM != 0) {
+ ILOG("%s (%s (line %ld))", s, SOURCING_NAME, (long)SOURCING_LNUM);
} else {
ILOG("%s", s);
}
@@ -722,20 +742,18 @@ static bool emsg_multiline(const char *s, bool multiline)
}
emsg_on_display = true; // remember there is an error message
- msg_scroll++; // don't overwrite a previous message
attr = HL_ATTR(HLF_E); // set highlight mode for error messages
if (msg_scrolled != 0) {
need_wait_return = true; // needed in case emsg() is called after
- } // wait_return has reset need_wait_return
+ } // wait_return() has reset need_wait_return
// and a redraw is expected because
// msg_scrolled is non-zero
if (msg_ext_kind == NULL) {
msg_ext_set_kind("emsg");
}
- /*
- * Display name and line number for the source of the error.
- */
+ // Display name and line number for the source of the error.
+ // Sets "msg_scroll".
msg_source(attr);
// Display the error message itself.
@@ -748,7 +766,7 @@ static bool emsg_multiline(const char *s, bool multiline)
/// Rings the bell, if appropriate, and calls message() to do the real work
/// When terminal not initialized (yet) mch_errmsg(..) is used.
///
-/// @return true if wait_return not called
+/// @return true if wait_return() not called
bool emsg(const char *s)
{
return emsg_multiline(s, false);
@@ -812,8 +830,15 @@ static bool semsgv(const char *fmt, va_list ap)
/// detected when fuzzing vim.
void iemsg(const char *s)
{
+ if (emsg_not_now()) {
+ return;
+ }
+
emsg(s);
#ifdef ABORT_ON_INTERNAL_ERROR
+ set_vim_var_string(VV_ERRMSG, s, -1);
+ msg_putchar('\n'); // avoid overwriting the error message
+ ui_flush();
abort();
#endif
}
@@ -823,11 +848,17 @@ void iemsg(const char *s)
/// detected when fuzzing vim.
void siemsg(const char *s, ...)
{
+ if (emsg_not_now()) {
+ return;
+ }
+
va_list ap;
va_start(ap, s);
(void)semsgv(s, ap);
va_end(ap);
#ifdef ABORT_ON_INTERNAL_ERROR
+ msg_putchar('\n'); // avoid overwriting the error message
+ ui_flush();
abort();
#endif
}
@@ -999,7 +1030,7 @@ int delete_first_msg(void)
xfree(p->msg);
hl_msg_free(p->multiattr);
xfree(p);
- --msg_hist_len;
+ msg_hist_len--;
return OK;
}
@@ -1055,7 +1086,7 @@ void ex_messages(void *const eap_p)
HlMessageChunk chunk = kv_A(p->multiattr, i);
Array content_entry = ARRAY_DICT_INIT;
ADD(content_entry, INTEGER_OBJ(chunk.attr));
- ADD(content_entry, STRING_OBJ(copy_string(chunk.text)));
+ ADD(content_entry, STRING_OBJ(copy_string(chunk.text, NULL)));
ADD(content, ARRAY_OBJ(content_entry));
}
} else if (p->msg && p->msg[0]) {
@@ -1100,7 +1131,7 @@ void msg_end_prompt(void)
/// Wait for the user to hit a key (normally Enter)
///
-/// @param redraw if true, redraw the entire screen NOT_VALID
+/// @param redraw if true, redraw the entire screen UPD_NOT_VALID
/// if false, do a normal redraw
/// if -1, don't redraw at all
void wait_return(int redraw)
@@ -1112,7 +1143,7 @@ void wait_return(int redraw)
FILE *save_scriptout;
if (redraw == true) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
// If using ":silent cmd", don't wait for a return. Also don't set
@@ -1146,12 +1177,12 @@ void wait_return(int redraw)
oldState = State;
if (quit_more) {
c = CAR; // just pretend CR was hit
- quit_more = FALSE;
- got_int = FALSE;
+ quit_more = false;
+ got_int = false;
} else if (exmode_active) {
msg_puts(" "); // make sure the cursor is on the right line
c = CAR; // no need for a return in ex mode
- got_int = FALSE;
+ got_int = false;
} else {
// Make sure the hit-return prompt is on screen when 'guioptions' was
// just changed.
@@ -1216,8 +1247,8 @@ void wait_return(int redraw)
}
if (quit_more) {
c = CAR; // just pretend CR was hit
- quit_more = FALSE;
- got_int = FALSE;
+ quit_more = false;
+ got_int = false;
} else if (c != K_IGNORE) {
c = K_IGNORE;
hit_return_msg();
@@ -1276,7 +1307,7 @@ void wait_return(int redraw)
emsg_on_display = false; // can delete error message now
lines_left = -1; // reset lines_left at next msg_start()
reset_last_sourcing();
- if (keep_msg != NULL && vim_strsize((char *)keep_msg) >=
+ if (keep_msg != NULL && vim_strsize(keep_msg) >=
(Rows - cmdline_row - 1) * Columns + sc_col) {
XFREE_CLEAR(keep_msg); // don't redisplay message, it's too long
}
@@ -1285,7 +1316,7 @@ void wait_return(int redraw)
ui_refresh();
} else if (!skip_redraw) {
if (redraw == true || (msg_scrolled != 0 && redraw != -1)) {
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
if (ui_has(kUIMessages)) {
msg_ext_clear(true);
@@ -1319,7 +1350,7 @@ void set_keep_msg(char *s, int attr)
{
xfree(keep_msg);
if (s != NULL && msg_silent == 0) {
- keep_msg = vim_strsave((char_u *)s);
+ keep_msg = xstrdup(s);
} else {
keep_msg = NULL;
}
@@ -1479,7 +1510,7 @@ void msg_home_replace_hl(char_u *fname)
static void msg_home_replace_attr(char_u *fname, int attr)
{
char *name = home_replace_save(NULL, (char *)fname);
- msg_outtrans_attr((char_u *)name, attr);
+ msg_outtrans_attr(name, attr);
xfree(name);
}
@@ -1490,29 +1521,29 @@ static void msg_home_replace_attr(char_u *fname, int attr)
/// @return the number of characters it takes on the screen.
int msg_outtrans(char *str)
{
- return msg_outtrans_attr((char_u *)str, 0);
+ return msg_outtrans_attr(str, 0);
}
-int msg_outtrans_attr(const char_u *str, int attr)
+int msg_outtrans_attr(const char *str, int attr)
{
- return msg_outtrans_len_attr(str, (int)STRLEN(str), attr);
+ return msg_outtrans_len_attr((char_u *)str, (int)STRLEN(str), attr);
}
-int msg_outtrans_len(const char_u *str, int len)
+int msg_outtrans_len(const char *str, int len)
{
- return msg_outtrans_len_attr(str, len, 0);
+ return msg_outtrans_len_attr((char_u *)str, len, 0);
}
/// Output one character at "p".
/// Handles multi-byte characters.
///
/// @return pointer to the next character.
-char_u *msg_outtrans_one(char_u *p, int attr)
+char *msg_outtrans_one(char *p, int attr)
{
int l;
- if ((l = utfc_ptr2len((char *)p)) > 1) {
- msg_outtrans_len_attr(p, l, attr);
+ if ((l = utfc_ptr2len(p)) > 1) {
+ msg_outtrans_len_attr((char_u *)p, l, attr);
return p + l;
}
msg_puts_attr((const char *)transchar_byte(*p), attr);
@@ -1597,12 +1628,13 @@ int msg_outtrans_len_attr(const char_u *msgstr, int len, int attr)
return retval;
}
-void msg_make(char_u *arg)
+void msg_make(char *arg)
{
int i;
- static char_u *str = (char_u *)"eeffoc", *rs = (char_u *)"Plon#dqg#vxjduB";
+ static char *str = "eeffoc";
+ static char *rs = "Plon#dqg#vxjduB";
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
for (i = 5; *arg && i >= 0; i--) {
if (*arg++ != str[i]) {
break;
@@ -1610,7 +1642,7 @@ void msg_make(char_u *arg)
}
if (i < 0) {
msg_putchar('\n');
- for (i = 0; rs[i]; ++i) {
+ for (i = 0; rs[i]; i++) {
msg_putchar(rs[i] - 3);
}
}
@@ -1622,7 +1654,7 @@ void msg_make(char_u *arg)
/// If K_SPECIAL is encountered, then it is taken in conjunction with the
/// following character and shown as <F1>, <S-Up> etc. Any other character
/// which is not printable shown in <> form.
-/// If 'from' is TRUE (lhs of a mapping), a space is shown as <Space>.
+/// If 'from' is true (lhs of a mapping), a space is shown as <Space>.
/// If a character is displayed in one of these special ways, is also
/// highlighted (its highlight name is '8' in the p_hl variable).
/// Otherwise characters are not highlighted.
@@ -1945,13 +1977,13 @@ void msg_prt_line(char_u *s, int list)
/// Use grid_puts() to output one multi-byte character.
///
/// @return the pointer "s" advanced to the next character.
-static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
+static char *screen_puts_mbyte(char *s, int l, int attr)
{
int cw;
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
msg_didout = true; // remember that line is not empty
- cw = utf_ptr2cells((char *)s);
+ cw = utf_ptr2cells(s);
if (cw > 1
&& (cmdmsg_rl ? msg_col <= 1 : msg_col == Columns - 1)) {
// Doesn't fit, print a highlighted '>' to fill it up.
@@ -1964,13 +1996,13 @@ static char_u *screen_puts_mbyte(char_u *s, int l, int attr)
msg_col -= cw;
if (msg_col == 0) {
msg_col = Columns;
- ++msg_row;
+ msg_row++;
}
} else {
msg_col += cw;
if (msg_col >= Columns) {
msg_col = 0;
- ++msg_row;
+ msg_row++;
}
}
return s + l;
@@ -2123,7 +2155,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
int t_col = 0; // Screen cells todo, 0 when "t_s" not used.
int l;
int cw;
- const char_u *sb_str = str;
+ const char *sb_str = (char *)str;
int sb_col = msg_col;
int wrap;
int did_last_char;
@@ -2166,7 +2198,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
// ourselves).
if (t_col > 0) {
// output postponed text
- t_puts(&t_col, t_s, s, attr);
+ t_puts(&t_col, (char *)t_s, (char *)s, attr);
}
// When no more prompt and no more room, truncate here
@@ -2191,7 +2223,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
} else {
l = utfc_ptr2len((char *)s);
}
- s = screen_puts_mbyte((char_u *)s, l, attr);
+ s = (char_u *)screen_puts_mbyte((char *)s, l, attr);
did_last_char = true;
} else {
did_last_char = false;
@@ -2208,11 +2240,11 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
if (p_more) {
// Store text for scrolling back.
- store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, true);
+ store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, true);
}
inc_msg_scrolled();
- need_wait_return = true; // may need wait_return in main()
+ need_wait_return = true; // may need wait_return() in main()
redraw_cmdline = true;
if (cmdline_row > 0 && !exmode_active) {
cmdline_row--;
@@ -2223,7 +2255,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
* for a character.
*/
if (lines_left > 0) {
- --lines_left;
+ lines_left--;
}
if (p_more && lines_left == 0 && State != MODE_HITRETURN
&& !msg_no_more && !exmode_active) {
@@ -2250,12 +2282,12 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
if (t_col > 0 && (wrap || *s == '\r' || *s == '\b'
|| *s == '\t' || *s == BELL)) {
// Output any postponed text.
- t_puts(&t_col, t_s, s, attr);
+ t_puts(&t_col, (char *)t_s, (char *)s, attr);
}
if (wrap && p_more && !recurse) {
// Store text for scrolling back.
- store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, true);
+ store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, true);
}
if (*s == '\n') { // go to next line
@@ -2272,7 +2304,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
msg_col = 0;
} else if (*s == '\b') { // go to previous char
if (msg_col) {
- --msg_col;
+ msg_col--;
}
} else if (*s == TAB) { // translate Tab into spaces
do {
@@ -2293,7 +2325,7 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
// characters and draw them all at once later.
if (cmdmsg_rl || (cw > 1 && msg_col + t_col >= Columns - 1)) {
if (l > 1) {
- s = screen_puts_mbyte((char_u *)s, l, attr) - 1;
+ s = (char_u *)screen_puts_mbyte((char *)s, l, attr) - 1;
} else {
msg_screen_putchar(*s, attr);
}
@@ -2306,15 +2338,15 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
s += l - 1;
}
}
- ++s;
+ s++;
}
// Output any postponed text.
if (t_col > 0) {
- t_puts(&t_col, t_s, s, attr);
+ t_puts(&t_col, (char *)t_s, (char *)s, attr);
}
if (p_more && !recurse) {
- store_sb_text((char_u **)&sb_str, (char_u *)s, attr, &sb_col, false);
+ store_sb_text((char **)&sb_str, (char *)s, attr, &sb_col, false);
}
msg_check();
@@ -2322,13 +2354,13 @@ static void msg_puts_display(const char_u *str, int maxlen, int attr, int recurs
/// @return true when ":filter pattern" was used and "msg" does not match
/// "pattern".
-bool message_filtered(char_u *msg)
+bool message_filtered(char *msg)
{
if (cmdmod.cmod_filter_regmatch.regprog == NULL) {
return false;
}
- bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, (char *)msg, (colnr_T)0);
+ bool match = vim_regexec(&cmdmod.cmod_filter_regmatch, msg, (colnr_T)0);
return cmdmod.cmod_filter_force ? match : !match;
}
@@ -2447,7 +2479,7 @@ void msg_reset_scroll(void)
}
}
} else {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
msg_scrolled = 0;
msg_scrolled_at_flush = 0;
@@ -2457,7 +2489,7 @@ void msg_reset_scroll(void)
static void inc_msg_scrolled(void)
{
if (*get_vim_var_str(VV_SCROLLSTART) == NUL) {
- char *p = sourcing_name;
+ char *p = SOURCING_NAME;
char *tofree = NULL;
// v:scrollstart is empty, set it to the script/function name and line
@@ -2468,15 +2500,15 @@ static void inc_msg_scrolled(void)
size_t len = strlen(p) + 40;
tofree = xmalloc(len);
vim_snprintf(tofree, len, _("%s line %" PRId64),
- p, (int64_t)sourcing_lnum);
+ p, (int64_t)SOURCING_LNUM);
p = tofree;
}
set_vim_var_string(VV_SCROLLSTART, p, -1);
xfree(tofree);
}
msg_scrolled++;
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
}
@@ -2497,7 +2529,7 @@ static sb_clear_T do_clear_sb_text = SB_CLEAR_NONE;
/// @param sb_str start of string
/// @param s just after string
/// @param finish line ends
-static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int finish)
+static void store_sb_text(char **sb_str, char *s, int attr, int *sb_col, int finish)
{
msgchunk_T *mp;
@@ -2526,7 +2558,7 @@ static void store_sb_text(char_u **sb_str, char_u *s, int attr, int *sb_col, int
}
mp->sb_next = NULL;
} else if (finish && last_msgchunk != NULL) {
- last_msgchunk->sb_eol = TRUE;
+ last_msgchunk->sb_eol = true;
}
*sb_str = s;
@@ -2583,7 +2615,7 @@ void sb_text_end_cmdline(void)
}
/// Clear any text remembered for scrolling back.
-/// When "all" is FALSE keep the last line.
+/// When "all" is false keep the last line.
/// Called when redrawing the screen.
void clear_sb_text(int all)
{
@@ -2618,7 +2650,7 @@ void show_sb_text(void)
vim_beep(BO_MESS);
} else {
do_more_prompt('G');
- wait_return(FALSE);
+ wait_return(false);
}
}
@@ -2637,7 +2669,7 @@ static msgchunk_T *msg_sb_start(msgchunk_T *mps)
void msg_sb_eol(void)
{
if (last_msgchunk != NULL) {
- last_msgchunk->sb_eol = TRUE;
+ last_msgchunk->sb_eol = true;
}
}
@@ -2654,9 +2686,9 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
msg_col = mp->sb_msg_col;
p = mp->sb_text;
if (*p == '\n') { // don't display the line break
- ++p;
+ p++;
}
- msg_puts_display(p, -1, mp->sb_attr, TRUE);
+ msg_puts_display(p, -1, mp->sb_attr, true);
if (mp->sb_eol || mp->sb_next == NULL) {
break;
}
@@ -2667,27 +2699,26 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
}
/// Output any postponed text for msg_puts_attr_len().
-static void t_puts(int *t_col, const char_u *t_s, const char_u *s, int attr)
+static void t_puts(int *t_col, const char *t_s, const char *s, int attr)
{
attr = hl_combine_attr(HL_ATTR(HLF_MSG), attr);
// Output postponed text.
msg_didout = true; // Remember that line is not empty.
- grid_puts_len(&msg_grid_adj, (char_u *)t_s, (int)(s - t_s), msg_row, msg_col,
- attr);
+ grid_puts_len(&msg_grid_adj, (char *)t_s, (int)(s - t_s), msg_row, msg_col, attr);
msg_col += *t_col;
*t_col = 0;
// If the string starts with a composing character don't increment the
// column position for it.
- if (utf_iscomposing(utf_ptr2char((char *)t_s))) {
+ if (utf_iscomposing(utf_ptr2char(t_s))) {
msg_col--;
}
if (msg_col >= Columns) {
msg_col = 0;
- ++msg_row;
+ msg_row++;
}
}
-/// @return TRUE when messages should be printed to stdout/stderr:
+/// @return true when messages should be printed to stdout/stderr:
/// - "batch mode" ("silent mode", -es/-Es)
/// - no UI and not embedded
int msg_use_printf(void)
@@ -2755,14 +2786,14 @@ static void msg_puts_printf(const char *str, const ptrdiff_t maxlen)
/// When at hit-enter prompt "typed_char" is the already typed character,
/// otherwise it's NUL.
///
-/// @return TRUE when jumping ahead to "confirm_msg_tail".
+/// @return true when jumping ahead to "confirm_msg_tail".
static int do_more_prompt(int typed_char)
{
static bool entered = false;
int used_typed_char = typed_char;
int oldState = State;
int c;
- int retval = FALSE;
+ int retval = false;
int toscroll;
bool to_redraw = false;
msgchunk_T *mp_last = NULL;
@@ -2786,7 +2817,7 @@ static int do_more_prompt(int typed_char)
// "g<": Find first line on the last page.
mp_last = msg_sb_start(last_msgchunk);
for (i = 0; i < Rows - 2 && mp_last != NULL
- && mp_last->sb_prev != NULL; ++i) {
+ && mp_last->sb_prev != NULL; i++) {
mp_last = msg_sb_start(mp_last->sb_prev);
}
}
@@ -2794,7 +2825,7 @@ static int do_more_prompt(int typed_char)
State = MODE_ASKMORE;
setmouse();
if (typed_char == NUL) {
- msg_moremsg(FALSE);
+ msg_moremsg(false);
}
for (;;) {
/*
@@ -2867,10 +2898,10 @@ static int do_more_prompt(int typed_char)
case ESC:
if (confirm_msg_used) {
// Jump to the choices of the dialog.
- retval = TRUE;
+ retval = true;
} else {
- got_int = TRUE;
- quit_more = TRUE;
+ got_int = true;
+ quit_more = true;
}
// When there is some more output (wrapping line) display that
// without another prompt.
@@ -2886,7 +2917,7 @@ static int do_more_prompt(int typed_char)
break;
default: // no valid response
- msg_moremsg(TRUE);
+ msg_moremsg(true);
continue;
}
@@ -2905,8 +2936,7 @@ static int do_more_prompt(int typed_char)
}
// go to start of line at top of the screen
- for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL;
- ++i) {
+ for (i = 0; i < Rows - 2 && mp != NULL && mp->sb_prev != NULL; i++) {
mp = msg_sb_start(mp->sb_prev);
}
@@ -2938,7 +2968,7 @@ static int do_more_prompt(int typed_char)
HL_ATTR(HLF_MSG));
for (i = 0; mp != NULL && i < Rows - 1; i++) {
mp = disp_sb_line(i, mp);
- ++msg_scrolled;
+ msg_scrolled++;
}
to_redraw = false;
}
@@ -3037,12 +3067,12 @@ static void msg_screen_putchar(int c, int attr)
if (cmdmsg_rl) {
if (--msg_col == 0) {
msg_col = Columns;
- ++msg_row;
+ msg_row++;
}
} else {
if (++msg_col >= Columns) {
msg_col = 0;
- ++msg_row;
+ msg_row++;
}
}
}
@@ -3053,10 +3083,9 @@ void msg_moremsg(int full)
char_u *s = (char_u *)_("-- More --");
attr = hl_combine_attr(HL_ATTR(HLF_MSG), HL_ATTR(HLF_M));
- grid_puts(&msg_grid_adj, s, Rows - 1, 0, attr);
+ grid_puts(&msg_grid_adj, (char *)s, Rows - 1, 0, attr);
if (full) {
- grid_puts(&msg_grid_adj, (char_u *)
- _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
+ grid_puts(&msg_grid_adj, _(" SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "),
Rows - 1, vim_strsize((char *)s), attr);
}
}
@@ -3136,9 +3165,9 @@ void msg_clr_cmdline(void)
}
/// end putting a message on the screen
-/// call wait_return if the message does not fit in the available space
+/// call wait_return() if the message does not fit in the available space
///
-/// @return TRUE if wait_return not called.
+/// @return true if wait_return() not called.
int msg_end(void)
{
/*
@@ -3214,8 +3243,8 @@ void msg_ext_clear_later(void)
{
if (msg_ext_is_visible()) {
msg_ext_need_clear = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
+ if (must_redraw < UPD_VALID) {
+ must_redraw = UPD_VALID;
}
}
}
@@ -3339,7 +3368,7 @@ int redirecting(void)
void verbose_enter(void)
{
if (*p_vfile != NUL) {
- ++msg_silent;
+ msg_silent++;
}
}
@@ -3358,10 +3387,10 @@ void verbose_leave(void)
void verbose_enter_scroll(void)
{
if (*p_vfile != NUL) {
- ++msg_silent;
+ msg_silent++;
} else {
// always scroll up, don't overwrite
- msg_scroll = TRUE;
+ msg_scroll = true;
}
}
@@ -3384,7 +3413,7 @@ void verbose_stop(void)
fclose(verbose_fd);
verbose_fd = NULL;
}
- verbose_did_open = FALSE;
+ verbose_did_open = false;
}
/// Open the file 'verbosefile'.
@@ -3394,9 +3423,9 @@ int verbose_open(void)
{
if (verbose_fd == NULL && !verbose_did_open) {
// Only give the error message once.
- verbose_did_open = TRUE;
+ verbose_did_open = true;
- verbose_fd = os_fopen((char *)p_vfile, "a");
+ verbose_fd = os_fopen(p_vfile, "a");
if (verbose_fd == NULL) {
semsg(_(e_notopen), p_vfile);
return FAIL;
@@ -3493,7 +3522,7 @@ void msg_advance(int col)
/// different letter.
///
/// @param textfiel IObuff for inputdialog(), NULL otherwise
-/// @param ex_cmd when TRUE pressing : accepts default and starts Ex command
+/// @param ex_cmd when true pressing : accepts default and starts Ex command
/// @returns 0 if cancelled, otherwise the nth button (1-indexed).
int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton,
char_u *textfield, int ex_cmd)
@@ -3520,7 +3549,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
* Since we wait for a keypress, don't make the
* user press RETURN as well afterwards.
*/
- ++no_wait_return;
+ no_wait_return++;
hotkeys = msg_show_console_dialog(message, buttons, dfltbutton);
for (;;) {
@@ -3569,7 +3598,7 @@ int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfl
msg_silent = save_msg_silent;
State = oldState;
setmouse();
- --no_wait_return;
+ no_wait_return--;
msg_end_prompt();
return retval;
@@ -3723,7 +3752,7 @@ static void copy_hotkeys_and_msg(const char_u *message, char_u *buttons, int def
}
} else if (*r == DLG_HOTKEY_CHAR || first_hotkey) {
if (*r == DLG_HOTKEY_CHAR) {
- ++r;
+ r++;
}
first_hotkey = false;
@@ -3769,7 +3798,7 @@ int vim_dialog_yesno(int type, char_u *title, char_u *message, int dflt)
if (do_dialog(type,
title == NULL ? (char_u *)_("Question") : title,
message,
- (char_u *)_("&Yes\n&No"), dflt, NULL, FALSE) == 1) {
+ (char_u *)_("&Yes\n&No"), dflt, NULL, false) == 1) {
return VIM_YES;
}
return VIM_NO;
@@ -3780,7 +3809,7 @@ int vim_dialog_yesnocancel(int type, char_u *title, char_u *message, int dflt)
switch (do_dialog(type,
title == NULL ? (char_u *)_("Question") : title,
message,
- (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL, FALSE)) {
+ (char_u *)_("&Yes\n&No\n&Cancel"), dflt, NULL, false)) {
case 1:
return VIM_YES;
case 2:
@@ -3795,7 +3824,7 @@ int vim_dialog_yesnoallcancel(int type, char_u *title, char_u *message, int dflt
title == NULL ? (char_u *)"Question" : title,
message,
(char_u *)_("&Yes\n&No\nSave &All\n&Discard All\n&Cancel"),
- dflt, NULL, FALSE)) {
+ dflt, NULL, false)) {
case 1:
return VIM_YES;
case 2:
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index a4a521fa80..73147204a1 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -8,13 +8,14 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/fold.h"
+#include "nvim/grid.h"
#include "nvim/memline.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/os_unix.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -178,7 +179,7 @@ retnomove:
}
if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion
}
return IN_BUFFER;
}
@@ -278,7 +279,7 @@ retnomove:
: col >= fdc + (cmdwin_type == 0 && wp == curwin ? 0 : 1))
&& (flags & MOUSE_MAY_STOP_VIS)))) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion
}
if (cmdwin_type != 0 && wp != curwin) {
// A click outside the command-line window: Use modeless
@@ -344,7 +345,7 @@ retnomove:
// before moving the cursor for a left click, stop Visual mode
if (flags & MOUSE_MAY_STOP_VIS) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion
}
if (grid == 0) {
@@ -373,20 +374,20 @@ retnomove:
if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) {
curwin->w_topfill++;
} else {
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
}
}
check_topfill(curwin, false);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
row = 0;
} else if (row >= curwin->w_height_inner) {
count = 0;
for (first = true; curwin->w_topline < curbuf->b_ml.ml_line_count;) {
if (curwin->w_topfill > 0) {
- ++count;
+ count++;
} else {
count += plines_win(curwin, curwin->w_topline, true);
}
@@ -409,7 +410,7 @@ retnomove:
}
}
check_topfill(curwin, false);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
curwin->w_valid &=
~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP);
row = curwin->w_height_inner - 1;
@@ -514,7 +515,7 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump)
break; // past end of file
}
row -= count;
- ++lnum;
+ lnum++;
}
if (!retval) {
@@ -630,14 +631,16 @@ colnr_T vcol2col(win_T *const wp, const linenr_T lnum, const colnr_T vcol)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
// try to advance to the specified column
- char_u *ptr = ml_get_buf(wp->w_buffer, lnum, false);
- char_u *const line = ptr;
- colnr_T count = 0;
- while (count < vcol && *ptr != NUL) {
- count += win_lbr_chartabsize(wp, line, ptr, count, NULL);
- MB_PTR_ADV(ptr);
- }
- return (colnr_T)(ptr - line);
+ char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) {
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ MB_PTR_ADV(cts.cts_ptr);
+ }
+ clear_chartabsize_arg(&cts);
+
+ return (colnr_T)((char_u *)cts.cts_ptr - line);
}
/// Set UI mouse depending on current mode and 'mouse'.
@@ -666,7 +669,7 @@ static colnr_T scroll_line_len(linenr_T lnum)
char_u *line = ml_get(lnum);
if (*line != NUL) {
for (;;) {
- int numchar = win_chartabsize(curwin, line, col);
+ int numchar = win_chartabsize(curwin, (char *)line, col);
MB_PTR_ADV(line);
if (*line == NUL) { // don't count the last character
break;
@@ -701,8 +704,8 @@ static linenr_T find_longest_lnum(void)
max = len;
ret = lnum;
} else if (len == (colnr_T)max
- && abs((int)(lnum - curwin->w_cursor.lnum))
- < abs((int)(ret - curwin->w_cursor.lnum))) {
+ && abs(lnum - curwin->w_cursor.lnum)
+ < abs(ret - curwin->w_cursor.lnum)) {
ret = lnum;
}
}
@@ -789,7 +792,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// checked for concealed characters.
vcol = 0;
while (vcol < offset && *ptr != NUL) {
- vcol += win_chartabsize(curwin, ptr, vcol);
+ vcol += win_chartabsize(curwin, (char *)ptr, vcol);
ptr += utfc_ptr2len((char *)ptr);
}
@@ -800,7 +803,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
vcol = offset;
ptr_end = ptr_row_offset;
while (vcol < col && *ptr_end != NUL) {
- vcol += win_chartabsize(curwin, ptr_end, vcol);
+ vcol += win_chartabsize(curwin, (char *)ptr_end, vcol);
ptr_end += utfc_ptr2len((char *)ptr_end);
}
@@ -815,7 +818,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
#define DECR() nudge--; ptr_end -= utfc_ptr2len((char *)ptr_end)
while (ptr < ptr_end && *ptr != NUL) {
- cwidth = win_chartabsize(curwin, ptr, vcol);
+ cwidth = win_chartabsize(curwin, (char *)ptr, vcol);
vcol += cwidth;
if (cwidth > 1 && *ptr == '\t' && nudge > 0) {
// A tab will "absorb" any previous adjustments.
diff --git a/src/nvim/mouse.h b/src/nvim/mouse.h
index 08261e4a30..21ff56bbbc 100644
--- a/src/nvim/mouse.h
+++ b/src/nvim/mouse.h
@@ -36,7 +36,7 @@
#define MOUSE_X2 0x400 // Mouse-button X2
// Direction for nv_mousescroll() and ins_mousescroll()
-#define MSCR_DOWN 0 // DOWN must be FALSE
+#define MSCR_DOWN 0 // DOWN must be false
#define MSCR_UP 1
#define MSCR_LEFT (-1)
#define MSCR_RIGHT (-2)
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 6d4eb8ef49..b3ec3a8e7a 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -21,16 +21,18 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/window.h"
@@ -103,7 +105,7 @@ void redraw_for_cursorline(win_T *wp)
if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible()
&& (wp->w_p_rnu || win_cursorline_standout(wp))) {
// win_line() will redraw the number column and cursorline only.
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
}
@@ -114,13 +116,13 @@ static void redraw_for_cursorcolumn(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) {
- if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC]) && using_hlsearch())) {
+ if (wp->w_p_cuc || ((HL_ATTR(HLF_LC) || win_hl_attr(wp, HLF_LC)) && using_hlsearch())) {
// When 'cursorcolumn' is set or "CurSearch" is in use
- // need to redraw with SOME_VALID.
- redraw_later(wp, SOME_VALID);
+ // need to redraw with UPD_SOME_VALID.
+ redraw_later(wp, UPD_SOME_VALID);
} else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) {
- // When 'cursorlineopt' contains "screenline" need to redraw with VALID.
- redraw_later(wp, VALID);
+ // When 'cursorlineopt' contains "screenline" need to redraw with UPD_VALID.
+ redraw_later(wp, UPD_VALID);
}
}
// If the cursor moves horizontally when 'concealcursor' is active, then the
@@ -182,7 +184,7 @@ void update_topline(win_T *wp)
// If the buffer is empty, always set topline to 1.
if (buf_is_empty(curbuf)) { // special case - file is empty
if (wp->w_topline != 1) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
wp->w_topline = 1;
wp->w_botline = 2;
@@ -334,9 +336,9 @@ void update_topline(win_T *wp)
dollar_vcol = -1;
if (wp->w_skipcol != 0) {
wp->w_skipcol = 0;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
} else {
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
// May need to set w_skipcol when cursor in w_topline.
if (wp->w_cursor.lnum == wp->w_topline) {
@@ -448,7 +450,7 @@ void changed_window_setting_win(win_T *wp)
wp->w_lines_valid = 0;
changed_line_abv_curs_win(wp);
wp->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP|VALID_TOPLINE);
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
/*
@@ -470,7 +472,7 @@ void set_topline(win_T *wp, linenr_T lnum)
}
wp->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_TOPLINE);
// Don't set VALID_TOPLINE here, 'scrolloff' needs to be checked.
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
/*
@@ -569,7 +571,7 @@ static void curs_rows(win_T *wp)
|| wp->w_lines[0].wl_lnum > wp->w_topline);
int i = 0;
wp->w_cline_row = 0;
- for (linenr_T lnum = wp->w_topline; lnum < wp->w_cursor.lnum; ++i) {
+ for (linenr_T lnum = wp->w_topline; lnum < wp->w_cursor.lnum; i++) {
bool valid = false;
if (!all_invalid && i < wp->w_lines_valid) {
if (wp->w_lines[i].wl_lnum < lnum || !wp->w_lines[i].wl_valid) {
@@ -585,7 +587,7 @@ static void curs_rows(win_T *wp)
valid = true;
}
} else if (wp->w_lines[i].wl_lnum > lnum) {
- --i; // hold at inserted lines
+ i--; // hold at inserted lines
}
}
if (valid && (lnum != wp->w_topline || !win_may_fill(wp))) {
@@ -845,7 +847,7 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_leftcol = new_leftcol;
win_check_anchored_floats(wp);
// screen has to be redrawn with new wp->w_leftcol
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
wp->w_wcol -= wp->w_leftcol;
@@ -952,7 +954,7 @@ void curs_columns(win_T *wp, int may_scroll)
wp->w_skipcol = 0;
}
if (prev_skipcol != wp->w_skipcol) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
redraw_for_cursorcolumn(curwin);
@@ -1051,12 +1053,12 @@ bool scrolldown(long line_count, int byfold)
if (curwin->w_topline == 1) {
break;
}
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
// A sequence of folded lines only counts for one logical line
linenr_T first;
if (hasFolding(curwin->w_topline, &first, NULL)) {
- ++done;
+ done++;
if (!byfold) {
line_count -= curwin->w_topline - first - 1;
}
@@ -1066,7 +1068,7 @@ bool scrolldown(long line_count, int byfold)
done += plines_win_nofill(curwin, curwin->w_topline, true);
}
}
- --curwin->w_botline; // approximate w_botline
+ curwin->w_botline--; // approximate w_botline
invalidate_botline();
}
curwin->w_wrow += done; // keep w_wrow updated
@@ -1092,7 +1094,7 @@ bool scrolldown(long line_count, int byfold)
while (wrow >= curwin->w_height_inner && curwin->w_cursor.lnum > 1) {
linenr_T first;
if (hasFolding(curwin->w_cursor.lnum, &first, NULL)) {
- --wrow;
+ wrow--;
if (first == 1) {
curwin->w_cursor.lnum = 1;
} else {
@@ -1128,7 +1130,7 @@ bool scrollup(long line_count, int byfold)
linenr_T lnum = curwin->w_topline;
while (line_count--) {
if (curwin->w_topfill > 0) {
- --curwin->w_topfill;
+ curwin->w_topfill--;
} else {
if (byfold) {
(void)hasFolding(lnum, NULL, &lnum);
@@ -1185,7 +1187,7 @@ void check_topfill(win_T *wp, bool down)
int n = plines_win_nofill(wp, wp->w_topline, true);
if (wp->w_topfill + n > wp->w_height_inner) {
if (down && wp->w_topline > 1) {
- --wp->w_topline;
+ wp->w_topline--;
wp->w_topfill = 0;
} else {
wp->w_topfill = wp->w_height_inner - n;
@@ -1247,14 +1249,14 @@ void scrolldown_clamp(void)
}
if (end_row < curwin->w_height_inner - get_scrolloff_value(curwin)) {
if (can_fill) {
- ++curwin->w_topfill;
+ curwin->w_topfill++;
check_topfill(curwin, true);
} else {
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
}
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
- --curwin->w_botline; // approximate w_botline
+ curwin->w_botline--; // approximate w_botline
curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE);
}
}
@@ -1307,7 +1309,7 @@ static void topline_back(win_T *wp, lineoff_T *lp)
lp->fill++;
lp->height = 1;
} else {
- --lp->lnum;
+ lp->lnum--;
lp->fill = 0;
if (lp->lnum < 1) {
lp->height = MAXCOL;
@@ -1333,7 +1335,7 @@ static void botline_forw(win_T *wp, lineoff_T *lp)
lp->fill++;
lp->height = 1;
} else {
- ++lp->lnum;
+ lp->lnum++;
lp->fill = 0;
assert(wp->w_buffer != 0);
if (lp->lnum > wp->w_buffer->b_ml.ml_line_count) {
@@ -1406,8 +1408,8 @@ void scroll_cursor_top(int min_scroll, int always)
}
if (hasFolding(curwin->w_cursor.lnum, &top, &bot)) {
- --top;
- ++bot;
+ top--;
+ bot++;
} else {
top = curwin->w_cursor.lnum - 1;
bot = curwin->w_cursor.lnum + 1;
@@ -1453,8 +1455,8 @@ void scroll_cursor_top(int min_scroll, int always)
extra += i;
new_topline = top;
- --top;
- ++bot;
+ top--;
+ bot++;
}
/*
@@ -1664,7 +1666,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
for (i = 0; i < scrolled && boff.lnum < curwin->w_botline;) {
botline_forw(curwin, &boff);
i += boff.height;
- ++line_count;
+ line_count++;
}
if (i < scrolled) { // below curwin->w_botline, don't scroll
line_count = 9999;
@@ -1724,9 +1726,9 @@ void scroll_cursor_halfway(int atend)
}
below += boff.height;
} else {
- ++below; // count a "~" line
+ below++; // count a "~" line
if (atend) {
- ++used;
+ used++;
}
}
}
@@ -1835,7 +1837,7 @@ void cursor_correct(void)
if (topline < botline) {
above += win_get_fill(curwin, topline + 1);
}
- ++topline;
+ topline++;
}
}
if (topline == botline || botline == 0) {
@@ -1897,7 +1899,7 @@ int onepage(Direction dir, long count)
if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
// Vi compatible scrolling
if (p_window <= 2) {
- ++curwin->w_topline;
+ curwin->w_topline++;
} else {
curwin->w_topline += (linenr_T)p_window - 2;
}
@@ -1933,7 +1935,7 @@ int onepage(Direction dir, long count)
if (ONE_WINDOW && p_window > 0 && p_window < Rows - 1) {
// Vi compatible scrolling (sort of)
if (p_window <= 2) {
- --curwin->w_topline;
+ curwin->w_topline--;
} else {
curwin->w_topline -= (linenr_T)p_window - 2;
}
@@ -1998,7 +2000,7 @@ int onepage(Direction dir, long count)
max_topfill();
}
if (curwin->w_topfill == loff.fill) {
- --curwin->w_topline;
+ curwin->w_topline--;
curwin->w_topfill = 0;
}
comp_botline(curwin);
@@ -2038,7 +2040,7 @@ int onepage(Direction dir, long count)
}
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
return retval;
}
@@ -2142,7 +2144,7 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_topfill = win_get_fill(curwin, curwin->w_topline);
if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
curwin->w_valid &=
~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
@@ -2175,7 +2177,7 @@ void halfpage(bool flag, linenr_T Prenum)
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
(void)hasFolding(curwin->w_cursor.lnum, NULL,
&curwin->w_cursor.lnum);
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
} else {
curwin->w_cursor.lnum += n;
@@ -2197,7 +2199,7 @@ void halfpage(bool flag, linenr_T Prenum)
if (n < 0 && scrolled > 0) {
break;
}
- --curwin->w_topline;
+ curwin->w_topline--;
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
curwin->w_topfill = 0;
}
@@ -2205,7 +2207,7 @@ void halfpage(bool flag, linenr_T Prenum)
VALID_BOTLINE|VALID_BOTLINE_AP);
scrolled += i;
if (curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
curwin->w_valid &= ~(VALID_VIRTCOL|VALID_CHEIGHT|VALID_WCOL);
}
}
@@ -2216,7 +2218,7 @@ void halfpage(bool flag, linenr_T Prenum)
curwin->w_cursor.lnum = 1;
} else if (hasAnyFolding(curwin)) {
while (--n >= 0 && curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
(void)hasFolding(curwin->w_cursor.lnum,
&curwin->w_cursor.lnum, NULL);
}
@@ -2230,7 +2232,7 @@ void halfpage(bool flag, linenr_T Prenum)
check_topfill(curwin, !flag);
cursor_correct();
beginline(BL_SOL | BL_FIX);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
void do_check_cursorbind(void)
@@ -2282,7 +2284,7 @@ void do_check_cursorbind(void)
}
// Correct cursor for multi-byte character.
mb_adjust_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
// Only scroll when 'scrollbind' hasn't done this.
if (!curwin->w_p_scb) {
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 1ca68979f4..d22bcb29d5 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -158,7 +158,7 @@ Object rpc_send_call(uint64_t id, const char *method_name, Array args, ArenaMem
}
// frame.result was allocated in an arena
- arena_mem_free(frame.result_mem, &rpc->unpacker->reuse_blk);
+ arena_mem_free(frame.result_mem);
frame.result_mem = NULL;
}
@@ -244,7 +244,7 @@ static void parse_msgpack(Channel *channel)
ui_client_event_raw_line(p->grid_line_event);
} else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {
p->ui_handler.fn(p->result.data.array);
- arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
+ arena_mem_free(arena_finish(&p->arena));
}
} else if (p->type == kMessageTypeResponse) {
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
@@ -295,7 +295,7 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
if (!p->handler.fn) {
send_error(channel, p->type, p->request_id, p->unpack_error.msg);
api_clear_error(&p->unpack_error);
- arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
+ arena_mem_free(arena_finish(&p->arena));
return;
}
@@ -304,7 +304,8 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
evdata->channel = channel;
evdata->handler = p->handler;
evdata->args = args;
- evdata->used_mem = arena_finish(&p->arena);
+ evdata->used_mem = p->arena;
+ p->arena = (Arena)ARENA_EMPTY;
evdata->request_id = p->request_id;
channel_incref(channel);
if (p->handler.fast) {
@@ -344,7 +345,8 @@ static void request_event(void **argv)
// channel was closed, abort any pending requests
goto free_ret;
}
- Object result = handler.fn(channel->id, e->args, &error);
+
+ Object result = handler.fn(channel->id, e->args, &e->used_mem, &error);
if (e->type == kMessageTypeRequest || ERROR_SET(&error)) {
// Send the response.
msgpack_packer response;
@@ -355,13 +357,14 @@ static void request_event(void **argv)
&error,
result,
&out_buffer));
- } else {
+ }
+ if (!handler.arena_return) {
api_free_object(result);
}
free_ret:
- // e->args is allocated in an arena
- arena_mem_free(e->used_mem, &channel->rpc.unpacker->reuse_blk);
+ // e->args (and possibly result) are allocated in an arena
+ arena_mem_free(arena_finish(&e->used_mem));
channel_decref(channel);
xfree(e);
api_clear_error(&error);
@@ -624,7 +627,6 @@ static WBuffer *serialize_response(uint64_t channel_id, MessageType type, uint32
1, // responses only go though 1 channel
xfree);
msgpack_sbuffer_clear(sbuffer);
- api_free_object(arg);
return rv;
}
@@ -642,7 +644,7 @@ void rpc_set_client_info(uint64_t id, Dictionary info)
Dictionary rpc_client_info(Channel *chan)
{
- return copy_dictionary(chan->rpc.info);
+ return copy_dictionary(chan->rpc.info, NULL);
}
const char *rpc_client_name(Channel *chan)
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index e622ebddf5..404e68329a 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -27,7 +27,7 @@ typedef struct {
MsgpackRpcRequestHandler handler;
Array args;
uint32_t request_id;
- ArenaMem used_mem;
+ Arena used_mem;
} RequestEvent;
typedef struct {
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index b252f0998e..532e684f93 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -216,7 +216,7 @@ bool server_stop(char *endpoint)
watchers.ga_len--;
// Bump v:servername to the next available server, if any.
- if (strequal(addr, (char *)get_vim_var_str(VV_SEND_SERVER))) {
+ if (strequal(addr, get_vim_var_str(VV_SEND_SERVER))) {
set_vservername(&watchers);
}
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
index c8e9fdd4c3..24480835a1 100644
--- a/src/nvim/msgpack_rpc/unpacker.c
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -181,13 +181,11 @@ void unpacker_init(Unpacker *p)
p->unpack_error = (Error)ERROR_INIT;
p->arena = (Arena)ARENA_EMPTY;
- p->reuse_blk = NULL;
}
void unpacker_teardown(Unpacker *p)
{
- arena_mem_free(p->reuse_blk, NULL);
- arena_mem_free(arena_finish(&p->arena), NULL);
+ arena_mem_free(arena_finish(&p->arena));
}
bool unpacker_parse_header(Unpacker *p)
@@ -308,7 +306,7 @@ bool unpacker_advance(Unpacker *p)
p->state = 10;
} else {
p->state = p->type == kMessageTypeResponse ? 1 : 2;
- arena_start(&p->arena, &p->reuse_blk);
+ p->arena = (Arena)ARENA_EMPTY;
}
}
@@ -322,7 +320,7 @@ bool unpacker_advance(Unpacker *p)
goto done;
} else {
// unpack other ui events using mpack_parse()
- arena_start(&p->arena, &p->reuse_blk);
+ p->arena = (Arena)ARENA_EMPTY;
}
}
@@ -416,13 +414,13 @@ redo:
if (p->ui_handler.fn != ui_client_event_grid_line) {
p->state = 12;
if (p->grid_line_event) {
- arena_mem_free(arena_finish(&p->arena), &p->reuse_blk);
+ arena_mem_free(arena_finish(&p->arena));
p->grid_line_event = NULL;
}
return true;
} else {
p->state = 13;
- arena_start(&p->arena, &p->reuse_blk);
+ p->arena = (Arena)ARENA_EMPTY;
p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true);
g = p->grid_line_event;
}
diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h
index f39439be63..35048fb877 100644
--- a/src/nvim/msgpack_rpc/unpacker.h
+++ b/src/nvim/msgpack_rpc/unpacker.h
@@ -32,8 +32,6 @@ struct Unpacker {
Error unpack_error;
Arena arena;
- // one length free-list of reusable blocks
- ArenaMem reuse_blk;
int nevents;
int ncalls;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 2d752eade7..47ad000385 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -18,10 +18,13 @@
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
+#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/event/loop.h"
#include "nvim/ex_cmds.h"
@@ -32,7 +35,8 @@
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
+#include "nvim/help.h"
#include "nvim/indent.h"
#include "nvim/keycodes.h"
#include "nvim/log.h"
@@ -41,6 +45,7 @@
#include "nvim/mark.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -50,15 +55,18 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/plines.h"
+#include "nvim/profile.h"
#include "nvim/quickfix.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/tag.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -465,7 +473,7 @@ void normal_enter(bool cmdwin, bool noexmode)
static void normal_prepare(NormalState *s)
{
- memset(&s->ca, 0, sizeof(s->ca)); // also resets ca.retval
+ CLEAR_FIELD(s->ca); // also resets s->ca.retval
s->ca.oap = &s->oa;
// Use a count remembered from before entering an operator. After typing "3d"
@@ -517,7 +525,7 @@ static bool normal_handle_special_visual_command(NormalState *s)
&& (nv_cmds[s->idx].cmd_flags & NV_STS)
&& !(mod_mask & MOD_MASK_SHIFT)) {
end_visual_mode();
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
// Keys that work different when 'keymodel' contains "startsel"
@@ -623,16 +631,16 @@ static void normal_redraw_mode_message(NormalState *s)
if (must_redraw && keep_msg != NULL && !emsg_on_display) {
char_u *kmsg;
- kmsg = keep_msg;
+ kmsg = (char_u *)keep_msg;
keep_msg = NULL;
// Showmode() will clear keep_msg, but we want to use it anyway.
// First update w_topline.
setcursor();
update_screen(0);
// now reset it, otherwise it's put in the history again
- keep_msg = kmsg;
+ keep_msg = (char *)kmsg;
- kmsg = vim_strsave(keep_msg);
+ kmsg = vim_strsave((char_u *)keep_msg);
msg_attr((const char *)kmsg, keep_msg_attr);
xfree(kmsg);
}
@@ -1275,11 +1283,11 @@ static void normal_redraw(NormalState *s)
validate_cursor();
if (VIsual_active) {
- redraw_curbuf_later(INVERTED); // update inverted part
- update_screen(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED); // update inverted part
+ update_screen(0);
} else if (must_redraw) {
update_screen(0);
- } else if (redraw_cmdline || clear_cmdline) {
+ } else if (redraw_cmdline || clear_cmdline || redraw_mode) {
showmode();
}
@@ -1294,7 +1302,7 @@ static void normal_redraw(NormalState *s)
// Display message after redraw. If an external message is still visible,
// it contains the kept message already.
if (keep_msg != NULL && !msg_ext_is_visible()) {
- char_u *const p = vim_strsave(keep_msg);
+ char *const p = xstrdup(keep_msg);
// msg_start() will set keep_msg to NULL, make a copy
// first. Don't reset keep_msg, msg_attr_keep() uses it to
@@ -1316,7 +1324,7 @@ static void normal_redraw(NormalState *s)
did_emsg = false;
msg_didany = false; // reset lines_left in msg_start()
may_clear_sb_text(); // clear scroll-back text on next msg
- showruler(false);
+ show_cursor_info(false);
setcursor();
}
@@ -1832,7 +1840,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
}
if (jump_flags) {
jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
- update_curbuf(VIsual_active ? INVERTED : VALID);
+ redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
+ update_screen(0);
setcursor();
ui_flush(); // Update before showing popup menu
}
@@ -2179,7 +2188,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, long count, bool fixindent)
curwin->w_set_curswant = true;
}
if (is_click) {
- redraw_curbuf_later(INVERTED); // update the inversion
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
}
} else if (VIsual_active && !old_active) {
if (mod_mask & MOD_MASK_ALT) {
@@ -2304,7 +2313,7 @@ void reset_VIsual_and_resel(void)
{
if (VIsual_active) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion later
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion later
}
VIsual_reselect = false;
}
@@ -2314,7 +2323,7 @@ void reset_VIsual(void)
{
if (VIsual_active) {
end_visual_mode();
- redraw_curbuf_later(INVERTED); // delete the inversion later
+ redraw_curbuf_later(UPD_INVERTED); // delete the inversion later
VIsual_reselect = false;
}
}
@@ -2383,7 +2392,7 @@ static bool find_is_eval_item(const char_u *const ptr, int *const colp, int *con
///
/// If text is found, a pointer to the text is put in "*text". This
/// points into the current buffer line and is not always NUL terminated.
-size_t find_ident_under_cursor(char_u **text, int find_type)
+size_t find_ident_under_cursor(char **text, int find_type)
FUNC_ATTR_NONNULL_ARG(1)
{
return find_ident_at_pos(curwin, curwin->w_cursor.lnum,
@@ -2394,7 +2403,7 @@ size_t find_ident_under_cursor(char_u **text, int find_type)
/// However: Uses 'iskeyword' from the current window!.
///
/// @param textcol column where "text" starts, can be NULL
-size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **text, int *textcol,
+size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char **text, int *textcol,
int find_type)
FUNC_ATTR_NONNULL_ARG(1, 4)
{
@@ -2470,7 +2479,7 @@ size_t find_ident_at_pos(win_T *wp, linenr_T lnum, colnr_T startcol, char_u **te
return 0;
}
ptr += col;
- *text = ptr;
+ *text = (char *)ptr;
if (textcol != NULL) {
*textcol = col;
}
@@ -2645,8 +2654,8 @@ void clear_showcmd(void)
lines = bot - top + 1;
if (VIsual_mode == Ctrl_V) {
- char_u *const saved_sbr = p_sbr;
- char_u *const saved_w_sbr = curwin->w_p_sbr;
+ char *const saved_sbr = p_sbr;
+ char *const saved_w_sbr = curwin->w_p_sbr;
// Make 'sbr' empty for a moment to get the correct size.
p_sbr = empty_option;
@@ -2709,8 +2718,6 @@ void clear_showcmd(void)
/// @return true if output has been written (and setcursor() has been called).
bool add_to_showcmd(int c)
{
- char_u *p;
- int i;
static int ignore[] = {
K_IGNORE,
K_LEFTMOUSE, K_LEFTDRAG, K_LEFTRELEASE, K_MOUSEMOVE,
@@ -2733,14 +2740,14 @@ bool add_to_showcmd(int c)
// Ignore keys that are scrollbar updates and mouse clicks
if (IS_SPECIAL(c)) {
- for (i = 0; ignore[i] != 0; i++) {
+ for (int i = 0; ignore[i] != 0; i++) {
if (ignore[i] == c) {
return false;
}
}
}
- p = transchar(c);
+ char *p = (char *)transchar(c);
if (*p == ' ') {
STRCPY(p, "<20>");
}
@@ -2836,12 +2843,12 @@ static void display_showcmd(void)
grid_puts_line_start(&msg_grid_adj, showcmd_row);
if (!showcmd_is_clear) {
- grid_puts(&msg_grid_adj, showcmd_buf, showcmd_row, sc_col,
+ grid_puts(&msg_grid_adj, (char *)showcmd_buf, showcmd_row, sc_col,
HL_ATTR(HLF_MSG));
}
// clear the rest of an old message by outputting up to SHOWCMD_COLS spaces
- grid_puts(&msg_grid_adj, (char_u *)" " + len, showcmd_row,
+ grid_puts(&msg_grid_adj, (char *)" " + len, showcmd_row,
sc_col + len, HL_ATTR(HLF_MSG));
grid_puts_line_flush(false);
@@ -2949,7 +2956,7 @@ void check_scrollbind(linenr_T topline_diff, long leftcol_diff)
}
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
cursor_correct();
curwin->w_redr_status = true;
}
@@ -3035,9 +3042,9 @@ static void nv_page(cmdarg_T *cap)
static void nv_gd(oparg_T *oap, int nchar, int thisblock)
{
size_t len;
- char_u *ptr;
+ char *ptr;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0
- || !find_decl(ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
+ || !find_decl((char_u *)ptr, len, nchar == 'd', thisblock, SEARCH_START)) {
clearopbeep(oap);
} else {
if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) {
@@ -3483,7 +3490,7 @@ void scroll_redraw(int up, long count)
if (moved) {
curwin->w_viewport_invalid = true;
}
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
}
/// Get the count specified after a 'z' command. Only the 'z<CR>', 'zl', 'zh',
@@ -3557,7 +3564,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
if (checkclearop(cap->oap)) {
return OK;
}
- char_u *ptr = NULL;
+ char *ptr = NULL;
size_t len;
if (VIsual_active && !get_visual_text(cap, &ptr, &len)) {
return FAIL;
@@ -3572,7 +3579,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
len = spell_move_to(curwin, FORWARD, true, true, NULL);
emsg_off--;
if (len != 0 && curwin->w_cursor.col <= pos.col) {
- ptr = ml_get_pos(&curwin->w_cursor);
+ ptr = (char *)ml_get_pos(&curwin->w_cursor);
}
curwin->w_cursor = pos;
}
@@ -3581,7 +3588,7 @@ static int nv_zg_zw(cmdarg_T *cap, int nchar)
return FAIL;
}
assert(len <= INT_MAX);
- spell_add_word(ptr, (int)len,
+ spell_add_word((char_u *)ptr, (int)len,
nchar == 'w' || nchar == 'W' ? SPELL_ADD_BAD : SPELL_ADD_GOOD,
(nchar == 'G' || nchar == 'W') ? 0 : (int)cap->count1,
undo);
@@ -3649,7 +3656,7 @@ static void nv_zet(cmdarg_T *cap)
case 't':
scroll_cursor_top(0, true);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
set_fraction(curwin);
break;
@@ -3660,7 +3667,7 @@ static void nv_zet(cmdarg_T *cap)
case 'z':
scroll_cursor_halfway(true);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
set_fraction(curwin);
break;
@@ -3683,7 +3690,7 @@ static void nv_zet(cmdarg_T *cap)
case 'b':
scroll_cursor_bot(0, true);
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
set_fraction(curwin);
break;
@@ -3735,7 +3742,7 @@ static void nv_zet(cmdarg_T *cap)
}
if (curwin->w_leftcol != col) {
curwin->w_leftcol = col;
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
}
break;
@@ -3756,7 +3763,7 @@ static void nv_zet(cmdarg_T *cap)
}
if (curwin->w_leftcol != col) {
curwin->w_leftcol = col;
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
}
break;
@@ -4091,7 +4098,7 @@ static void nv_clear(cmdarg_T *cap)
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
wp->w_s->b_syn_slow = false;
}
- redraw_later(curwin, CLEAR);
+ redraw_later(curwin, UPD_CLEAR);
}
}
@@ -4148,7 +4155,7 @@ void do_nv_ident(int c1, int c2)
cmdarg_T ca;
clear_oparg(&oa);
- memset(&ca, 0, sizeof(ca));
+ CLEAR_FIELD(ca);
ca.oap = &oa;
ca.cmdchar = c1;
ca.nchar = c2;
@@ -4157,7 +4164,7 @@ void do_nv_ident(int c1, int c2)
/// 'K' normal-mode command. Get the command to lookup the keyword under the
/// cursor.
-static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, char_u **ptr_arg,
+static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, char **ptr_arg,
size_t n, char *buf, size_t buf_size)
{
if (kp_help) {
@@ -4176,7 +4183,7 @@ static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, c
return n;
}
- char_u *ptr = *ptr_arg;
+ char *ptr = *ptr_arg;
// An external command will probably use an argument starting
// with "-" as an option. To avoid trouble we skip the "-".
@@ -4226,7 +4233,7 @@ static size_t nv_K_getcmd(cmdarg_T *cap, char_u *kp, bool kp_help, bool kp_ex, c
/// g ']' :tselect for current identifier
static void nv_ident(cmdarg_T *cap)
{
- char_u *ptr = NULL;
+ char *ptr = NULL;
char_u *p;
size_t n = 0; // init for GCC
int cmdchar;
@@ -4268,14 +4275,13 @@ static void nv_ident(cmdarg_T *cap)
// Allocate buffer to put the command in. Inserting backslashes can
// double the length of the word. p_kp / curbuf->b_p_kp could be added
// and some numbers.
- char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : curbuf->b_p_kp; // 'keywordprg'
- assert(*kp != NUL); // option.c:do_set() should default to ":help" if empty.
- bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command
- bool kp_help = (STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0);
- if (kp_help && *skipwhite((char *)ptr) == NUL) {
+ char_u *kp = *curbuf->b_p_kp == NUL ? p_kp : (char_u *)curbuf->b_p_kp; // 'keywordprg'
+ bool kp_help = (*kp == NUL || STRCMP(kp, ":he") == 0 || STRCMP(kp, ":help") == 0);
+ if (kp_help && *skipwhite(ptr) == NUL) {
emsg(_(e_noident)); // found white space only
return;
}
+ bool kp_ex = (*kp == ':'); // 'keywordprg' is an ex command
size_t buf_size = n * 2 + 30 + STRLEN(kp);
char *buf = xmalloc(buf_size);
buf[0] = NUL;
@@ -4288,9 +4294,9 @@ static void nv_ident(cmdarg_T *cap)
// Call setpcmark() first, so "*``" puts the cursor back where
// it was.
setpcmark();
- curwin->w_cursor.col = (colnr_T)(ptr - get_cursor_line_ptr());
+ curwin->w_cursor.col = (colnr_T)(ptr - (char *)get_cursor_line_ptr());
- if (!g_cmd && vim_iswordp(ptr)) {
+ if (!g_cmd && vim_iswordp((char_u *)ptr)) {
STRCPY(buf, "\\<");
}
no_smartcase = true; // don't use 'smartcase' now
@@ -4327,13 +4333,13 @@ static void nv_ident(cmdarg_T *cap)
// Now grab the chars in the identifier
if (cmdchar == 'K' && !kp_help) {
- ptr = vim_strnsave(ptr, n);
+ ptr = xstrnsave(ptr, n);
if (kp_ex) {
// Escape the argument properly for an Ex command
p = (char_u *)vim_strsave_fnameescape((const char *)ptr, VSE_NONE);
} else {
// Escape the argument properly for a shell command
- p = vim_strsave_shellescape(ptr, true, true);
+ p = vim_strsave_shellescape((char_u *)ptr, true, true);
}
xfree(ptr);
char *newbuf = xrealloc(buf, STRLEN(buf) + STRLEN(p) + 1);
@@ -4364,11 +4370,11 @@ static void nv_ident(cmdarg_T *cap)
}
// When current byte is a part of multibyte character, copy all
// bytes of that character.
- const size_t len = (size_t)(utfc_ptr2len((char *)ptr) - 1);
+ const size_t len = (size_t)(utfc_ptr2len(ptr) - 1);
for (size_t i = 0; i < len && n > 0; i++, n--) {
- *p++ = *ptr++;
+ *p++ = (char_u)(*ptr++);
}
- *p++ = *ptr++;
+ *p++ = (char_u)(*ptr++);
}
*p = NUL;
}
@@ -4376,14 +4382,15 @@ static void nv_ident(cmdarg_T *cap)
// Execute the command.
if (cmdchar == '*' || cmdchar == '#') {
if (!g_cmd
- && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), ptr))) {
+ && vim_iswordp(mb_prevptr(get_cursor_line_ptr(), (char_u *)ptr))) {
STRCAT(buf, "\\>");
}
+
// put pattern in search history
init_history();
add_to_history(HIST_SEARCH, (char_u *)buf, true, NUL);
- (void)normal_search(cap, cmdchar == '*' ? '/' : '?', (char_u *)buf, 0,
- NULL);
+
+ (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL);
} else {
g_tag_at_cursor = true;
do_cmdline_cmd(buf);
@@ -4406,7 +4413,7 @@ static void nv_ident(cmdarg_T *cap)
/// @param lenp return: length of selected text
///
/// @return false if more than one line selected.
-bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp)
+bool get_visual_text(cmdarg_T *cap, char **pp, size_t *lenp)
{
if (VIsual_mode != 'V') {
unadjust_for_sel();
@@ -4418,14 +4425,14 @@ bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp)
return false;
}
if (VIsual_mode == 'V') {
- *pp = get_cursor_line_ptr();
+ *pp = (char *)get_cursor_line_ptr();
*lenp = STRLEN(*pp);
} else {
if (lt(curwin->w_cursor, VIsual)) {
- *pp = ml_get_pos(&curwin->w_cursor);
+ *pp = (char *)ml_get_pos(&curwin->w_cursor);
*lenp = (size_t)VIsual.col - (size_t)curwin->w_cursor.col + 1;
} else {
- *pp = ml_get_pos(&VIsual);
+ *pp = (char *)ml_get_pos(&VIsual);
*lenp = (size_t)curwin->w_cursor.col - (size_t)VIsual.col + 1;
}
if (**pp == NUL) {
@@ -4433,7 +4440,7 @@ bool get_visual_text(cmdarg_T *cap, char_u **pp, size_t *lenp)
}
if (*lenp > 0) {
// Correct the length to include all bytes of the last character.
- *lenp += (size_t)(utfc_ptr2len((char *)(*pp) + (*lenp - 1)) - 1);
+ *lenp += (size_t)(utfc_ptr2len(*pp + (*lenp - 1)) - 1);
}
}
reset_VIsual_and_resel();
@@ -4557,9 +4564,9 @@ static void nv_right(cmdarg_T *cap)
// <Space> wraps to next line if 'whichwrap' has 's'.
// 'l' wraps to next line if 'whichwrap' has 'l'.
// CURS_RIGHT wraps to next line if 'whichwrap' has '>'.
- if (((cap->cmdchar == ' ' && vim_strchr((char *)p_ww, 's') != NULL)
- || (cap->cmdchar == 'l' && vim_strchr((char *)p_ww, 'l') != NULL)
- || (cap->cmdchar == K_RIGHT && vim_strchr((char *)p_ww, '>') != NULL))
+ if (((cap->cmdchar == ' ' && vim_strchr(p_ww, 's') != NULL)
+ || (cap->cmdchar == 'l' && vim_strchr(p_ww, 'l') != NULL)
+ || (cap->cmdchar == K_RIGHT && vim_strchr(p_ww, '>') != NULL))
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
// When deleting we also count the NL as a character.
// Set cap->oap->inclusive when last char in the line is
@@ -4627,9 +4634,9 @@ static void nv_left(cmdarg_T *cap)
// 'h' wraps to previous line if 'whichwrap' has 'h'.
// CURS_LEFT wraps to previous line if 'whichwrap' has '<'.
if ((((cap->cmdchar == K_BS || cap->cmdchar == Ctrl_H)
- && vim_strchr((char *)p_ww, 'b') != NULL)
- || (cap->cmdchar == 'h' && vim_strchr((char *)p_ww, 'h') != NULL)
- || (cap->cmdchar == K_LEFT && vim_strchr((char *)p_ww, '<') != NULL))
+ && vim_strchr(p_ww, 'b') != NULL)
+ || (cap->cmdchar == 'h' && vim_strchr(p_ww, 'h') != NULL)
+ || (cap->cmdchar == K_LEFT && vim_strchr(p_ww, '<') != NULL))
&& curwin->w_cursor.lnum > 1) {
curwin->w_cursor.lnum--;
coladvance(MAXCOL);
@@ -4796,7 +4803,7 @@ static void nv_search(cmdarg_T *cap)
// When using 'incsearch' the cursor may be moved to set a different search
// start position.
- cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0, true);
+ cap->searchbuf = (char *)getcmdline(cap->cmdchar, cap->count1, 0, true);
if (cap->searchbuf == NULL) {
clearop(oap);
@@ -4832,9 +4839,8 @@ static void nv_next(cmdarg_T *cap)
/// @param opt extra flags for do_search()
///
/// @return 0 for failure, 1 for found, 2 for found and line offset added.
-static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrapped)
+static int normal_search(cmdarg_T *cap, int dir, char *pat, int opt, int *wrapped)
{
- int i;
searchit_arg_T sia;
cap->oap->motion_type = kMTCharWise;
@@ -4842,9 +4848,9 @@ static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrap
cap->oap->use_reg_one = true;
curwin->w_set_curswant = true;
- memset(&sia, 0, sizeof(sia));
- i = do_search(cap->oap, dir, dir, pat, cap->count1,
- opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
+ CLEAR_FIELD(sia);
+ int i = do_search(cap->oap, dir, dir, (char_u *)pat, cap->count1,
+ opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
if (wrapped != NULL) {
*wrapped = sia.sa_wrapped;
}
@@ -5044,15 +5050,15 @@ static void nv_brackets(cmdarg_T *cap)
// fwd bwd fwd bwd fwd bwd
// identifier "]i" "[i" "]I" "[I" "]^I" "[^I"
// define "]d" "[d" "]D" "[D" "]^D" "[^D"
- char_u *ptr;
+ char *ptr;
size_t len;
if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) {
clearop(cap->oap);
} else {
// Make a copy, if the line was changed it will be freed.
- ptr = vim_strnsave(ptr, len);
- find_pattern_in_path(ptr, 0, len, true,
+ ptr = xstrnsave(ptr, len);
+ find_pattern_in_path((char_u *)ptr, 0, len, true,
cap->count0 == 0 ? !isupper(cap->nchar) : false,
(((cap->nchar & 0xf) == ('d' & 0xf))
? FIND_DEFINE
@@ -5543,7 +5549,7 @@ static void n_swapchar(cmdarg_T *cap)
return;
}
- if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr((char *)p_ww, '~') == NULL) {
+ if (LINEEMPTY(curwin->w_cursor.lnum) && vim_strchr(p_ww, '~') == NULL) {
clearopbeep(cap->oap);
return;
}
@@ -5559,7 +5565,7 @@ static void n_swapchar(cmdarg_T *cap)
did_change |= swapchar(cap->oap->op_type, &curwin->w_cursor);
inc_cursor();
if (gchar_cursor() == NUL) {
- if (vim_strchr((char *)p_ww, '~') != NULL
+ if (vim_strchr(p_ww, '~') != NULL
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
@@ -5615,7 +5621,7 @@ static MarkMoveRes nv_mark_move_to(cmdarg_T *cap, MarkMove flags, fmark_T *fm)
/// Handle commands that are operators in Visual mode.
static void v_visop(cmdarg_T *cap)
{
- static char_u trans[] = "YyDdCcxdXdAAIIrr";
+ static char trans[] = "YyDdCcxdXdAAIIrr";
// Uppercase means linewise, except in block mode, then "D" deletes till
// the end of the line, and "C" replaces till EOL
@@ -5627,7 +5633,7 @@ static void v_visop(cmdarg_T *cap)
curwin->w_curswant = MAXCOL;
}
}
- cap->cmdchar = (uint8_t)(*(vim_strchr((char *)trans, cap->cmdchar) + 1));
+ cap->cmdchar = (uint8_t)(*(vim_strchr(trans, cap->cmdchar) + 1));
nv_operator(cap);
}
@@ -5810,7 +5816,7 @@ static void nv_visual(cmdarg_T *cap)
showmode();
may_trigger_modechanged();
}
- redraw_curbuf_later(INVERTED); // update the inversion
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
} else { // start Visual mode
if (cap->count0 > 0 && resel_VIsual_mode != NUL) {
// use previously selected part
@@ -5856,7 +5862,7 @@ static void nv_visual(cmdarg_T *cap)
} else {
curwin->w_set_curswant = true;
}
- redraw_curbuf_later(INVERTED); // show the inversion
+ redraw_curbuf_later(UPD_INVERTED); // show the inversion
} else {
if (!cap->arg) {
// start Select mode when 'selectmode' contains "cmd"
@@ -5891,7 +5897,7 @@ void start_selection(void)
void may_start_select(int c)
{
VIsual_select = (c == 'o' || (stuff_empty() && typebuf_typed()))
- && vim_strchr((char *)p_slm, c) != NULL;
+ && vim_strchr(p_slm, c) != NULL;
}
/// Start Visual mode "c".
@@ -5922,7 +5928,7 @@ static void n_start_visual_mode(int c)
}
// Only need to redraw this line, unless still need to redraw an old
// Visual area (when 'lazyredraw' is set).
- if (curwin->w_redr_type < INVERTED) {
+ if (curwin->w_redr_type < UPD_INVERTED) {
curwin->w_old_cursor_lnum = curwin->w_cursor.lnum;
curwin->w_old_visual_lnum = curwin->w_cursor.lnum;
}
@@ -6009,7 +6015,7 @@ static void nv_gv_cmd(cmdarg_T *cap)
may_start_select('c');
}
setmouse();
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
showmode();
}
@@ -6892,7 +6898,7 @@ static void nv_normal(cmdarg_T *cap)
}
if (VIsual_active) {
end_visual_mode(); // stop Visual
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
} else {
clearopbeep(cap->oap);
@@ -6923,6 +6929,10 @@ static void nv_esc(cmdarg_T *cap)
}
}
+ if (restart_edit != 0) {
+ redraw_mode = true; // remove "-- (insert) --"
+ }
+
restart_edit = 0;
if (cmdwin_type != 0) {
@@ -6930,10 +6940,10 @@ static void nv_esc(cmdarg_T *cap)
got_int = false; // don't stop executing autocommands et al.
return;
}
- } else if (cmdwin_type != 0 && ex_normal_busy) {
+ } else if (cmdwin_type != 0 && ex_normal_busy && typebuf_was_empty) {
// When :normal runs out of characters while in the command line window
- // vgetorpeek() will return ESC. Exit the cmdline window to break the
- // loop.
+ // vgetorpeek() will repeatedly return ESC. Exit the cmdline window to
+ // break the loop.
cmdwin_result = K_IGNORE;
return;
}
@@ -6942,7 +6952,7 @@ static void nv_esc(cmdarg_T *cap)
end_visual_mode(); // stop Visual
check_cursor_col(); // make sure cursor is not beyond EOL
curwin->w_set_curswant = true;
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
} else if (no_reason) {
vim_beep(BO_ESC);
}
@@ -7062,8 +7072,8 @@ static void nv_object(cmdarg_T *cap)
include = true; // "ax" = an object: include white space
}
// Make sure (), [], {} and <> are in 'matchpairs'
- mps_save = curbuf->b_p_mps;
- curbuf->b_p_mps = (char_u *)"(:),{:},[:],<:>";
+ mps_save = (char_u *)curbuf->b_p_mps;
+ curbuf->b_p_mps = "(:),{:},[:],<:>";
switch (cap->nchar) {
case 'w': // "aw" = a word
@@ -7117,7 +7127,7 @@ static void nv_object(cmdarg_T *cap)
break;
}
- curbuf->b_p_mps = mps_save;
+ curbuf->b_p_mps = (char *)mps_save;
if (!flag) {
clearopbeep(cap->oap);
}
diff --git a/src/nvim/normal.h b/src/nvim/normal.h
index 9bda70eacd..13ea233658 100644
--- a/src/nvim/normal.h
+++ b/src/nvim/normal.h
@@ -57,7 +57,7 @@ typedef struct oparg_S {
* Arguments for Normal mode commands.
*/
typedef struct cmdarg_S {
- oparg_T *oap; // Operator arguments
+ oparg_T *oap; // Operator arguments
int prechar; // prefix character (optional, always 'g')
int cmdchar; // command character
int nchar; // next command character (optional)
@@ -69,7 +69,7 @@ typedef struct cmdarg_S {
long count1; // count before command, default 1
int arg; // extra argument from nv_cmds[]
int retval; // return: CA_* values
- char_u *searchbuf; // return: pointer to search pattern or NULL
+ char *searchbuf; // return: pointer to search pattern or NULL
} cmdarg_T;
// values for retval:
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 53612be697..4f2b84d20f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -17,6 +17,7 @@
#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -24,7 +25,6 @@
#include "nvim/ex_cmds2.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
-#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
@@ -47,11 +47,11 @@
#include "nvim/os/time.h"
#include "nvim/path.h"
#include "nvim/plines.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/terminal.h"
+#include "nvim/textformat.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -79,9 +79,9 @@ struct block_def {
colnr_T textcol; // index of chars (partially) in block
colnr_T start_vcol; // start col of 1st char wholly inside block
colnr_T end_vcol; // start col of 1st char wholly after block
- int is_short; // TRUE if line is too short to fit in block
- int is_MAX; // TRUE if curswant==MAXCOL when starting
- int is_oneChar; // TRUE if block within one character
+ int is_short; // true if line is too short to fit in block
+ int is_MAX; // true if curswant==MAXCOL when starting
+ int is_oneChar; // true if block within one character
int pre_whitesp; // screen cols of ws before block
int pre_whitesp_c; // chars of ws before block
colnr_T end_char_vcols; // number of vcols of post-block char
@@ -182,13 +182,13 @@ int get_op_type(int char1, int char2)
return i;
}
-/// @return TRUE if operator "op" always works on whole lines.
+/// @return true if operator "op" always works on whole lines.
int op_on_lines(int op)
{
return opchars[op][2] & OPF_LINES;
}
-/// @return TRUE if operator "op" changes text.
+/// @return true if operator "op" changes text.
int op_is_change(int op)
{
return opchars[op][2] & OPF_CHANGE;
@@ -235,7 +235,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
// isn't set or 'cindent' isn't set or '#' isn't in 'cino'.
shift_line(oap->op_type == OP_LSHIFT, p_sr, amount, false);
}
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
if (oap->motion_type == kMTBlockWise) {
@@ -245,7 +245,7 @@ void op_shift(oparg_T *oap, int curs_top, int amount)
curwin->w_cursor.lnum = oap->start.lnum;
beginline(BL_SOL | BL_FIX); // shift_line() may have set cursor.col
} else {
- --curwin->w_cursor.lnum; // put cursor on last line, for ":>"
+ curwin->w_cursor.lnum--; // put cursor on last line, for ":>"
}
// The cursor line is not in a closed fold
foldOpenCursor();
@@ -289,13 +289,13 @@ void shift_line(int left, int round, int amount, int call_changed_bytes)
{
int count;
int i, j;
- int p_sw = (int)get_sw_value_indent(curbuf);
+ const int sw_val = (int)get_sw_value_indent(curbuf);
count = get_indent(); // get current indent
if (round) { // round off indent
- i = count / p_sw; // number of p_sw rounded down
- j = count % p_sw; // extra spaces
+ i = count / sw_val; // number of 'shiftwidth' rounded down
+ j = count % sw_val; // extra spaces
if (j && left) { // first remove extra spaces
amount--;
}
@@ -307,15 +307,15 @@ void shift_line(int left, int round, int amount, int call_changed_bytes)
} else {
i += amount;
}
- count = i * p_sw;
+ count = i * sw_val;
} else { // original vi indent
if (left) {
- count -= p_sw * amount;
+ count -= sw_val * amount;
if (count < 0) {
count = 0;
}
} else {
- count += p_sw * amount;
+ count += sw_val * amount;
}
}
@@ -335,9 +335,8 @@ static void shift_block(oparg_T *oap, int amount)
const int oldstate = State;
char_u *newp;
const int oldcol = curwin->w_cursor.col;
- int p_sw = (int)get_sw_value_indent(curbuf);
- long *p_vts = curbuf->b_p_vts_array;
- const long p_ts = curbuf->b_p_ts;
+ const int sw_val = (int)get_sw_value_indent(curbuf);
+ const int ts_val = (int)curbuf->b_p_ts;
struct block_def bd;
int incr;
int i = 0, j = 0;
@@ -352,8 +351,8 @@ static void shift_block(oparg_T *oap, int amount)
}
// total is number of screen columns to be inserted/removed
- int total = (int)((unsigned)amount * (unsigned)p_sw);
- if ((total / p_sw) != amount) {
+ int total = (int)((unsigned)amount * (unsigned)sw_val);
+ if ((total / sw_val) != amount) {
return; // multiplication overflow
}
@@ -379,16 +378,24 @@ static void shift_block(oparg_T *oap, int amount)
bd.startspaces = 0;
}
}
- for (; ascii_iswhite(*bd.textstart);) {
- // TODO(fmoralesc): is passing bd.textstart for start of the line OK?
- incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, bd.start_vcol);
+
+ // TODO(vim): is passing bd.textstart for start of the line OK?
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
+ bd.start_vcol, bd.textstart, bd.textstart);
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ incr = lbr_chartabsize_adv(&cts);
total += incr;
- bd.start_vcol += incr;
+ cts.cts_vcol += incr;
}
+ bd.textstart = (char_u *)cts.cts_ptr;
+ bd.start_vcol = cts.cts_vcol;
+ clear_chartabsize_arg(&cts);
+
// OK, now total=all the VWS reqd, and textstart points at the 1st
// non-ws char in the block.
if (!curbuf->b_p_et) {
- tabstop_fromto(ws_vcol, ws_vcol + total, p_ts, p_vts, &i, &j);
+ tabstop_fromto(ws_vcol, ws_vcol + total, ts_val, curbuf->b_p_vts_array, &i, &j);
} else {
j = total;
}
@@ -439,10 +446,16 @@ static void shift_block(oparg_T *oap, int amount)
// The character's column is in "bd.start_vcol".
colnr_T non_white_col = bd.start_vcol;
- while (ascii_iswhite(*non_white)) {
- incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col);
- non_white_col += incr;
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum,
+ non_white_col, bd.textstart, non_white);
+ while (ascii_iswhite(*cts.cts_ptr)) {
+ incr = lbr_chartabsize_adv(&cts);
+ cts.cts_vcol += incr;
}
+ non_white_col = cts.cts_vcol;
+ non_white = (char_u *)cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
const colnr_T block_space_width = non_white_col - oap->start_vcol;
// We will shift by "total" or "block_space_width", whichever is less.
@@ -463,17 +476,19 @@ static void shift_block(oparg_T *oap, int amount)
if (bd.startspaces) {
verbatim_copy_width -= bd.start_char_vcols;
}
- while (verbatim_copy_width < destination_col) {
- char_u *line = verbatim_copy_end;
-
- // TODO: is passing verbatim_copy_end for start of the line OK?
- incr = lbr_chartabsize(line, verbatim_copy_end, verbatim_copy_width);
- if (verbatim_copy_width + incr > destination_col) {
+ init_chartabsize_arg(&cts, curwin, 0, verbatim_copy_width,
+ bd.textstart, verbatim_copy_end);
+ while (cts.cts_vcol < destination_col) {
+ incr = lbr_chartabsize(&cts);
+ if (cts.cts_vcol + incr > destination_col) {
break;
}
- verbatim_copy_width += incr;
- MB_PTR_ADV(verbatim_copy_end);
+ cts.cts_vcol += incr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ verbatim_copy_width = cts.cts_vcol;
+ verbatim_copy_end = (char_u *)cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
// If "destination_col" is different from the width of the initial
// part of the line that will be copied, it means we encountered a tab
@@ -512,7 +527,7 @@ static void shift_block(oparg_T *oap, int amount)
/// Caller must prepare for undo.
static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def *bdp)
{
- int p_ts;
+ int ts_val;
int count = 0; // extra spaces to replace a cut TAB
int spaces = 0; // non-zero if cutting a TAB
colnr_T offset; // pointer along new line
@@ -531,18 +546,18 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
oldp = ml_get(lnum);
if (b_insert) {
- p_ts = bdp->start_char_vcols;
+ ts_val = bdp->start_char_vcols;
spaces = bdp->startspaces;
if (spaces != 0) {
- count = p_ts - 1; // we're cutting a TAB
+ count = ts_val - 1; // we're cutting a TAB
}
offset = bdp->textcol;
} else { // append
- p_ts = bdp->end_char_vcols;
+ ts_val = bdp->end_char_vcols;
if (!bdp->is_short) { // spaces = padding after block
- spaces = (bdp->endspaces ? p_ts - bdp->endspaces : 0);
+ spaces = (bdp->endspaces ? ts_val - bdp->endspaces : 0);
if (spaces != 0) {
- count = p_ts - 1; // we're cutting a TAB
+ count = ts_val - 1; // we're cutting a TAB
}
offset = bdp->textcol + bdp->textlen - (spaces != 0);
} else { // spaces = padding to block edge
@@ -566,7 +581,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
assert(count >= 0);
// Make sure the allocated size matches what is actually copied below.
newp = xmalloc(STRLEN(oldp) + (size_t)spaces + s_len
- + (spaces > 0 && !bdp->is_short ? (size_t)p_ts - (size_t)spaces : 0)
+ + (spaces > 0 && !bdp->is_short ? (size_t)ts_val - (size_t)spaces : 0)
+ (size_t)count + 1);
// copy up to shifted part
@@ -585,7 +600,7 @@ static void block_insert(oparg_T *oap, char_u *s, int b_insert, struct block_def
if (spaces > 0 && !bdp->is_short) {
if (*oldp == TAB) {
// insert post-padding
- memset(newp + offset + spaces, ' ', (size_t)(p_ts - spaces));
+ memset(newp + offset + spaces, ' ', (size_t)(ts_val - spaces));
// We're splitting a TAB, don't copy it.
oldp++;
// We allowed for that TAB, remember this now
@@ -684,7 +699,7 @@ void op_reindent(oparg_T *oap, Indenter how)
oap->is_VIsual ? start_lnum + (linenr_T)oap->line_count :
last_changed + 1, 0L, true);
} else if (oap->is_VIsual) {
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
if (oap->line_count > p_report) {
@@ -911,7 +926,7 @@ int do_record(int c)
if (!ui_has_messages()) {
// Enable macro indicator temporarily
set_option_value("ch", 1L, NULL, 0);
- update_screen(VALID);
+ update_screen(UPD_VALID);
changed_cmdheight = true;
}
@@ -965,7 +980,7 @@ int do_record(int c)
if (changed_cmdheight) {
// Restore cmdheight
set_option_value("ch", 0L, NULL, 0);
- redraw_all_later(CLEAR);
+ redraw_all_later(UPD_CLEAR);
}
}
return retval;
@@ -998,14 +1013,14 @@ static int stuff_yank(int regname, char_u *p)
}
yankreg_T *reg = get_yank_register(regname, YREG_YANK);
if (is_append_register(regname) && reg->y_array != NULL) {
- char_u **pp = (char_u **)&(reg->y_array[reg->y_size - 1]);
+ char **pp = &(reg->y_array[reg->y_size - 1]);
char_u *lp = xmalloc(STRLEN(*pp) + STRLEN(p) + 1);
STRCPY(lp, *pp);
// TODO(philix): use xstpcpy() in stuff_yank()
STRCAT(lp, p);
xfree(p);
xfree(*pp);
- *pp = lp;
+ *pp = (char *)lp;
} else {
free_register(reg);
set_yreg_additional_data(reg, NULL);
@@ -1112,7 +1127,7 @@ int do_execreg(int regname, int colon, int addcr, int silent)
// don't keep the cmdline containing @:
XFREE_CLEAR(new_last_cmdline);
// Escape all control characters with a CTRL-V
- p = vim_strsave_escaped_ext(last_cmdline,
+ p = vim_strsave_escaped_ext((char_u *)last_cmdline,
(char_u *)"\001\002\003\004\005\006\007"
"\010\011\012\013\014\015\016\017"
"\020\021\022\023\024\025\026\027"
@@ -1277,7 +1292,7 @@ int insert_reg(int regname, bool literally_arg)
return FAIL;
}
- char_u *arg;
+ char *arg;
if (regname == '.') { // Insert last inserted text.
retval = stuff_inserted(NUL, 1L, true);
} else if (get_spec_reg(regname, &arg, &allocated, true)) {
@@ -1313,34 +1328,6 @@ int insert_reg(int regname, bool literally_arg)
return retval;
}
-/// Stuff a string into the typeahead buffer, such that edit() will insert it
-/// literally ("literally" true) or interpret is as typed characters.
-static void stuffescaped(const char *arg, bool literally)
-{
- while (*arg != NUL) {
- // Stuff a sequence of normal ASCII characters, that's fast. Also
- // stuff K_SPECIAL to get the effect of a special key when "literally"
- // is true.
- const char *const start = arg;
- while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL
- && !literally)) {
- arg++;
- }
- if (arg > start) {
- stuffReadbuffLen(start, (arg - start));
- }
-
- // stuff a single special character
- if (*arg != NUL) {
- const int c = mb_cptr2char_adv((const char_u **)&arg);
- if (literally && ((c < ' ' && c != TAB) || c == DEL)) {
- stuffcharReadbuff(Ctrl_V);
- }
- stuffcharReadbuff(c);
- }
- }
-}
-
/// If "regname" is a special register, return true and store a pointer to its
/// value in "argp".
///
@@ -1348,7 +1335,7 @@ static void stuffescaped(const char *arg, bool literally)
/// @param errmsg give error message when failing
///
/// @return true if "regname" is a special register,
-bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
+bool get_spec_reg(int regname, char **argp, bool *allocated, bool errmsg)
{
size_t cnt;
@@ -1359,15 +1346,15 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
if (errmsg) {
check_fname(); // will give emsg if not set
}
- *argp = (char_u *)curbuf->b_fname;
+ *argp = curbuf->b_fname;
return true;
case '#': // alternate file name
- *argp = (char_u *)getaltfname(errmsg); // may give emsg if not set
+ *argp = getaltfname(errmsg); // may give emsg if not set
return true;
case '=': // result of expression
- *argp = get_expr_line();
+ *argp = (char *)get_expr_line();
*allocated = true;
return true;
@@ -1382,11 +1369,11 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
if (last_search_pat() == NULL && errmsg) {
emsg(_(e_noprevre));
}
- *argp = last_search_pat();
+ *argp = (char *)last_search_pat();
return true;
case '.': // last inserted text
- *argp = get_last_insert_save();
+ *argp = (char *)get_last_insert_save();
*allocated = true;
if (*argp == NULL && errmsg) {
emsg(_(e_noinstext));
@@ -1398,8 +1385,9 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
if (!errmsg) {
return false;
}
- *argp = file_name_at_cursor(FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0),
- 1L, NULL);
+ *argp
+ = (char *)file_name_at_cursor(FNAME_MESS | FNAME_HYP | (regname == Ctrl_P ? FNAME_EXP : 0),
+ 1L, NULL);
*allocated = true;
return true;
@@ -1411,7 +1399,7 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
cnt = find_ident_under_cursor(argp, (regname == Ctrl_W
? (FIND_IDENT|FIND_STRING)
: FIND_STRING));
- *argp = cnt ? vim_strnsave(*argp, cnt) : NULL;
+ *argp = cnt ? xstrnsave(*argp, cnt) : NULL;
*allocated = true;
return true;
@@ -1420,11 +1408,11 @@ bool get_spec_reg(int regname, char_u **argp, bool *allocated, bool errmsg)
return false;
}
- *argp = ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false);
+ *argp = (char *)ml_get_buf(curwin->w_buffer, curwin->w_cursor.lnum, false);
return true;
case '_': // black hole: always empty
- *argp = (char_u *)"";
+ *argp = "";
return true;
}
@@ -1578,6 +1566,7 @@ int op_delete(oparg_T *oap)
// Put deleted text into register 1 and shift number registers if the
// delete contains a line break, or when using a specific operator (Vi
// compatible)
+
if (oap->motion_type == kMTLineWise || oap->line_count > 1 || oap->use_reg_one) {
shift_delete_registers(is_append_register(oap->regname));
reg = &y_regs[1];
@@ -1927,8 +1916,8 @@ static int op_replace(oparg_T *oap, int c)
// times.
if (utf_char2cells(c) > 1) {
if ((numc & 1) && !bd.is_short) {
- ++bd.endspaces;
- ++n;
+ bd.endspaces++;
+ n++;
}
numc = numc / 2;
}
@@ -2003,7 +1992,7 @@ static int op_replace(oparg_T *oap, int c)
curwin->w_cursor.col = 0;
oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
if (oap->end.col) {
- --oap->end.col;
+ oap->end.col--;
}
} else if (!oap->inclusive) {
dec(&(oap->end));
@@ -2091,7 +2080,7 @@ void op_tilde(oparg_T *oap)
{
pos_T pos;
struct block_def bd;
- int did_change = FALSE;
+ int did_change = false;
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL) {
@@ -2117,7 +2106,7 @@ void op_tilde(oparg_T *oap)
pos.col = 0;
oap->end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum));
if (oap->end.col) {
- --oap->end.col;
+ oap->end.col--;
}
} else if (!oap->inclusive) {
dec(&(oap->end));
@@ -2144,7 +2133,7 @@ void op_tilde(oparg_T *oap)
if (!did_change && oap->is_VIsual) {
// No change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
@@ -2166,7 +2155,7 @@ void op_tilde(oparg_T *oap)
/// @param length is rounded up to include the whole last multi-byte character.
/// Also works correctly when the number of bytes changes.
///
-/// @return TRUE if some character was changed.
+/// @return true if some character was changed.
static int swapchars(int op_type, pos_T *pos, int length)
FUNC_ATTR_NONNULL_ALL
{
@@ -2263,7 +2252,7 @@ void op_insert(oparg_T *oap, long count1)
// vis block is still marked. Get rid of it now.
curwin->w_cursor.lnum = oap->start.lnum;
- update_screen(INVERTED);
+ update_screen(UPD_INVERTED);
if (oap->motion_type == kMTBlockWise) {
// When 'virtualedit' is used, need to insert the extra spaces before
@@ -2283,7 +2272,7 @@ void op_insert(oparg_T *oap, long count1)
coladvance_force(oap->op_type == OP_APPEND
? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
- --curwin->w_cursor.col;
+ curwin->w_cursor.col--;
}
curwin->w_ve_flags = old_ve_flags;
}
@@ -2304,10 +2293,10 @@ void op_insert(oparg_T *oap, long count1)
if (oap->motion_type == kMTBlockWise
&& curwin->w_cursor.coladd == 0) {
// Move the cursor to the character right of the block.
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
while (*get_cursor_pos_ptr() != NUL
&& (curwin->w_cursor.col < bd.textcol + bd.textlen)) {
- ++curwin->w_cursor.col;
+ curwin->w_cursor.col++;
}
if (bd.is_short && !bd.is_MAX) {
// First line was too short, make it longer and adjust the
@@ -2413,7 +2402,7 @@ void op_insert(oparg_T *oap, long count1)
if (oap->op_type == OP_APPEND) {
pre_textlen += bd2.textlen - bd.textlen;
if (bd2.endspaces) {
- --bd2.textlen;
+ bd2.textlen--;
}
}
bd.textcol = bd2.textcol;
@@ -2464,7 +2453,7 @@ void op_insert(oparg_T *oap, long count1)
/// handle a change operation
///
-/// @return TRUE if edit() returns because of a CTRL-O command
+/// @return true if edit() returns because of a CTRL-O command
int op_change(oparg_T *oap)
{
colnr_T l;
@@ -2490,10 +2479,10 @@ int op_change(oparg_T *oap)
// save for undo
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
if (u_save_cursor() == FAIL) {
- return FALSE;
+ return false;
}
} else if (op_delete(oap) == FAIL) {
- return FALSE;
+ return false;
}
if ((l > curwin->w_cursor.col) && !LINEEMPTY(curwin->w_cursor.lnum)
@@ -2511,7 +2500,7 @@ int op_change(oparg_T *oap)
}
firstline = ml_get(oap->start.lnum);
pre_textlen = (long)STRLEN(firstline);
- pre_indent = (long)getwhitecols(firstline);
+ pre_indent = (long)getwhitecols((char *)firstline);
bd.textcol = curwin->w_cursor.col;
}
@@ -2519,7 +2508,7 @@ int op_change(oparg_T *oap)
fix_indent();
}
- retval = edit(NUL, FALSE, (linenr_T)1);
+ retval = edit(NUL, false, (linenr_T)1);
/*
* In Visual block mode, handle copying the new text to all lines of the
@@ -2532,7 +2521,7 @@ int op_change(oparg_T *oap)
// the indent, exclude that indent change from the inserted text.
firstline = ml_get(oap->start.lnum);
if (bd.textcol > (colnr_T)pre_indent) {
- long new_indent = (long)getwhitecols(firstline);
+ long new_indent = (long)getwhitecols((char *)firstline);
pre_textlen += new_indent - pre_indent;
bd.textcol += (colnr_T)(new_indent - pre_indent);
@@ -2961,7 +2950,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
int delcount;
int incr = 0;
struct block_def bd;
- char_u **y_array = NULL;
+ char **y_array = NULL;
linenr_T nr_lines = 0;
pos_T new_cursor;
int indent;
@@ -2970,7 +2959,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
bool first_indent = true;
int lendiff = 0;
pos_T old_pos;
- char_u *insert_string = NULL;
+ char *insert_string = NULL;
bool allocated = false;
long cnt;
const pos_T orig_start = curbuf->b_op_start;
@@ -3093,10 +3082,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
* Loop twice: count the number of lines and save them. */
for (;;) {
y_size = 0;
- ptr = insert_string;
+ ptr = (char_u *)insert_string;
while (ptr != NULL) {
if (y_array != NULL) {
- y_array[y_size] = ptr;
+ y_array[y_size] = (char *)ptr;
}
y_size++;
ptr = (char_u *)vim_strchr((char *)ptr, '\n');
@@ -3104,7 +3093,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_array != NULL) {
*ptr = NUL;
}
- ++ptr;
+ ptr++;
// A trailing '\n' makes the register linewise.
if (*ptr == NUL) {
y_type = kMTLineWise;
@@ -3115,7 +3104,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (y_array != NULL) {
break;
}
- y_array = (char_u **)xmalloc(y_size * sizeof(char_u *));
+ y_array = xmalloc(y_size * sizeof(char_u *));
}
} else {
y_size = 1; // use fake one-line yank register
@@ -3132,7 +3121,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
y_type = reg->y_type;
y_width = reg->y_width;
y_size = reg->y_size;
- y_array = (char_u **)reg->y_array;
+ y_array = reg->y_array;
}
if (curbuf->terminal) {
@@ -3308,12 +3297,19 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// get the old line and advance to the position to insert at
oldp = get_cursor_line_ptr();
oldlen = STRLEN(oldp);
- for (ptr = oldp; vcol < col && *ptr;) {
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, curwin->w_cursor.lnum, 0,
+ oldp, oldp);
+
+ while (cts.cts_vcol < col && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- incr = lbr_chartabsize_adv(oldp, &ptr, vcol);
- vcol += incr;
+ incr = lbr_chartabsize_adv(&cts);
+ cts.cts_vcol += incr;
}
+ vcol = cts.cts_vcol;
+ ptr = (char_u *)cts.cts_ptr;
bd.textcol = (colnr_T)(ptr - oldp);
+ clear_chartabsize_arg(&cts);
shortline = (vcol < col) || (vcol == col && !*ptr);
@@ -3322,7 +3318,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
} else if (vcol > col) {
bd.endspaces = vcol - col;
bd.startspaces = incr - bd.endspaces;
- --bd.textcol;
+ bd.textcol--;
delcount = 1;
bd.textcol -= utf_head_off(oldp, oldp + bd.textcol);
if (oldp[bd.textcol] != TAB) {
@@ -3340,9 +3336,14 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
// calculate number of spaces required to fill right side of
// block
spaces = y_width + 1;
+ init_chartabsize_arg(&cts, curwin, 0, 0,
+ (char_u *)y_array[i], (char_u *)y_array[i]);
for (int j = 0; j < yanklen; j++) {
- spaces -= lbr_chartabsize(NULL, &y_array[i][j], 0);
+ spaces -= lbr_chartabsize(&cts);
+ cts.cts_ptr++;
+ cts.cts_vcol = 0;
}
+ clear_chartabsize_arg(&cts);
if (spaces < 0) {
spaces = 0;
}
@@ -3395,7 +3396,7 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, bd.textcol,
delcount, (int)totlen + lines_appended, kExtmarkUndo);
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
if (i == 0) {
curwin->w_cursor.col += bd.startspaces;
}
@@ -3442,12 +3443,9 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
}
}
curbuf->b_op_start = curwin->w_cursor;
- }
- /*
- * Line mode: BACKWARD is the same as FORWARD on the previous line
- */
- else if (dir == BACKWARD) {
- --lnum;
+ } else if (dir == BACKWARD) {
+ // Line mode: BACKWARD is the same as FORWARD on the previous line
+ lnum--;
}
new_cursor = curwin->w_cursor;
@@ -3580,13 +3578,13 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
for (; i < y_size; i++) {
if ((y_type != kMTCharWise || i < y_size - 1)) {
- if (ml_append(lnum, (char *)y_array[i], (colnr_T)0, false) == FAIL) {
+ if (ml_append(lnum, y_array[i], (colnr_T)0, false) == FAIL) {
goto error;
}
new_lnum++;
}
lnum++;
- ++nr_lines;
+ nr_lines++;
if (flags & PUT_FIXINDENT) {
old_pos = curwin->w_cursor;
curwin->w_cursor.lnum = lnum;
@@ -3670,8 +3668,8 @@ error:
if (col > 1) {
curbuf->b_op_end.col = col - 1;
if (len > 0) {
- curbuf->b_op_end.col -= utf_head_off(y_array[y_size - 1],
- y_array[y_size - 1] + len - 1);
+ curbuf->b_op_end.col -= utf_head_off((char_u *)y_array[y_size - 1],
+ (char_u *)y_array[y_size - 1] + len - 1);
}
} else {
curbuf->b_op_end.col = 0;
@@ -3702,7 +3700,7 @@ error:
// put cursor on first non-blank in first inserted line
curwin->w_cursor.col = 0;
if (dir == FORWARD) {
- ++curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum++;
}
beginline(BL_WHITE | BL_FIX);
} else { // put cursor on first inserted character
@@ -3712,7 +3710,7 @@ error:
}
msgmore(nr_lines);
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
end:
if (cmdmod.cmod_flags & CMOD_LOCKMARKS) {
@@ -3726,7 +3724,7 @@ end:
xfree(y_array);
}
- VIsual_active = FALSE;
+ VIsual_active = false;
// If the cursor is past the end of the line put it at the end.
adjust_cursor_eol();
@@ -3755,7 +3753,7 @@ void adjust_cursor_eol(void)
}
}
-/// @return TRUE if lines starting with '#' should be left aligned.
+/// @return true if lines starting with '#' should be left aligned.
int preprocs_left(void)
{
return ((curbuf->b_p_si && !curbuf->b_p_cin)
@@ -3841,7 +3839,7 @@ void ex_display(exarg_T *eap)
bool do_show = false;
for (size_t j = 0; !do_show && j < yb->y_size; j++) {
- do_show = !message_filtered((char_u *)yb->y_array[j]);
+ do_show = !message_filtered(yb->y_array[j]);
}
if (do_show || yb->y_size == 0) {
@@ -3862,7 +3860,7 @@ void ex_display(exarg_T *eap)
for (p = (char_u *)yb->y_array[j];
*p != NUL && (n -= ptr2cells((char *)p)) >= 0; p++) { // -V1019
clen = utfc_ptr2len((char *)p);
- msg_outtrans_len(p, clen);
+ msg_outtrans_len((char *)p, clen);
p += clen - 1;
}
}
@@ -3878,7 +3876,7 @@ void ex_display(exarg_T *eap)
// display last inserted text
if ((p = get_last_insert()) != NULL
&& (arg == NULL || vim_strchr((char *)arg, '.') != NULL) && !got_int
- && !message_filtered(p)) {
+ && !message_filtered((char *)p)) {
msg_puts("\n c \". ");
dis_msg(p, true);
}
@@ -3887,13 +3885,13 @@ void ex_display(exarg_T *eap)
if (last_cmdline != NULL && (arg == NULL || vim_strchr((char *)arg, ':') != NULL)
&& !got_int && !message_filtered(last_cmdline)) {
msg_puts("\n c \": ");
- dis_msg(last_cmdline, false);
+ dis_msg((char_u *)last_cmdline, false);
}
// display current file name
if (curbuf->b_fname != NULL
&& (arg == NULL || vim_strchr((char *)arg, '%') != NULL) && !got_int
- && !message_filtered((char_u *)curbuf->b_fname)) {
+ && !message_filtered(curbuf->b_fname)) {
msg_puts("\n c \"% ");
dis_msg((char_u *)curbuf->b_fname, false);
}
@@ -3903,7 +3901,7 @@ void ex_display(exarg_T *eap)
char *fname;
linenr_T dummy;
- if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered((char_u *)fname)) {
+ if (buflist_name_nr(0, &fname, &dummy) != FAIL && !message_filtered(fname)) {
msg_puts("\n c \"# ");
dis_msg((char_u *)fname, false);
}
@@ -3912,14 +3910,14 @@ void ex_display(exarg_T *eap)
// display last search pattern
if (last_search_pat() != NULL
&& (arg == NULL || vim_strchr((char *)arg, '/') != NULL) && !got_int
- && !message_filtered(last_search_pat())) {
+ && !message_filtered((char *)last_search_pat())) {
msg_puts("\n c \"/ ");
dis_msg(last_search_pat(), false);
}
// display last used expression
if (expr_line != NULL && (arg == NULL || vim_strchr((char *)arg, '=') != NULL)
- && !got_int && !message_filtered(expr_line)) {
+ && !got_int && !message_filtered((char *)expr_line)) {
msg_puts("\n c \"= ");
dis_msg(expr_line, false);
}
@@ -3940,10 +3938,10 @@ static void dis_msg(const char_u *p, bool skip_esc)
&& !(*p == ESC && skip_esc && *(p + 1) == NUL)
&& (n -= ptr2cells((char *)p)) >= 0) {
if ((l = utfc_ptr2len((char *)p)) > 1) {
- msg_outtrans_len(p, l);
+ msg_outtrans_len((char *)p, l);
p += l;
} else {
- msg_outtrans_len(p++, 1);
+ msg_outtrans_len((char *)p++, 1);
}
}
os_breakcheck();
@@ -3962,9 +3960,9 @@ static void dis_msg(const char_u *p, bool skip_esc)
/// comment.
char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_comment)
{
- char_u *comment_flags = NULL;
+ char *comment_flags = NULL;
int lead_len;
- int leader_offset = get_last_leader_offset((char *)line, (char **)&comment_flags);
+ int leader_offset = get_last_leader_offset((char *)line, &comment_flags);
*is_comment = false;
if (leader_offset != -1) {
@@ -3986,7 +3984,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
return line;
}
- lead_len = get_leader_len((char *)line, (char **)&comment_flags, false, include_space);
+ lead_len = get_leader_len((char *)line, &comment_flags, false, include_space);
if (lead_len == 0) {
return line;
@@ -4001,7 +3999,7 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
|| *comment_flags == ':') {
break;
}
- ++comment_flags;
+ comment_flags++;
}
// If we found a colon, it means that we are not processing a line
@@ -4015,8 +4013,8 @@ char_u *skip_comment(char_u *line, bool process, bool include_space, bool *is_co
}
/// @param count number of lines (minimal 2) to join at cursor position.
-/// @param save_undo when TRUE, save lines for undo first.
-/// @param use_formatoptions set to FALSE when e.g. processing backspace and comment
+/// @param save_undo when true, save lines for undo first.
+/// @param use_formatoptions set to false when e.g. processing backspace and comment
/// leaders should not be removed.
/// @param setmark when true, sets the '[ and '] mark, else, the caller is expected
/// to set those marks.
@@ -4037,7 +4035,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
colnr_T col = 0;
int ret = OK;
int *comments = NULL;
- int remove_comments = (use_formatoptions == TRUE)
+ int remove_comments = (use_formatoptions == true)
&& has_format_option(FO_REMOVE_COMS);
bool prev_was_comment = false;
assert(count >= 1);
@@ -4092,11 +4090,11 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
if (endcurr1 == ' ') {
endcurr1 = endcurr2;
} else {
- ++spaces[t];
+ spaces[t]++;
}
// Extra space when 'joinspaces' set and line ends in '.', '?', or '!'.
if (p_js && (endcurr1 == '.' || endcurr1 == '?' || endcurr1 == '!')) {
- ++spaces[t];
+ spaces[t]++;
}
}
}
@@ -4212,7 +4210,7 @@ int do_join(size_t count, int insert_space, int save_undo, int use_formatoptions
check_cursor_col();
curwin->w_cursor.coladd = 0;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
theend:
xfree(spaces);
@@ -4222,532 +4220,6 @@ theend:
return ret;
}
-/// @return TRUE if the two comment leaders given are the same.
-///
-/// @param lnum The first line. White-space is ignored.
-///
-/// @note the whole of 'leader1' must match 'leader2_len' characters from 'leader2'.
-static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, int leader2_len,
- char_u *leader2_flags)
-{
- int idx1 = 0, idx2 = 0;
- char_u *p;
- char_u *line1;
- char_u *line2;
-
- if (leader1_len == 0) {
- return leader2_len == 0;
- }
-
- /*
- * If first leader has 'f' flag, the lines can be joined only if the
- * second line does not have a leader.
- * If first leader has 'e' flag, the lines can never be joined.
- * If first leader has 's' flag, the lines can only be joined if there is
- * some text after it and the second line has the 'm' flag.
- */
- if (leader1_flags != NULL) {
- for (p = leader1_flags; *p && *p != ':'; ++p) {
- if (*p == COM_FIRST) {
- return leader2_len == 0;
- }
- if (*p == COM_END) {
- return FALSE;
- }
- if (*p == COM_START) {
- if (*(ml_get(lnum) + leader1_len) == NUL) {
- return FALSE;
- }
- if (leader2_flags == NULL || leader2_len == 0) {
- return FALSE;
- }
- for (p = leader2_flags; *p && *p != ':'; ++p) {
- if (*p == COM_MIDDLE) {
- return TRUE;
- }
- }
- return FALSE;
- }
- }
- }
-
- /*
- * Get current line and next line, compare the leaders.
- * The first line has to be saved, only one line can be locked at a time.
- */
- line1 = vim_strsave(ml_get(lnum));
- for (idx1 = 0; ascii_iswhite(line1[idx1]); idx1++) {}
- line2 = ml_get(lnum + 1);
- for (idx2 = 0; idx2 < leader2_len; ++idx2) {
- if (!ascii_iswhite(line2[idx2])) {
- if (line1[idx1++] != line2[idx2]) {
- break;
- }
- } else {
- while (ascii_iswhite(line1[idx1])) {
- ++idx1;
- }
- }
- }
- xfree(line1);
-
- return idx2 == leader2_len && idx1 == leader1_len;
-}
-
-/// Implementation of the format operator 'gq'.
-///
-/// @param keep_cursor keep cursor on same text char
-static void op_format(oparg_T *oap, int keep_cursor)
-{
- linenr_T old_line_count = curbuf->b_ml.ml_line_count;
-
- // Place the cursor where the "gq" or "gw" command was given, so that "u"
- // can put it back there.
- curwin->w_cursor = oap->cursor_start;
-
- if (u_save((linenr_T)(oap->start.lnum - 1),
- (linenr_T)(oap->end.lnum + 1)) == FAIL) {
- return;
- }
- curwin->w_cursor = oap->start;
-
- if (oap->is_VIsual) {
- // When there is no change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
- }
-
- if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
- // Set '[ mark at the start of the formatted area
- curbuf->b_op_start = oap->start;
- }
-
- // For "gw" remember the cursor position and put it back below (adjusted
- // for joined and split lines).
- if (keep_cursor) {
- saved_cursor = oap->cursor_start;
- }
-
- format_lines((linenr_T)oap->line_count, keep_cursor);
-
- /*
- * Leave the cursor at the first non-blank of the last formatted line.
- * If the cursor was moved one line back (e.g. with "Q}") go to the next
- * line, so "." will do the next lines.
- */
- if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- ++curwin->w_cursor.lnum;
- }
- beginline(BL_WHITE | BL_FIX);
- old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
- msgmore(old_line_count);
-
- if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
- // put '] mark on the end of the formatted area
- curbuf->b_op_end = curwin->w_cursor;
- }
-
- if (keep_cursor) {
- curwin->w_cursor = saved_cursor;
- saved_cursor.lnum = 0;
-
- // formatting may have made the cursor position invalid
- check_cursor();
- }
-
- if (oap->is_VIsual) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_old_cursor_lnum != 0) {
- // When lines have been inserted or deleted, adjust the end of
- // the Visual area to be redrawn.
- if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) {
- wp->w_old_cursor_lnum += old_line_count;
- } else {
- wp->w_old_visual_lnum += old_line_count;
- }
- }
- }
- }
-}
-
-/// Implementation of the format operator 'gq' for when using 'formatexpr'.
-static void op_formatexpr(oparg_T *oap)
-{
- if (oap->is_VIsual) {
- // When there is no change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
- }
-
- if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) {
- // As documented: when 'formatexpr' returns non-zero fall back to
- // internal formatting.
- op_format(oap, false);
- }
-}
-
-/// @param c character to be inserted
-int fex_format(linenr_T lnum, long count, int c)
-{
- int use_sandbox = was_set_insecurely(curwin, "formatexpr", OPT_LOCAL);
- int r;
- char_u *fex;
-
- /*
- * Set v:lnum to the first line number and v:count to the number of lines.
- * Set v:char to the character to be inserted (can be NUL).
- */
- set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
- set_vim_var_nr(VV_COUNT, (varnumber_T)count);
- set_vim_var_char(c);
-
- // Make a copy, the option could be changed while calling it.
- fex = vim_strsave(curbuf->b_p_fex);
- // Evaluate the function.
- if (use_sandbox) {
- sandbox++;
- }
- r = (int)eval_to_number((char *)fex);
- if (use_sandbox) {
- sandbox--;
- }
-
- set_vim_var_string(VV_CHAR, NULL, -1);
- xfree(fex);
-
- return r;
-}
-
-/// @param line_count number of lines to format, starting at the cursor position.
-/// when negative, format until the end of the paragraph.
-///
-/// Lines after the cursor line are saved for undo, caller must have saved the
-/// first line.
-///
-/// @param avoid_fex don't use 'formatexpr'
-void format_lines(linenr_T line_count, int avoid_fex)
-{
- bool is_not_par; // current line not part of parag.
- bool next_is_not_par; // next line not part of paragraph
- bool is_end_par; // at end of paragraph
- bool prev_is_end_par = false; // prev. line not part of parag.
- bool next_is_start_par = false;
- int leader_len = 0; // leader len of current line
- int next_leader_len; // leader len of next line
- char_u *leader_flags = NULL; // flags for leader of current line
- char_u *next_leader_flags = NULL; // flags for leader of next line
- bool advance = true;
- int second_indent = -1; // indent for second line (comment aware)
- bool first_par_line = true;
- int smd_save;
- long count;
- bool need_set_indent = true; // set indent of next paragraph
- linenr_T first_line = curwin->w_cursor.lnum;
- bool force_format = false;
- const int old_State = State;
-
- // length of a line to force formatting: 3 * 'tw'
- const int max_len = comp_textwidth(true) * 3;
-
- // check for 'q', '2' and '1' in 'formatoptions'
- const bool do_comments = has_format_option(FO_Q_COMS); // format comments
- int do_comments_list = 0; // format comments with 'n' or '2'
- const bool do_second_indent = has_format_option(FO_Q_SECOND);
- const bool do_number_indent = has_format_option(FO_Q_NUMBER);
- const bool do_trail_white = has_format_option(FO_WHITE_PAR);
-
- // Get info about the previous and current line.
- if (curwin->w_cursor.lnum > 1) {
- is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
- &leader_len, &leader_flags, do_comments);
- } else {
- is_not_par = true;
- }
- next_is_not_par = fmt_check_par(curwin->w_cursor.lnum,
- &next_leader_len, &next_leader_flags, do_comments
- );
- is_end_par = (is_not_par || next_is_not_par);
- if (!is_end_par && do_trail_white) {
- is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
- }
-
- curwin->w_cursor.lnum--;
- for (count = line_count; count != 0 && !got_int; --count) {
- /*
- * Advance to next paragraph.
- */
- if (advance) {
- curwin->w_cursor.lnum++;
- prev_is_end_par = is_end_par;
- is_not_par = next_is_not_par;
- leader_len = next_leader_len;
- leader_flags = next_leader_flags;
- }
-
- /*
- * The last line to be formatted.
- */
- if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
- next_is_not_par = true;
- next_leader_len = 0;
- next_leader_flags = NULL;
- } else {
- next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1,
- &next_leader_len, &next_leader_flags, do_comments
- );
- if (do_number_indent) {
- next_is_start_par =
- (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
- }
- }
- advance = true;
- is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
- if (!is_end_par && do_trail_white) {
- is_end_par = !ends_in_white(curwin->w_cursor.lnum);
- }
-
- /*
- * Skip lines that are not in a paragraph.
- */
- if (is_not_par) {
- if (line_count < 0) {
- break;
- }
- } else {
- /*
- * For the first line of a paragraph, check indent of second line.
- * Don't do this for comments and empty lines.
- */
- if (first_par_line
- && (do_second_indent || do_number_indent)
- && prev_is_end_par
- && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
- if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
- if (leader_len == 0 && next_leader_len == 0) {
- // no comment found
- second_indent =
- get_indent_lnum(curwin->w_cursor.lnum + 1);
- } else {
- second_indent = next_leader_len;
- do_comments_list = 1;
- }
- } else if (do_number_indent) {
- if (leader_len == 0 && next_leader_len == 0) {
- // no comment found
- second_indent =
- get_number_indent(curwin->w_cursor.lnum);
- } else {
- // get_number_indent() is now "comment aware"...
- second_indent =
- get_number_indent(curwin->w_cursor.lnum);
- do_comments_list = 1;
- }
- }
- }
-
- /*
- * When the comment leader changes, it's the end of the paragraph.
- */
- if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
- || !same_leader(curwin->w_cursor.lnum,
- leader_len, leader_flags,
- next_leader_len,
- next_leader_flags)) {
- // Special case: If the next line starts with a line comment
- // and this line has a line comment after some text, the
- // paragraph doesn't really end.
- if (next_leader_flags == NULL
- || STRNCMP(next_leader_flags, "://", 3) != 0
- || check_linecomment(get_cursor_line_ptr()) == MAXCOL) {
- is_end_par = true;
- }
- }
-
- /*
- * If we have got to the end of a paragraph, or the line is
- * getting long, format it.
- */
- if (is_end_par || force_format) {
- if (need_set_indent) {
- int indent = 0; // amount of indent needed
-
- // Replace indent in first line of a paragraph with minimal
- // number of tabs and spaces, according to current options.
- // For the very first formatted line keep the current
- // indent.
- if (curwin->w_cursor.lnum == first_line) {
- indent = get_indent();
- } else if (curbuf->b_p_lisp) {
- indent = get_lisp_indent();
- } else {
- if (cindent_on()) {
- indent = *curbuf->b_p_inde != NUL ? get_expr_indent() : get_c_indent();
- } else {
- indent = get_indent();
- }
- }
- (void)set_indent(indent, SIN_CHANGED);
- }
-
- // put cursor on last non-space
- State = MODE_NORMAL; // don't go past end-of-line
- coladvance(MAXCOL);
- while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) {
- dec_cursor();
- }
-
- // do the formatting, without 'showmode'
- State = MODE_INSERT; // for open_line()
- smd_save = p_smd;
- p_smd = FALSE;
- insertchar(NUL, INSCHAR_FORMAT
- + (do_comments ? INSCHAR_DO_COM : 0)
- + (do_comments && do_comments_list
- ? INSCHAR_COM_LIST : 0)
- + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
- State = old_State;
- p_smd = smd_save;
- second_indent = -1;
- // at end of par.: need to set indent of next par.
- need_set_indent = is_end_par;
- if (is_end_par) {
- // When called with a negative line count, break at the
- // end of the paragraph.
- if (line_count < 0) {
- break;
- }
- first_par_line = true;
- }
- force_format = false;
- }
-
- /*
- * When still in same paragraph, join the lines together. But
- * first delete the leader from the second line.
- */
- if (!is_end_par) {
- advance = false;
- curwin->w_cursor.lnum++;
- curwin->w_cursor.col = 0;
- if (line_count < 0 && u_save_cursor() == FAIL) {
- break;
- }
- if (next_leader_len > 0) {
- (void)del_bytes(next_leader_len, false, false);
- mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
- (long)-next_leader_len, 0);
- } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
- int indent = (int)getwhitecols_curline();
-
- if (indent > 0) {
- (void)del_bytes(indent, false, false);
- mark_col_adjust(curwin->w_cursor.lnum,
- (colnr_T)0, 0L, (long)-indent, 0);
- }
- }
- curwin->w_cursor.lnum--;
- if (do_join(2, TRUE, FALSE, FALSE, false) == FAIL) {
- beep_flush();
- break;
- }
- first_par_line = false;
- // If the line is getting long, format it next time
- if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) {
- force_format = true;
- } else {
- force_format = false;
- }
- }
- }
- line_breakcheck();
- }
-}
-
-/// @return TRUE if line "lnum" ends in a white character.
-static int ends_in_white(linenr_T lnum)
-{
- char_u *s = ml_get(lnum);
- size_t l;
-
- if (*s == NUL) {
- return FALSE;
- }
- l = STRLEN(s) - 1;
- return ascii_iswhite(s[l]);
-}
-
-/// Blank lines, and lines containing only the comment leader, are left
-/// untouched by the formatting. The function returns TRUE in this
-/// case. It also returns TRUE when a line starts with the end of a comment
-/// ('e' in comment flags), so that this line is skipped, and not joined to the
-/// previous line. A new paragraph starts after a blank line, or when the
-/// comment leader changes.
-static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, int do_comments)
-{
- char_u *flags = NULL; // init for GCC
- char_u *ptr;
-
- ptr = ml_get(lnum);
- if (do_comments) {
- *leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
- } else {
- *leader_len = 0;
- }
-
- if (*leader_len > 0) {
- /*
- * Search for 'e' flag in comment leader flags.
- */
- flags = *leader_flags;
- while (*flags && *flags != ':' && *flags != COM_END) {
- ++flags;
- }
- }
-
- return *skipwhite((char *)ptr + *leader_len) == NUL
- || (*leader_len > 0 && *flags == COM_END)
- || startPS(lnum, NUL, FALSE);
-}
-
-/// Used for auto-formatting.
-///
-/// @return TRUE when a paragraph starts in line "lnum".
-/// FALSE when the previous line is in the same paragraph.
-int paragraph_start(linenr_T lnum)
-{
- char_u *p;
- int leader_len = 0; // leader len of current line
- char_u *leader_flags = NULL; // flags for leader of current line
- int next_leader_len = 0; // leader len of next line
- char_u *next_leader_flags = NULL; // flags for leader of next line
-
- if (lnum <= 1) {
- return TRUE; // start of the file
- }
- p = ml_get(lnum - 1);
- if (*p == NUL) {
- return TRUE; // after empty line
- }
- const bool do_comments = has_format_option(FO_Q_COMS); // format comments
- if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
- return true; // after non-paragraph line
- }
-
- if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
- return true; // "lnum" is not a paragraph line
- }
-
- if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) {
- return TRUE; // missing trailing space in previous line.
- }
- if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) {
- return TRUE; // numbered item starts in "lnum".
- }
- if (!same_leader(lnum - 1, leader_len, leader_flags,
- next_leader_len, next_leader_flags)) {
- return TRUE; // change of comment leader.
- }
- return FALSE;
-}
-
/// prepare a few things for block mode yank/delete/tilde
///
/// for delete:
@@ -4784,22 +4256,28 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
bdp->start_char_vcols = 0;
line = ml_get(lnum);
- pstart = line;
prev_pstart = line;
- while (bdp->start_vcol < oap->start_vcol && *pstart) {
+
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, lnum, bdp->start_vcol, line, line);
+ while (cts.cts_vcol < oap->start_vcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- incr = lbr_chartabsize(line, pstart, bdp->start_vcol);
- bdp->start_vcol += incr;
- if (ascii_iswhite(*pstart)) {
+ incr = lbr_chartabsize(&cts);
+ cts.cts_vcol += incr;
+ if (ascii_iswhite(*cts.cts_ptr)) {
bdp->pre_whitesp += incr;
bdp->pre_whitesp_c++;
} else {
bdp->pre_whitesp = 0;
bdp->pre_whitesp_c = 0;
}
- prev_pstart = pstart;
- MB_PTR_ADV(pstart);
+ prev_pstart = (char_u *)cts.cts_ptr;
+ MB_PTR_ADV(cts.cts_ptr);
}
+ bdp->start_vcol = cts.cts_vcol;
+ pstart = (char_u *)cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
+
bdp->start_char_vcols = incr;
if (bdp->start_vcol < oap->start_vcol) { // line too short
bdp->end_vcol = bdp->start_vcol;
@@ -4835,13 +4313,19 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, bool
}
}
} else {
+ init_chartabsize_arg(&cts, curwin, lnum, bdp->end_vcol,
+ line, pend);
prev_pend = pend;
- while (bdp->end_vcol <= oap->end_vcol && *pend != NUL) {
+ while (cts.cts_vcol <= oap->end_vcol && *cts.cts_ptr != NUL) {
// Count a tab for what it's worth (if list mode not on)
- prev_pend = pend;
- incr = lbr_chartabsize_adv(line, &pend, bdp->end_vcol);
- bdp->end_vcol += incr;
+ prev_pend = (char_u *)cts.cts_ptr;
+ incr = lbr_chartabsize_adv(&cts);
+ cts.cts_vcol += incr;
}
+ bdp->end_vcol = cts.cts_vcol;
+ pend = (char_u *)cts.cts_ptr;
+ clear_chartabsize_arg(&cts);
+
if (bdp->end_vcol <= oap->end_vcol
&& (!is_del
|| oap->op_type == OP_APPEND
@@ -4966,7 +4450,7 @@ void op_addsub(oparg_T *oap, linenr_T Prenum1, bool g_cmd)
if (!change_cnt && oap->is_VIsual) {
// No change: need to remove the Visual selection
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
// Set '[ mark if something changed. Keep the last end
@@ -5014,12 +4498,12 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
pos_T endpos;
colnr_T save_coladd = 0;
- const bool do_hex = vim_strchr((char *)curbuf->b_p_nf, 'x') != NULL; // "heX"
- const bool do_oct = vim_strchr((char *)curbuf->b_p_nf, 'o') != NULL; // "Octal"
- const bool do_bin = vim_strchr((char *)curbuf->b_p_nf, 'b') != NULL; // "Bin"
- const bool do_alpha = vim_strchr((char *)curbuf->b_p_nf, 'p') != NULL; // "alPha"
+ const bool do_hex = vim_strchr(curbuf->b_p_nf, 'x') != NULL; // "heX"
+ const bool do_oct = vim_strchr(curbuf->b_p_nf, 'o') != NULL; // "Octal"
+ const bool do_bin = vim_strchr(curbuf->b_p_nf, 'b') != NULL; // "Bin"
+ const bool do_alpha = vim_strchr(curbuf->b_p_nf, 'p') != NULL; // "alPha"
// "Unsigned"
- const bool do_unsigned = vim_strchr((char *)curbuf->b_p_nf, 'u') != NULL;
+ const bool do_unsigned = vim_strchr(curbuf->b_p_nf, 'u') != NULL;
if (virtual_active()) {
save_coladd = pos->coladd;
@@ -5318,7 +4802,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
*ptr = NUL;
STRCAT(buf1, buf2);
- ins_str(buf1); // insert the new number
+ ins_str((char *)buf1); // insert the new number
endpos = curwin->w_cursor;
if (curwin->w_cursor.col) {
curwin->w_cursor.col--;
@@ -5457,16 +4941,16 @@ void *get_reg_contents(int regname, int flags)
return NULL;
}
- char_u *retval;
+ char *retval;
bool allocated;
if (get_spec_reg(regname, &retval, &allocated, false)) {
if (retval == NULL) {
return NULL;
}
if (allocated) {
- return get_reg_wrap_one_line(retval, flags);
+ return get_reg_wrap_one_line((char_u *)retval, flags);
}
- return get_reg_wrap_one_line(vim_strsave(retval), flags);
+ return get_reg_wrap_one_line(vim_strsave((char_u *)retval), flags);
}
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
@@ -5557,11 +5041,11 @@ void write_reg_contents(int name, const char_u *str, ssize_t len, int must_appen
write_reg_contents_ex(name, str, len, must_append, kMTUnknown, 0L);
}
-void write_reg_contents_lst(int name, char_u **strings, bool must_append, MotionType yank_type,
+void write_reg_contents_lst(int name, char **strings, bool must_append, MotionType yank_type,
colnr_T block_len)
{
if (name == '/' || name == '=') {
- char_u *s = strings[0];
+ char_u *s = (char_u *)strings[0];
if (strings[0] == NULL) {
s = (char_u *)"";
} else if (strings[1] != NULL) {
@@ -5583,7 +5067,7 @@ void write_reg_contents_lst(int name, char_u **strings, bool must_append, Motion
return;
}
- str_to_reg(reg, yank_type, (char_u *)strings, STRLEN((char_u *)strings),
+ str_to_reg(reg, yank_type, (char *)strings, STRLEN((char_u *)strings),
block_len, true);
finish_write_reg(name, reg, old_y_previous);
}
@@ -5615,7 +5099,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
// Special case: '/' search pattern
if (name == '/') {
- set_last_search_pat(str, RE_SEARCH, TRUE, TRUE);
+ set_last_search_pat(str, RE_SEARCH, true, true);
return;
}
@@ -5671,7 +5155,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
if (!(reg = init_write_reg(name, &old_y_previous, must_append))) {
return;
}
- str_to_reg(reg, yank_type, str, (size_t)len, block_len, false);
+ str_to_reg(reg, yank_type, (char *)str, (size_t)len, block_len, false);
finish_write_reg(name, reg, old_y_previous);
}
@@ -5685,7 +5169,7 @@ void write_reg_contents_ex(int name, const char_u *str, ssize_t len, bool must_a
/// @param len length of the string (Ignored when str_list=true.)
/// @param blocklen width of visual block, or -1 for "I don't know."
/// @param str_list True if str is `char_u **`.
-static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str, size_t len,
+static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char *str, size_t len,
colnr_T blocklen, bool str_list)
FUNC_ATTR_NONNULL_ALL
{
@@ -5705,18 +5189,18 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
// Count the number of lines within the string
if (str_list) {
- for (char_u **ss = (char_u **)str; *ss != NULL; ++ss) {
+ for (char_u **ss = (char_u **)str; *ss != NULL; ss++) {
newlines++;
}
} else {
newlines = memcnt(str, '\n', len);
if (yank_type == kMTCharWise || len == 0 || str[len - 1] != '\n') {
extraline = 1;
- ++newlines; // count extra newline at the end
+ newlines++; // count extra newline at the end
}
if (y_ptr->y_size > 0 && y_ptr->y_type == kMTCharWise) {
append = true;
- --newlines; // uncount newline when appending first line
+ newlines--; // uncount newline when appending first line
}
}
@@ -5727,9 +5211,8 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
}
// Grow the register array to hold the pointers to the new lines.
- char_u **pp = xrealloc(y_ptr->y_array,
- (y_ptr->y_size + newlines) * sizeof(char_u *));
- y_ptr->y_array = (char **)pp;
+ char **pp = xrealloc(y_ptr->y_array, (y_ptr->y_size + newlines) * sizeof(char_u *));
+ y_ptr->y_array = pp;
size_t lnum = y_ptr->y_size; // The current line number.
@@ -5738,7 +5221,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
// Find the end of each line and save it into the array.
if (str_list) {
- for (char_u **ss = (char_u **)str; *ss != NULL; ++ss, ++lnum) {
+ for (char_u **ss = (char_u **)str; *ss != NULL; ss++, lnum++) {
size_t ss_len = STRLEN(*ss);
pp[lnum] = xmemdupz(*ss, ss_len);
if (ss_len > maxlen) {
@@ -5747,7 +5230,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
}
} else {
size_t line_len;
- for (const char_u *start = str, *end = str + len;
+ for (const char_u *start = (char_u *)str, *end = (char_u *)str + len;
start < end + extraline;
start += line_len + 1, lnum++) {
assert(end - start >= 0);
@@ -5770,7 +5253,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
xfree(pp[lnum]);
append = false; // only first line is appended
}
- pp[lnum] = (char_u *)s;
+ pp[lnum] = s;
// Convert NULs to '\n' to prevent truncation.
memchrsub(pp[lnum], NUL, '\n', s_len);
@@ -5789,7 +5272,7 @@ static void str_to_reg(yankreg_T *y_ptr, MotionType yank_type, const char_u *str
void clear_oparg(oparg_T *oap)
{
- memset(oap, 0, sizeof(oparg_T));
+ CLEAR_POINTER(oap);
}
/// Count the number of bytes, characters and "words" in a line.
@@ -5888,12 +5371,12 @@ void cursor_pos_info(dict_T *dict)
max_pos = VIsual;
}
if (*p_sel == 'e' && max_pos.col > 0) {
- --max_pos.col;
+ max_pos.col--;
}
if (l_VIsual_mode == Ctrl_V) {
- char_u *const saved_sbr = p_sbr;
- char_u *const saved_w_sbr = curwin->w_p_sbr;
+ char *const saved_sbr = p_sbr;
+ char *const saved_w_sbr = curwin->w_p_sbr;
// Make 'sbr' empty for a moment to get the correct size.
p_sbr = empty_option;
@@ -5901,8 +5384,7 @@ void cursor_pos_info(dict_T *dict)
oparg.is_VIsual = true;
oparg.motion_type = kMTBlockWise;
oparg.op_type = OP_NOP;
- getvcols(curwin, &min_pos, &max_pos,
- &oparg.start_vcol, &oparg.end_vcol);
+ getvcols(curwin, &min_pos, &max_pos, &oparg.start_vcol, &oparg.end_vcol);
p_sbr = saved_sbr;
curwin->w_p_sbr = saved_w_sbr;
if (curwin->w_curswant == MAXCOL) {
@@ -5918,7 +5400,7 @@ void cursor_pos_info(dict_T *dict)
line_count_selected = max_pos.lnum - min_pos.lnum + 1;
}
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
+ for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) {
// Check for a CTRL-C every 100000 characters.
if (byte_count > last_check) {
os_breakcheck();
@@ -6065,14 +5547,14 @@ void cursor_pos_info(dict_T *dict)
}
if (dict == NULL) {
// Don't shorten this message, the user asked for it.
- p = p_shm;
- p_shm = (char_u *)"";
+ p = (char_u *)p_shm;
+ p_shm = "";
if (p_ch < 1) {
msg_start();
msg_scroll = true;
}
msg((char *)IObuff);
- p_shm = p;
+ p_shm = (char *)p;
}
}
@@ -6140,6 +5622,23 @@ static void op_colon(oparg_T *oap)
// do_cmdline() does the rest
}
+/// callback function for 'operatorfunc'
+static Callback opfunc_cb;
+
+/// Process the 'operatorfunc' option value.
+/// @return OK or FAIL
+int set_operatorfunc_option(void)
+{
+ return option_set_callback_func((char_u *)p_opfunc, &opfunc_cb);
+}
+
+#if defined(EXITFREE)
+void free_operatorfunc_option(void)
+{
+ callback_free(&opfunc_cb);
+}
+#endif
+
/// Handle the "g@" operator: call 'operatorfunc'.
static void op_function(const oparg_T *oap)
FUNC_ATTR_NONNULL_ALL
@@ -6177,7 +5676,10 @@ static void op_function(const oparg_T *oap)
// Reset finish_op so that mode() returns the right value.
finish_op = false;
- (void)call_func_retnr((char *)p_opfunc, 1, argv);
+ typval_T rettv;
+ if (callback_call(&opfunc_cb, 1, argv, &rettv)) {
+ tv_clear(&rettv);
+ }
virtual_op = save_virtual_op;
finish_op = save_finish_op;
@@ -6338,7 +5840,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
// If 'cpoptions' does not contain 'r', insert the search
// pattern to really repeat the same command.
if (vim_strchr(p_cpo, CPO_REDO) == NULL) {
- AppendToRedobuffLit((char *)cap->searchbuf, -1);
+ AppendToRedobuffLit(cap->searchbuf, -1);
}
AppendToRedobuff(NL_STR);
} else if (cap->cmdchar == ':' || cap->cmdchar == K_COMMAND) {
@@ -6348,7 +5850,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (repeat_cmdline == NULL) {
ResetRedobuff();
} else {
- AppendToRedobuffLit((char *)repeat_cmdline, -1);
+ AppendToRedobuffLit(repeat_cmdline, -1);
AppendToRedobuff(NL_STR);
XFREE_CLEAR(repeat_cmdline);
}
@@ -6581,7 +6083,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
&& oap->motion_force == NUL) {
// Make sure redrawing is correct.
curwin->w_p_lbr = lbr_saved;
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
}
}
@@ -6613,7 +6115,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
if (oap->is_VIsual && (oap->empty || !MODIFIABLE(curbuf)
|| oap->op_type == OP_FOLD)) {
curwin->w_p_lbr = lbr_saved;
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
}
// If the end of an operator is in column one while oap->motion_type
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 4958c10220..9bc5409ff9 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -27,18 +27,18 @@
#include <stdlib.h>
#include <string.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
+#include "nvim/change.h"
#include "nvim/charset.h"
-#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
+#include "nvim/decoration_provider.h"
#include "nvim/diff.h"
-#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
-#include "nvim/eval/vars.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
@@ -49,9 +49,10 @@
#include "nvim/hardcopy.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
#include "nvim/indent_c.h"
-#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
+#include "nvim/locale.h"
#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
@@ -62,16 +63,19 @@
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
+#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
-#include "nvim/popupmnu.h"
+#include "nvim/popupmenu.h"
#include "nvim/regexp.h"
-#include "nvim/runtime.h"
#include "nvim/screen.h"
+#include "nvim/search.h"
#include "nvim/spell.h"
#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -82,11 +86,25 @@
#ifdef WIN32
# include "nvim/os/pty_conpty_win.h"
#endif
+#include "nvim/api/extmark.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
-#include "nvim/quickfix.h"
+
+static char e_unknown_option[]
+ = N_("E518: Unknown option");
+static char e_not_allowed_in_modeline[]
+ = N_("E520: Not allowed in a modeline");
+static char e_not_allowed_in_modeline_when_modelineexpr_is_off[]
+ = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
+static char e_key_code_not_set[]
+ = N_("E846: Key code not set");
+static char e_number_required_after_equal[]
+ = N_("E521: Number required after =");
+static char e_preview_window_already_exists[]
+ = N_("E590: A preview window already exists");
/*
* The options that are local to a window or buffer have "indir" set to one of
@@ -119,74 +137,6 @@ typedef enum {
static char *p_term = NULL;
static char *p_ttytype = NULL;
-/*
- * These are the global values for options which are also local to a buffer.
- * Only to be used in option.c!
- */
-static int p_ai;
-static int p_bin;
-static int p_bomb;
-static char_u *p_bh;
-static char_u *p_bt;
-static int p_bl;
-static long p_channel;
-static int p_ci;
-static int p_cin;
-static char_u *p_cink;
-static char_u *p_cino;
-static char_u *p_cinw;
-static char_u *p_cinsd;
-static char_u *p_com;
-static char_u *p_cms;
-static char_u *p_cpt;
-static char_u *p_cfu;
-static char_u *p_ofu;
-static char_u *p_tfu;
-static char_u *p_umf;
-static int p_eol;
-static int p_fixeol;
-static int p_et;
-static char_u *p_fenc;
-static char_u *p_ff;
-static char_u *p_fo;
-static char_u *p_flp;
-static char_u *p_ft;
-static long p_iminsert;
-static long p_imsearch;
-static char_u *p_inex;
-static char_u *p_inde;
-static char_u *p_indk;
-static char_u *p_fex;
-static int p_inf;
-static char_u *p_isk;
-static int p_lisp;
-static int p_ml;
-static int p_ma;
-static int p_mod;
-static char_u *p_mps;
-static char_u *p_nf;
-static int p_pi;
-static char_u *p_qe;
-static int p_ro;
-static int p_si;
-static long p_sts;
-static char_u *p_sua;
-static long p_sw;
-static int p_swf;
-static long p_smc;
-static char_u *p_syn;
-static char_u *p_spc;
-static char_u *p_spf;
-static char_u *p_spl;
-static char_u *p_spo;
-static long p_ts;
-static long p_tw;
-static int p_udf;
-static long p_wm;
-static char_u *p_vsts;
-static char_u *p_vts;
-static char_u *p_keymap;
-
// Saved values for when 'bin' is set.
static int p_et_nobin;
static int p_ml_nobin;
@@ -199,7 +149,7 @@ static int p_et_nopaste;
static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
-static char_u *p_vsts_nopaste;
+static char *p_vsts_nopaste;
typedef struct vimoption {
char *fullname; // full option name
@@ -215,64 +165,6 @@ typedef struct vimoption {
} vimoption_T;
/*
- * Flags
- */
-#define P_BOOL 0x01U // the option is boolean
-#define P_NUM 0x02U // the option is numeric
-#define P_STRING 0x04U // the option is a string
-#define P_ALLOCED 0x08U // the string option is in allocated memory,
- // must use free_string_option() when
- // assigning new value. Not set if default is
- // the same.
-#define P_EXPAND 0x10U // environment expansion. NOTE: P_EXPAND can
- // never be used for local or hidden options
-#define P_NODEFAULT 0x40U // don't set to default value
-#define P_DEF_ALLOCED 0x80U // default value is in allocated memory, must
- // use free() when assigning new value
-#define P_WAS_SET 0x100U // option has been set/reset
-#define P_NO_MKRC 0x200U // don't include in :mkvimrc output
-
-// when option changed, what to display:
-#define P_RSTAT 0x1000U ///< redraw status lines
-#define P_RWIN 0x2000U ///< redraw current window and recompute text
-#define P_RBUF 0x4000U ///< redraw current buffer and recompute text
-#define P_RALL 0x6000U ///< redraw all windows
-#define P_RCLR 0x7000U ///< clear and redraw all
-
-#define P_COMMA 0x8000U ///< comma separated list
-#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive
- ///< commas
-#define P_NODUP 0x20000U ///< don't allow duplicate strings
-#define P_FLAGLIST 0x40000U ///< list of single-char flags
-
-#define P_SECURE 0x80000U ///< cannot change in modeline or secure mode
-#define P_GETTEXT 0x100000U ///< expand default value with _()
-#define P_NOGLOB 0x200000U ///< do not use local value for global vimrc
-#define P_NFNAME 0x400000U ///< only normal file name chars allowed
-#define P_INSECURE 0x800000U ///< option was set from a modeline
-#define P_PRI_MKRC 0x1000000U ///< priority for :mkvimrc (setting option
- ///< has side effects)
-#define P_NO_ML 0x2000000U ///< not allowed in modeline
-#define P_CURSWANT 0x4000000U ///< update curswant required; not needed
- ///< when there is a redraw flag
-#define P_NO_DEF_EXP 0x8000000U ///< Do not expand default value.
-
-#define P_RWINONLY 0x10000000U ///< only redraw current window
-#define P_NDNAME 0x20000000U ///< only normal dir name chars allowed
-#define P_UI_OPTION 0x40000000U ///< send option to remote ui
-#define P_MLE 0x80000000U ///< under control of 'modelineexpr'
-
-#define HIGHLIGHT_INIT \
- "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
- "i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \
- "N:CursorLineNr,G:CursorLineSign,O:CursorLineFold" \
- "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \
- "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \
- "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \
- "X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \
- "q:QuickFixLine,0:Whitespace,I:NormalNC"
-
-/*
* options[] is initialized here.
* The order of the options MUST be alphabetic for ":set all" and findoption().
* All option names MUST start with a lowercase letter (for findoption()).
@@ -287,59 +179,6 @@ typedef struct vimoption {
#define OPTION_COUNT ARRAY_SIZE(options)
-static char *(p_ambw_values[]) = { "single", "double", NULL };
-static char *(p_bg_values[]) = { "light", "dark", NULL };
-static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha",
- "unsigned", NULL };
-static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
-static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
-static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos",
- "mac", NULL };
-static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
-static char *(p_slm_values[]) = { "mouse", "key", "cmd", NULL };
-static char *(p_km_values[]) = { "startsel", "stopsel", NULL };
-static char *(p_scbopt_values[]) = { "ver", "hor", "jump", NULL };
-static char *(p_debug_values[]) = { "msg", "throw", "beep", NULL };
-static char *(p_ead_values[]) = { "both", "ver", "hor", NULL };
-static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix",
- "help", "acwrite", "terminal",
- "prompt", NULL };
-
-static char *(p_bufhidden_values[]) = { "hide", "unload", "delete",
- "wipe", NULL };
-static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL };
-static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
- "syntax", "diff", NULL };
-static char *(p_fcl_values[]) = { "all", NULL };
-static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview",
- "noinsert", "noselect", NULL };
-#ifdef BACKSLASH_IN_FILENAME
-static char *(p_csl_values[]) = { "slash", "backslash", NULL };
-#endif
-static char *(p_icm_values[]) = { "nosplit", "split", NULL };
-static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2",
- "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8",
- "auto:9",
- "yes:1", "yes:2", "yes:3", "yes:4", "yes:5", "yes:6",
- "yes:7", "yes:8",
- "yes:9", "number", NULL };
-static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2",
- "auto:3", "auto:4", "auto:5", "auto:6", "auto:7", "auto:8",
- "auto:9",
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", NULL };
-
-/// All possible flags for 'shm'.
-static char_u SHM_ALL[] = {
- SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI,
- SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER,
- SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU,
- SHM_RECORDING, SHM_FILEINFO, SHM_SEARCHCOUNT,
- 0,
-};
-
-static char e_unclosed_expression_sequence[] = N_("E540: Unclosed expression sequence");
-static char e_unbalanced_groups[] = N_("E542: unbalanced groups");
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.c.generated.h"
#endif
@@ -580,7 +419,7 @@ void set_init_1(bool clean_arg)
// NOTE: mlterm's author is being asked to 'set' a variable
// instead of an environment variable due to inheritance.
if (os_env_exists("MLTERM")) {
- set_option_value("tbidi", 1L, NULL, 0);
+ set_option_value_give_err("tbidi", 1L, NULL, 0);
}
didset_options2();
@@ -600,7 +439,7 @@ void set_init_1(bool clean_arg)
#ifdef HAVE_WORKING_LIBINTL
// GNU gettext 0.10.37 supports this feature: set the codeset used for
// translated messages independently from the current locale.
- (void)bind_textdomain_codeset(PROJECT_NAME, (char *)p_enc);
+ (void)bind_textdomain_codeset(PROJECT_NAME, p_enc);
#endif
// Set the default for 'helplang'.
@@ -616,7 +455,7 @@ static void set_option_default(int opt_idx, int opt_flags)
char_u *varp; // pointer to variable for current option
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- varp = get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
+ varp = (char_u *)get_varp_scope(&(options[opt_idx]), both ? OPT_LOCAL : opt_flags);
uint32_t flags = options[opt_idx].flags;
if (varp != NULL) { // skip hidden option, nothing to do for it
if (flags & P_STRING) {
@@ -627,7 +466,7 @@ static void set_option_default(int opt_idx, int opt_flags)
(char *)options[opt_idx].def_val, opt_flags, 0);
} else {
if ((opt_flags & OPT_FREE) && (flags & P_ALLOCED)) {
- free_string_option(*(char_u **)(varp));
+ free_string_option(*(char **)(varp));
}
*(char_u **)varp = options[opt_idx].def_val;
options[opt_idx].flags &= ~P_ALLOCED;
@@ -766,16 +605,17 @@ void free_all_options(void)
if (options[i].indir == PV_NONE) {
// global option: free value and default value.
if ((options[i].flags & P_ALLOCED) && options[i].var != NULL) {
- free_string_option(*(char_u **)options[i].var);
+ free_string_option(*(char **)options[i].var);
}
if (options[i].flags & P_DEF_ALLOCED) {
- free_string_option(options[i].def_val);
+ free_string_option((char *)options[i].def_val);
}
} else if (options[i].var != VAR_WIN && (options[i].flags & P_STRING)) {
// buffer-local option: free global value
- clear_string_option((char_u **)options[i].var);
+ clear_string_option((char **)options[i].var);
}
}
+ free_operatorfunc_option();
}
#endif
@@ -835,12 +675,12 @@ void set_init_3(void)
if (FNAMECMP(p, "csh") == 0
|| FNAMECMP(p, "tcsh") == 0) {
if (do_sp) {
- p_sp = (char_u *)"|& tee";
- options[idx_sp].def_val = p_sp;
+ p_sp = "|& tee";
+ options[idx_sp].def_val = (char_u *)p_sp;
}
if (do_srr) {
- p_srr = (char_u *)">&";
- options[idx_srr].def_val = p_srr;
+ p_srr = ">&";
+ options[idx_srr].def_val = (char_u *)p_srr;
}
} else if (FNAMECMP(p, "sh") == 0
|| FNAMECMP(p, "ksh") == 0
@@ -854,12 +694,12 @@ void set_init_3(void)
|| FNAMECMP(p, "dash") == 0) {
// Always use POSIX shell style redirection if we reach this
if (do_sp) {
- p_sp = (char_u *)"2>&1| tee";
- options[idx_sp].def_val = p_sp;
+ p_sp = "2>&1| tee";
+ options[idx_sp].def_val = (char_u *)p_sp;
}
if (do_srr) {
- p_srr = (char_u *)">%s 2>&1";
- options[idx_srr].def_val = p_srr;
+ p_srr = ">%s 2>&1";
+ options[idx_srr].def_val = (char_u *)p_srr;
}
}
xfree(p);
@@ -892,7 +732,7 @@ void set_helplang_default(const char *lang)
int idx = findoption("hlg");
if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) {
if (options[idx].flags & P_ALLOCED) {
- free_string_option(p_hlg);
+ free_string_option((char *)p_hlg);
}
p_hlg = (char_u *)xmemdupz(lang, lang_len);
// zh_CN becomes "cn", zh_TW becomes "tw".
@@ -970,7 +810,7 @@ int do_set(char *arg, int opt_flags)
int opt_idx;
char *errmsg;
char errbuf[80];
- char_u *startarg;
+ char *startarg;
int prefix; // 1: nothing, 0: "no", 2: "inv" in front of name
char_u nextchar; // next non-white char after option name
int afterchar; // character just after option name
@@ -979,7 +819,7 @@ int do_set(char *arg, int opt_flags)
varnumber_T value;
int key;
uint32_t flags; // flags for current option
- char_u *varp = NULL; // pointer to variable for current option
+ char *varp = NULL; // pointer to variable for current option
int did_show = false; // already showed one value
int adding; // "opt+=arg"
int prepending; // "opt^=arg"
@@ -993,7 +833,7 @@ int do_set(char *arg, int opt_flags)
while (*arg != NUL) { // loop to process all options
errmsg = NULL;
- startarg = (char_u *)arg; // remember for error message
+ startarg = arg; // remember for error message
if (STRNCMP(arg, "all", 3) == 0 && !isalpha(arg[3])
&& !(opt_flags & OPT_MODELINE)) {
@@ -1009,7 +849,7 @@ int do_set(char *arg, int opt_flags)
didset_options();
didset_options2();
ui_refresh_options();
- redraw_all_later(CLEAR);
+ redraw_all_later(UPD_CLEAR);
} else {
showoptions(1, opt_flags);
did_show = true;
@@ -1090,7 +930,7 @@ int do_set(char *arg, int opt_flags)
nextchar = (uint8_t)arg[len];
if (opt_idx == -1 && key == 0) { // found a mismatch: skip
- errmsg = N_("E518: Unknown option");
+ errmsg = e_unknown_option;
goto skip;
}
@@ -1101,7 +941,7 @@ int do_set(char *arg, int opt_flags)
if (vim_strchr("=:!&<", nextchar) == NULL
&& (!(options[opt_idx].flags & P_BOOL)
|| nextchar == '?')) {
- errmsg = _(e_unsupportedoption);
+ errmsg = e_unsupportedoption;
}
goto skip;
}
@@ -1128,11 +968,11 @@ int do_set(char *arg, int opt_flags)
// Disallow changing some options from modelines.
if (opt_flags & OPT_MODELINE) {
if (flags & (P_SECURE | P_NO_ML)) {
- errmsg = N_("E520: Not allowed in a modeline");
+ errmsg = e_not_allowed_in_modeline;
goto skip;
}
if ((flags & P_MLE) && !p_mle) {
- errmsg = N_("E992: Not allowed in a modeline when 'modelineexpr' is off");
+ errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off;
goto skip;
}
// In diff mode some options are overruled. This avoids that
@@ -1189,7 +1029,7 @@ int do_set(char *arg, int opt_flags)
showoneopt(&options[opt_idx], opt_flags);
if (p_verbose > 0) {
// Mention where the option was last set.
- if (varp == options[opt_idx].var) {
+ if (varp == (char *)options[opt_idx].var) {
option_last_set_msg(options[opt_idx].last_set);
} else if ((int)options[opt_idx].indir & PV_WIN) {
option_last_set_msg(curwin->w_p_script_ctx[
@@ -1200,7 +1040,7 @@ int do_set(char *arg, int opt_flags)
}
}
} else {
- errmsg = N_("E846: Key code not set");
+ errmsg = e_key_code_not_set;
goto skip;
}
if (nextchar != '?'
@@ -1251,8 +1091,7 @@ int do_set(char *arg, int opt_flags)
}
}
- errmsg = set_bool_option(opt_idx, varp, (int)value,
- opt_flags);
+ errmsg = set_bool_option(opt_idx, (char_u *)varp, (int)value, opt_flags);
} else { // Numeric or string.
if (vim_strchr("=:&<", nextchar) == NULL
|| prefix != 1) {
@@ -1294,11 +1133,11 @@ int do_set(char *arg, int opt_flags)
// Allow negative, octal and hex numbers.
vim_str2nr((char_u *)arg, NULL, &i, STR2NR_ALL, &value, NULL, 0, true);
if (i == 0 || (arg[i] != NUL && !ascii_iswhite(arg[i]))) {
- errmsg = N_("E521: Number required after =");
+ errmsg = e_number_required_after_equal;
goto skip;
}
} else {
- errmsg = N_("E521: Number required after =");
+ errmsg = e_number_required_after_equal;
goto skip;
}
@@ -1311,7 +1150,7 @@ int do_set(char *arg, int opt_flags)
if (removing) {
value = *(long *)varp - value;
}
- errmsg = set_num_option(opt_idx, varp, (long)value,
+ errmsg = set_num_option(opt_idx, (char_u *)varp, (long)value,
errbuf, sizeof(errbuf),
opt_flags);
} else if (opt_idx >= 0) { // String.
@@ -1334,7 +1173,7 @@ int do_set(char *arg, int opt_flags)
// reset, use the global value here.
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
&& ((int)options[opt_idx].indir & PV_BOTH)) {
- varp = options[opt_idx].var;
+ varp = (char *)options[opt_idx].var;
}
// The old value is kept until we are sure that the
@@ -1348,7 +1187,7 @@ int do_set(char *arg, int opt_flags)
// A global-local string option might have an empty
// option as value to indicate that the global
// value should be used.
- if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) {
+ if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == (char_u *)empty_option) {
origval_l = origval_g;
}
}
@@ -1368,7 +1207,7 @@ int do_set(char *arg, int opt_flags)
// required when an environment variable was set
// later
if (newval == NULL) {
- newval = empty_option;
+ newval = (char_u *)empty_option;
} else if (!(options[opt_idx].flags & P_NO_DEF_EXP)) {
s = option_expand(opt_idx, newval);
if (s == NULL) {
@@ -1383,27 +1222,20 @@ int do_set(char *arg, int opt_flags)
} else {
arg++; // jump to after the '=' or ':'
- /*
- * Set 'keywordprg' to ":help" if an empty
- * value was passed to :set by the user.
- * Misuse errbuf[] for the resulting string.
- */
- if (varp == (char_u *)&p_kp
- && (*arg == NUL || *arg == ' ')) {
+ // Set 'keywordprg' to ":help" if an empty
+ // value was passed to :set by the user.
+ // Misuse errbuf[] for the resulting string.
+ if (varp == (char *)&p_kp && (*arg == NUL || *arg == ' ')) {
STRCPY(errbuf, ":help");
save_arg = (char_u *)arg;
arg = errbuf;
- }
- /*
- * Convert 'backspace' number to string, for
- * adding, prepending and removing string.
- */
- else if (varp == (char_u *)&p_bs
- && ascii_isdigit(**(char_u **)varp)) {
+ } else if (varp == (char *)&p_bs && ascii_isdigit(**(char_u **)varp)) {
+ // Convert 'backspace' number to string, for
+ // adding, prepending and removing string.
i = getdigits_int((char **)varp, true, 0);
switch (i) {
case 0:
- *(char_u **)varp = empty_option;
+ *(char **)varp = empty_option;
break;
case 1:
*(char_u **)varp = vim_strsave((char_u *)"indent,eol");
@@ -1426,14 +1258,10 @@ int do_set(char *arg, int opt_flags)
origval_g = *(char_u **)varp;
}
oldval = *(char_u **)varp;
- }
- /*
- * Convert 'whichwrap' number to string, for
- * backwards compatibility with Vim 3.0.
- * Misuse errbuf[] for the resulting string.
- */
- else if (varp == (char_u *)&p_ww
- && ascii_isdigit(*arg)) {
+ } else if (varp == (char *)&p_ww && ascii_isdigit(*arg)) {
+ // Convert 'whichwrap' number to string, for
+ // backwards compatibility with Vim 3.0.
+ // Misuse errbuf[] for the resulting string.
*errbuf = NUL;
i = getdigits_int(&arg, true, 0);
if (i & 1) {
@@ -1453,14 +1281,11 @@ int do_set(char *arg, int opt_flags)
}
save_arg = (char_u *)arg;
arg = errbuf;
- }
- /*
- * Remove '>' before 'dir' and 'bdir', for
- * backwards compatibility with version 3.0
- */
- else if (*arg == '>'
- && (varp == (char_u *)&p_dir
- || varp == (char_u *)&p_bdir)) {
+ } else if (*arg == '>'
+ && (varp == (char *)&p_dir
+ || varp == (char *)&p_bdir)) {
+ // Remove '>' before 'dir' and 'bdir', for
+ // backwards compatibility with version 3.0
arg++;
}
@@ -1658,7 +1483,7 @@ int do_set(char *arg, int opt_flags)
// for ":set" on local options. Note: when setting
// 'syntax' or 'filetype' autocommands may be
// triggered that can cause havoc.
- errmsg = did_set_string_option(opt_idx, (char_u **)varp, oldval,
+ errmsg = did_set_string_option(opt_idx, (char **)varp, (char *)oldval,
errbuf, sizeof(errbuf),
opt_flags, &value_checked);
@@ -1718,17 +1543,17 @@ skip:
if (errmsg != NULL) {
STRLCPY(IObuff, _(errmsg), IOSIZE);
i = (int)STRLEN(IObuff) + 2;
- if (i + ((char_u *)arg - startarg) < IOSIZE) {
+ if (i + (arg - startarg) < IOSIZE) {
// append the argument with the error
STRCAT(IObuff, ": ");
- assert((char_u *)arg >= startarg);
- memmove(IObuff + i, startarg, (size_t)((char_u *)arg - startarg));
- IObuff[i + ((char_u *)arg - startarg)] = NUL;
+ assert(arg >= startarg);
+ memmove(IObuff + i, startarg, (size_t)(arg - startarg));
+ IObuff[i + (arg - startarg)] = NUL;
}
// make sure all characters are printable
trans_characters((char *)IObuff, IOSIZE);
- no_wait_return++; // wait_return done later
+ no_wait_return++; // wait_return() done later
emsg((char *)IObuff); // show error highlighted
no_wait_return--;
@@ -1758,7 +1583,7 @@ theend:
/// @param opt_flags possibly with OPT_MODELINE
/// @param new_value value was replaced completely
/// @param value_checked value was checked to be safe, no need to set P_INSECURE
-static void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked)
+void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked)
{
options[opt_idx].flags |= P_WAS_SET;
@@ -1775,19 +1600,9 @@ static void did_set_option(int opt_idx, int opt_flags, int new_value, int value_
}
}
-static char *illegal_char(char *errbuf, size_t errbuflen, int c)
-{
- if (errbuf == NULL) {
- return "";
- }
- vim_snprintf(errbuf, errbuflen, _("E539: Illegal character <%s>"),
- (char *)transchar(c));
- return errbuf;
-}
-
/// Convert a key name or string into a key value.
/// Used for 'wildchar' and 'cedit' options.
-static int string_to_key(char_u *arg)
+int string_to_key(char_u *arg)
{
if (*arg == '<') {
return find_key_option(arg + 1, true);
@@ -1798,29 +1613,11 @@ static int string_to_key(char_u *arg)
return *arg;
}
-/// Check value of 'cedit' and set cedit_key.
-/// Returns NULL if value is OK, error message otherwise.
-static char *check_cedit(void)
-{
- int n;
-
- if (*p_cedit == NUL) {
- cedit_key = -1;
- } else {
- n = string_to_key(p_cedit);
- if (vim_isprintc(n)) {
- return e_invarg;
- }
- cedit_key = n;
- }
- return NULL;
-}
-
// When changing 'title', 'titlestring', 'icon' or 'iconstring', call
// maketitle() to create and display it.
// When switching the title or icon off, call ui_set_{icon,title}(NULL) to get
// the old value back.
-static void did_set_title(void)
+void did_set_title(void)
{
if (starting != NO_SCREEN) {
maketitle();
@@ -1902,16 +1699,14 @@ int get_shada_parameter(int type)
/// Return NULL if the parameter is not specified in the string.
char_u *find_shada_parameter(int type)
{
- char_u *p;
-
- for (p = p_shada; *p; p++) {
+ for (char *p = p_shada; *p; p++) {
if (*p == type) {
- return p + 1;
+ return (char_u *)p + 1;
}
if (*p == 'n') { // 'n' is always the last one
break;
}
- p = (char_u *)vim_strchr((char *)p, ','); // skip until next ','
+ p = vim_strchr(p, ','); // skip until next ','
if (p == NULL) { // hit the end without finding parameter
break;
}
@@ -1946,35 +1741,15 @@ static char_u *option_expand(int opt_idx, char_u *val)
* names.
* For 'spellsuggest' expand after "file:".
*/
- expand_env_esc(val, NameBuff, MAXPATHL,
+ expand_env_esc(val, (char_u *)NameBuff, MAXPATHL,
(char_u **)options[opt_idx].var == &p_tags, false,
- (char_u **)options[opt_idx].var == &p_sps ? (char_u *)"file:" :
+ (char_u **)options[opt_idx].var == (char_u **)&p_sps ? (char_u *)"file:" :
NULL);
if (STRCMP(NameBuff, val) == 0) { // they are the same
return NULL;
}
- return NameBuff;
-}
-
-/// After setting various option values: recompute variables that depend on
-/// option values.
-static void didset_string_options(void)
-{
- (void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
- (void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true);
- (void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, true);
- (void)opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true);
- (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
- (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
- (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
- (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
- (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
- (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
- (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
- (void)opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
- (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
- (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
+ return (char_u *)NameBuff;
}
/// After setting various option values: recompute variables that depend on
@@ -1994,7 +1769,7 @@ static void didset_options(void)
(void)check_cedit();
// initialize the table for 'breakat'.
fill_breakat_flags();
- didset_window_options(curwin);
+ didset_window_options(curwin, true);
}
// More side effects of setting options.
@@ -2003,9 +1778,6 @@ static void didset_options2(void)
// Initialize the highlight_attr[] table.
highlight_changed();
- // Parse default for 'clipboard'.
- (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
-
// Parse default for 'fillchars'.
(void)set_chars_option(curwin, &curwin->w_p_fcs, true);
@@ -2027,96 +1799,11 @@ void check_options(void)
for (opt_idx = 0; options[opt_idx].fullname != NULL; opt_idx++) {
if ((options[opt_idx].flags & P_STRING) && options[opt_idx].var != NULL) {
- check_string_option((char_u **)get_varp(&(options[opt_idx])));
+ check_string_option((char **)get_varp(&(options[opt_idx])));
}
}
}
-/// Check string options in a buffer for NULL value.
-void check_buf_options(buf_T *buf)
-{
- check_string_option(&buf->b_p_bh);
- check_string_option(&buf->b_p_bt);
- check_string_option(&buf->b_p_fenc);
- check_string_option(&buf->b_p_ff);
- check_string_option(&buf->b_p_def);
- check_string_option(&buf->b_p_inc);
- check_string_option(&buf->b_p_inex);
- check_string_option(&buf->b_p_inde);
- check_string_option(&buf->b_p_indk);
- check_string_option(&buf->b_p_fp);
- check_string_option(&buf->b_p_fex);
- check_string_option(&buf->b_p_kp);
- check_string_option(&buf->b_p_mps);
- check_string_option(&buf->b_p_fo);
- check_string_option(&buf->b_p_flp);
- check_string_option(&buf->b_p_isk);
- check_string_option(&buf->b_p_com);
- check_string_option(&buf->b_p_cms);
- check_string_option(&buf->b_p_nf);
- check_string_option(&buf->b_p_qe);
- check_string_option(&buf->b_p_syn);
- check_string_option(&buf->b_s.b_syn_isk);
- check_string_option(&buf->b_s.b_p_spc);
- check_string_option(&buf->b_s.b_p_spf);
- check_string_option(&buf->b_s.b_p_spl);
- check_string_option(&buf->b_s.b_p_spo);
- check_string_option(&buf->b_p_sua);
- check_string_option(&buf->b_p_cink);
- check_string_option(&buf->b_p_cino);
- parse_cino(buf);
- check_string_option(&buf->b_p_ft);
- check_string_option(&buf->b_p_cinw);
- check_string_option(&buf->b_p_cinsd);
- check_string_option(&buf->b_p_cpt);
- check_string_option(&buf->b_p_cfu);
- check_string_option(&buf->b_p_ofu);
- check_string_option(&buf->b_p_keymap);
- check_string_option(&buf->b_p_gp);
- check_string_option(&buf->b_p_mp);
- check_string_option(&buf->b_p_efm);
- check_string_option(&buf->b_p_ep);
- check_string_option(&buf->b_p_path);
- check_string_option(&buf->b_p_tags);
- check_string_option(&buf->b_p_tfu);
- check_string_option(&buf->b_p_tc);
- check_string_option(&buf->b_p_dict);
- check_string_option(&buf->b_p_tsr);
- check_string_option(&buf->b_p_tsrfu);
- check_string_option(&buf->b_p_lw);
- check_string_option(&buf->b_p_bkc);
- check_string_option(&buf->b_p_menc);
- check_string_option(&buf->b_p_vsts);
- check_string_option(&buf->b_p_vts);
-}
-
-/// Free the string allocated for an option.
-/// Checks for the string being empty_option. This may happen if we're out of
-/// memory, vim_strsave() returned NULL, which was replaced by empty_option by
-/// check_options().
-/// Does NOT check for P_ALLOCED flag!
-void free_string_option(char_u *p)
-{
- if (p != empty_option) {
- xfree(p);
- }
-}
-
-void clear_string_option(char_u **pp)
-{
- if (*pp != empty_option) {
- xfree(*pp);
- }
- *pp = empty_option;
-}
-
-static void check_string_option(char_u **pp)
-{
- if (*pp == NULL) {
- *pp = empty_option;
- }
-}
-
/// Return true when option "opt" was set from a modeline or in secure mode.
/// Return false when it wasn't.
/// Return -1 for an unknown option.
@@ -2163,172 +1850,18 @@ static uint32_t *insecure_flag(win_T *const wp, int opt_idx, int opt_flags)
}
/// Redraw the window title and/or tab page text later.
-static void redraw_titles(void)
+void redraw_titles(void)
{
need_maketitle = true;
redraw_tabline = true;
}
-static int shada_idx = -1;
-
-/// Set a string option to a new value (without checking the effect).
-/// The string is copied into allocated memory.
-/// if ("opt_idx" == -1) "name" is used, otherwise "opt_idx" is used.
-/// When "set_sid" is zero set the scriptID to current_sctx.sc_sid. When
-/// "set_sid" is SID_NONE don't set the scriptID. Otherwise set the scriptID to
-/// "set_sid".
-///
-/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
-void set_string_option_direct(const char *name, int opt_idx, const char *val, int opt_flags,
- int set_sid)
-{
- char *s;
- char **varp;
- int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
- int idx = opt_idx;
-
- if (idx == -1) { // Use name.
- idx = findoption(name);
- if (idx < 0) { // Not found (should not happen).
- internal_error("set_string_option_direct()");
- siemsg(_("For option %s"), name);
- return;
- }
- }
-
- if (options[idx].var == NULL) { // can't set hidden option
- return;
- }
-
- assert((void *)options[idx].var != (void *)&p_shada);
-
- s = xstrdup(val);
- {
- varp = (char **)get_varp_scope(&(options[idx]), both ? OPT_LOCAL : opt_flags);
- if ((opt_flags & OPT_FREE) && (options[idx].flags & P_ALLOCED)) {
- free_string_option((char_u *)(*varp));
- }
- *varp = s;
-
- // For buffer/window local option may also set the global value.
- if (both) {
- set_string_option_global(idx, (char_u **)varp);
- }
-
- options[idx].flags |= P_ALLOCED;
-
- /* When setting both values of a global option with a local value,
- * make the local value empty, so that the global value is used. */
- if (((int)options[idx].indir & PV_BOTH) && both) {
- free_string_option((char_u *)(*varp));
- *varp = (char *)empty_option;
- }
- if (set_sid != SID_NONE) {
- sctx_T script_ctx;
-
- if (set_sid == 0) {
- script_ctx = current_sctx;
- } else {
- script_ctx.sc_sid = set_sid;
- script_ctx.sc_seq = 0;
- script_ctx.sc_lnum = 0;
- }
- set_option_sctx_idx(idx, opt_flags, script_ctx);
- }
- }
-}
-
-/// Set global value for string option when it's a local option.
-///
-/// @param opt_idx option index
-/// @param varp pointer to option variable
-static void set_string_option_global(int opt_idx, char_u **varp)
-{
- char_u **p, *s;
-
- // the global value is always allocated
- if (options[opt_idx].var == VAR_WIN) {
- p = (char_u **)GLOBAL_WO(varp);
- } else {
- p = (char_u **)options[opt_idx].var;
- }
- if (options[opt_idx].indir != PV_NONE && p != varp) {
- s = vim_strsave(*varp);
- free_string_option(*p);
- *p = s;
- }
-}
-
-/// Set a string option to a new value, handling the effects
-///
-/// @param[in] opt_idx Option to set.
-/// @param[in] value New value.
-/// @param[in] opt_flags Option flags: expected to contain #OPT_LOCAL and/or
-/// #OPT_GLOBAL.
-///
-/// @return NULL on success, error message on error.
-static char *set_string_option(const int opt_idx, const char *const value, const int opt_flags)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (options[opt_idx].var == NULL) { // don't set hidden option
- return NULL;
- }
-
- char *const s = xstrdup(value);
- char **const varp = (char **)get_varp_scope(&(options[opt_idx]),
- ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- ? (((int)options[opt_idx].indir & PV_BOTH)
- ? OPT_GLOBAL : OPT_LOCAL)
- : opt_flags));
- char *const oldval = *varp;
- char *oldval_l = NULL;
- char *oldval_g = NULL;
-
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- oldval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
- oldval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
-
- *varp = s;
-
- char *const saved_oldval = xstrdup(oldval);
- char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup(oldval_l) : 0;
- char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup(oldval_g) : 0;
- char *const saved_newval = xstrdup(s);
-
- int value_checked = false;
- char *const r = did_set_string_option(opt_idx, (char_u **)varp, (char_u *)oldval,
- NULL, 0,
- opt_flags, &value_checked);
- if (r == NULL) {
- did_set_option(opt_idx, opt_flags, true, value_checked);
- }
-
- // call autocommand after handling side effects
- if (r == NULL) {
- if (!starting) {
- trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
- saved_newval);
- }
- if (options[opt_idx].flags & P_UI_OPTION) {
- ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
- STRING_OBJ(cstr_as_string(saved_newval)));
- }
- }
- xfree(saved_oldval);
- xfree(saved_oldval_l);
- xfree(saved_oldval_g);
- xfree(saved_newval);
-
- return r;
-}
-
/// Return true if "val" is a valid name: only consists of alphanumeric ASCII
/// characters or characters in "allowed".
-static bool valid_name(const char_u *val, const char *allowed)
+bool valid_name(const char *val, const char *allowed)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
- for (const char_u *s = val; *s != NUL; s++) {
+ for (const char_u *s = (char_u *)val; *s != NUL; s++) {
if (!ASCII_ISALNUM(*s)
&& vim_strchr(allowed, *s) == NULL) {
return false;
@@ -2337,1632 +1870,36 @@ static bool valid_name(const char_u *val, const char *allowed)
return true;
}
-/// Return true if "val" is a valid 'filetype' name.
-/// Also used for 'syntax' and 'keymap'.
-static bool valid_filetype(const char_u *val)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return valid_name(val, ".-_");
-}
-
-/// Return true if "val" is a valid 'spelllang' value.
-bool valid_spelllang(const char_u *val)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return valid_name(val, ".-_,@");
-}
-
-/// Return true if "val" is a valid 'spellfile' value.
-static bool valid_spellfile(const char_u *val)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- for (const char_u *s = val; *s != NUL; s++) {
- if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
- return false;
- }
- }
- return true;
-}
-
-/// Handle setting 'mousescroll'.
-/// @return error message, NULL if it's OK.
-static char *check_mousescroll(char *string)
-{
- long vertical = -1;
- long horizontal = -1;
-
- for (;;) {
- char *end = vim_strchr(string, ',');
- size_t length = end ? (size_t)(end - string) : STRLEN(string);
-
- // Both "ver:" and "hor:" are 4 bytes long.
- // They should be followed by at least one digit.
- if (length <= 4) {
- return e_invarg;
- }
-
- long *direction;
-
- if (memcmp(string, "ver:", 4) == 0) {
- direction = &vertical;
- } else if (memcmp(string, "hor:", 4) == 0) {
- direction = &horizontal;
- } else {
- return e_invarg;
- }
-
- // If the direction has already been set, this is a duplicate.
- if (*direction != -1) {
- return e_invarg;
- }
-
- // Verify that only digits follow the colon.
- for (size_t i = 4; i < length; i++) {
- if (!ascii_isdigit(string[i])) {
- return N_("E548: digit expected");
- }
- }
-
- string += 4;
- *direction = getdigits_int(&string, false, -1);
-
- // Num options are generally kept within the signed int range.
- // We know this number won't be negative because we've already checked for
- // a minus sign. We'll allow 0 as a means of disabling mouse scrolling.
- if (*direction == -1) {
- return e_invarg;
- }
-
- if (!end) {
- break;
- }
-
- string = end + 1;
- }
-
- // If a direction wasn't set, fallback to the default value.
- p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical;
- p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal;
-
- return NULL;
-}
-
-/// Handle string options that need some action to perform when changed.
-/// The new value must be allocated.
-/// Returns NULL for success, or an error message for an error.
-///
-/// @param opt_idx index in options[] table
-/// @param varp pointer to the option variable
-/// @param oldval previous value of the option
-/// @param errbuf buffer for errors, or NULL
-/// @param errbuflen length of errors buffer
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
-/// @param value_checked value was checked to be safe, no need to set P_INSECURE
-static char *did_set_string_option(int opt_idx, char_u **varp, char_u *oldval, char *errbuf,
- size_t errbuflen, int opt_flags, int *value_checked)
-{
- char *errmsg = NULL;
- char_u *s, *p;
- int did_chartab = false;
- char_u **gvarp;
- bool free_oldval = (options[opt_idx].flags & P_ALLOCED);
- bool value_changed = false;
-
- /* Get the global option to compare with, otherwise we would have to check
- * two values for all local options. */
- gvarp = (char_u **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
-
- // Disallow changing some options from secure mode
- if ((secure || sandbox != 0)
- && (options[opt_idx].flags & P_SECURE)) {
- errmsg = e_secure;
- } else if (((options[opt_idx].flags & P_NFNAME)
- && strpbrk((char *)(*varp),
- (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
- || ((options[opt_idx].flags & P_NDNAME)
- && strpbrk((char *)(*varp), "*?[|;&<>\r\n") != NULL)) {
- // Check for a "normal" directory or file name in some options. Disallow a
- // path separator (slash and/or backslash), wildcards and characters that
- // are often illegal in a file name. Be more permissive if "secure" is off.
- errmsg = e_invarg;
- } else if (gvarp == &p_bkc) { // 'backupcopy'
- char_u *bkc = p_bkc;
- unsigned int *flags = &bkc_flags;
-
- if (opt_flags & OPT_LOCAL) {
- bkc = curbuf->b_p_bkc;
- flags = &curbuf->b_bkc_flags;
- }
-
- if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
- // make the local value empty: use the global value
- *flags = 0;
- } else {
- if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
- errmsg = e_invarg;
- }
-
- if (((*flags & BKC_AUTO) != 0)
- + ((*flags & BKC_YES) != 0)
- + ((*flags & BKC_NO) != 0) != 1) {
- // Must have exactly one of "auto", "yes" and "no".
- (void)opt_strings_flags(oldval, p_bkc_values, flags, true);
- errmsg = e_invarg;
- }
- }
- } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
- if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
- *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
- errmsg = N_("E589: 'backupext' and 'patchmode' are equal");
- }
- } else if (varp == &curwin->w_p_briopt) { // 'breakindentopt'
- if (briopt_check(curwin) == FAIL) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_isi
- || varp == &(curbuf->b_p_isk)
- || varp == &p_isp
- || varp == &p_isf) {
- // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
- // If the new option is invalid, use old value. 'lisp' option: refill
- // g_chartab[] for '-' char
- if (init_chartab() == FAIL) {
- did_chartab = true; // need to restore it below
- errmsg = e_invarg; // error in value
- }
- } else if (varp == &p_hf) { // 'helpfile'
- // May compute new values for $VIM and $VIMRUNTIME
- if (didset_vim) {
- os_setenv("VIM", "", 1);
- didset_vim = false;
- }
- if (didset_vimruntime) {
- os_setenv("VIMRUNTIME", "", 1);
- didset_vimruntime = false;
- }
- } else if (varp == &p_rtp || varp == &p_pp) { // 'runtimepath' 'packpath'
- runtime_search_path_invalidate();
- } else if (varp == &curwin->w_p_culopt
- || gvarp == &curwin->w_allbuf_opt.wo_culopt) { // 'cursorlineopt'
- if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &curwin->w_p_cc) { // 'colorcolumn'
- errmsg = check_colorcolumn(curwin);
- } else if (varp == &p_hlg) { // 'helplang'
- // Check for "", "ab", "ab,cd", etc.
- for (s = p_hlg; *s != NUL; s += 3) {
- if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
- errmsg = e_invarg;
- break;
- }
- if (s[2] == NUL) {
- break;
- }
- }
- } else if (varp == &p_hl) {
- // 'highlight'
- if (STRCMP(*varp, HIGHLIGHT_INIT) != 0) {
- errmsg = e_unsupportedoption;
- }
- } else if (varp == &p_jop) { // 'jumpoptions'
- if (opt_strings_flags(p_jop, p_jop_values, &jop_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_nf) { // 'nrformats'
- if (check_opt_strings(*varp, p_nf_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_ssop) { // 'sessionoptions'
- if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
- errmsg = e_invarg;
- }
- if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
- // Don't allow both "sesdir" and "curdir".
- (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
- errmsg = e_invarg;
- }
- } else if (varp == &p_vop) { // 'viewoptions'
- if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_rdb) { // 'redrawdebug'
- if (opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == (char_u **)&p_sbo) { // 'scrollopt'
- if (check_opt_strings((char_u *)p_sbo, p_scbopt_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_ambw || (int *)varp == &p_emoji) {
- // 'ambiwidth'
- if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
- errmsg = e_invarg;
- } else {
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
- errmsg = _("E834: Conflicts with value of 'listchars'");
- goto ambw_end;
- }
- if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
- errmsg = _("E835: Conflicts with value of 'fillchars'");
- goto ambw_end;
- }
- }
-ambw_end:
- {} // clint prefers {} over ; as an empty statement
- }
- } else if (varp == &p_bg) { // 'background'
- if (check_opt_strings(p_bg, p_bg_values, false) == OK) {
- int dark = (*p_bg == 'd');
-
- init_highlight(false, false);
-
- if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
- // The color scheme must have set 'background' back to another
- // value, that's not what we want here. Disable the color
- // scheme and set the colors again.
- do_unlet(S_LEN("g:colors_name"), true);
- free_string_option(p_bg);
- p_bg = vim_strsave((char_u *)(dark ? "dark" : "light"));
- check_string_option(&p_bg);
- init_highlight(false, false);
- }
- } else {
- errmsg = e_invarg;
- }
- } else if (varp == &p_wim) { // 'wildmode'
- if (check_opt_wim() == FAIL) {
- errmsg = e_invarg;
- }
- // 'wildoptions'
- } else if (varp == &p_wop) {
- if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_wak) { // 'winaltkeys'
- if (*p_wak == NUL
- || check_opt_strings(p_wak, p_wak_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_ei) { // 'eventignore'
- if (check_ei() == FAIL) {
- errmsg = e_invarg;
- }
- // 'encoding', 'fileencoding' and 'makeencoding'
- } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
- if (gvarp == &p_fenc) {
- if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) {
- errmsg = e_modifiable;
- } else if (vim_strchr((char *)(*varp), ',') != NULL) {
- // No comma allowed in 'fileencoding'; catches confusing it
- // with 'fileencodings'.
- errmsg = e_invarg;
- } else {
- // May show a "+" in the title now.
- redraw_titles();
- // Add 'fileencoding' to the swap file.
- ml_setflags(curbuf);
- }
- }
-
- if (errmsg == NULL) {
- // canonize the value, so that STRCMP() can be used on it
- p = enc_canonize(*varp);
- xfree(*varp);
- *varp = p;
- if (varp == &p_enc) {
- // only encoding=utf-8 allowed
- if (STRCMP(p_enc, "utf-8") != 0) {
- errmsg = e_unsupportedoption;
- } else {
- spell_reload();
- }
- }
- }
- } else if (varp == &p_penc) {
- // Canonize printencoding if VIM standard one
- p = enc_canonize(p_penc);
- xfree(p_penc);
- p_penc = p;
- } else if (varp == &curbuf->b_p_keymap) {
- if (!valid_filetype(*varp)) {
- errmsg = e_invarg;
- } else {
- int secure_save = secure;
-
- // Reset the secure flag, since the value of 'keymap' has
- // been checked to be safe.
- secure = 0;
-
- // load or unload key mapping tables
- errmsg = keymap_init();
-
- secure = secure_save;
-
- // Since we check the value, there is no need to set P_INSECURE,
- // even when the value comes from a modeline.
- *value_checked = true;
- }
-
- if (errmsg == NULL) {
- if (*curbuf->b_p_keymap != NUL) {
- // Installed a new keymap, switch on using it.
- curbuf->b_p_iminsert = B_IMODE_LMAP;
- if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT) {
- curbuf->b_p_imsearch = B_IMODE_LMAP;
- }
- } else {
- // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
- if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
- curbuf->b_p_iminsert = B_IMODE_NONE;
- }
- if (curbuf->b_p_imsearch == B_IMODE_LMAP) {
- curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
- }
- }
- if ((opt_flags & OPT_LOCAL) == 0) {
- set_iminsert_global();
- set_imsearch_global();
- }
- status_redraw_curbuf();
- }
- } else if (gvarp == &p_ff) { // 'fileformat'
- if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL)) {
- errmsg = e_modifiable;
- } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
- errmsg = e_invarg;
- } else {
- redraw_titles();
- // update flag in swap file
- ml_setflags(curbuf);
- /* Redraw needed when switching to/from "mac": a CR in the text
- * will be displayed differently. */
- if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm') {
- redraw_curbuf_later(NOT_VALID);
- }
- }
- } else if (varp == (char_u **)&p_ffs) { // 'fileformats'
- if (check_opt_strings((char_u *)p_ffs, p_ff_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_mps) { // 'matchpairs'
- for (p = *varp; *p != NUL; p++) {
- int x2 = -1;
- int x3 = -1;
-
- p += utfc_ptr2len((char *)p);
- if (*p != NUL) {
- x2 = *p++;
- }
- if (*p != NUL) {
- x3 = utf_ptr2char((char *)p);
- p += utfc_ptr2len((char *)p);
- }
- if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) {
- errmsg = e_invarg;
- break;
- }
- if (*p == NUL) {
- break;
- }
- }
- } else if (gvarp == &p_com) { // 'comments'
- for (s = *varp; *s;) {
- while (*s && *s != ':') {
- if (vim_strchr(COM_ALL, *s) == NULL
- && !ascii_isdigit(*s) && *s != '-') {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- s++;
- }
- if (*s++ == NUL) {
- errmsg = N_("E524: Missing colon");
- } else if (*s == ',' || *s == NUL) {
- errmsg = N_("E525: Zero length string");
- }
- if (errmsg != NULL) {
- break;
- }
- while (*s && *s != ',') {
- if (*s == '\\' && s[1] != NUL) {
- s++;
- }
- s++;
- }
- s = skip_to_option_part(s);
- }
- } else if (varp == &p_lcs) { // global 'listchars'
- errmsg = set_chars_option(curwin, varp, false);
- if (errmsg == NULL) {
- // The current window is set to use the global 'listchars' value.
- // So clear the window-local value.
- if (!(opt_flags & OPT_GLOBAL)) {
- clear_string_option(&curwin->w_p_lcs);
- }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- // If no error was returned above, we don't expect an error
- // here, so ignore the return value.
- (void)set_chars_option(wp, &wp->w_p_lcs, true);
- }
- redraw_all_later(NOT_VALID);
- }
- } else if (varp == &curwin->w_p_lcs) { // local 'listchars'
- errmsg = set_chars_option(curwin, varp, true);
- } else if (varp == &p_fcs) { // global 'fillchars'
- errmsg = set_chars_option(curwin, varp, false);
- if (errmsg == NULL) {
- // The current window is set to use the global 'fillchars' value.
- // So clear the window-local value.
- if (!(opt_flags & OPT_GLOBAL)) {
- clear_string_option(&curwin->w_p_fcs);
- }
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- // If no error was returned above, we don't expect an error
- // here, so ignore the return value.
- (void)set_chars_option(wp, &wp->w_p_fcs, true);
- }
- redraw_all_later(NOT_VALID);
- }
- } else if (varp == &curwin->w_p_fcs) { // local 'fillchars'
- errmsg = set_chars_option(curwin, varp, true);
- } else if (varp == &p_cedit) { // 'cedit'
- errmsg = check_cedit();
- } else if (varp == &p_vfile) { // 'verbosefile'
- verbose_stop();
- if (*p_vfile != NUL && verbose_open() == FAIL) {
- errmsg = e_invarg;
- }
- // 'shada'
- } else if (varp == &p_shada) {
- // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
- // option.
- opt_idx = ((options[opt_idx].fullname[0] == 'v')
- ? (shada_idx == -1
- ? ((shada_idx = findoption("shada")))
- : shada_idx)
- : opt_idx);
- // Update free_oldval now that we have the opt_idx for 'shada', otherwise
- // there would be a disconnect between the check for P_ALLOCED at the start
- // of the function and the set of P_ALLOCED at the end of the function.
- free_oldval = (options[opt_idx].flags & P_ALLOCED);
- for (s = p_shada; *s;) {
- // Check it's a valid character
- if (vim_strchr("!\"%'/:<@cfhnrs", *s) == NULL) {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- if (*s == 'n') { // name is always last one
- break;
- } else if (*s == 'r') { // skip until next ','
- while (*++s && *s != ',') {}
- } else if (*s == '%') {
- // optional number
- while (ascii_isdigit(*++s)) {}
- } else if (*s == '!' || *s == 'h' || *s == 'c') {
- s++; // no extra chars
- } else { // must have a number
- while (ascii_isdigit(*++s)) {}
-
- if (!ascii_isdigit(*(s - 1))) {
- if (errbuf != NULL) {
- vim_snprintf(errbuf, errbuflen,
- _("E526: Missing number after <%s>"),
- transchar_byte(*(s - 1)));
- errmsg = errbuf;
- } else {
- errmsg = "";
- }
- break;
- }
- }
- if (*s == ',') {
- s++;
- } else if (*s) {
- if (errbuf != NULL) {
- errmsg = N_("E527: Missing comma");
- } else {
- errmsg = "";
- }
- break;
- }
- }
- if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) {
- errmsg = N_("E528: Must specify a ' value");
- }
- } else if (gvarp == &p_sbr) { // 'showbreak'
- for (s = *varp; *s;) {
- if (ptr2cells((char *)s) != 1) {
- errmsg = N_("E595: 'showbreak' contains unprintable or wide character");
- }
- MB_PTR_ADV(s);
- }
- } else if (varp == &p_guicursor) { // 'guicursor'
- errmsg = parse_shape_opt(SHAPE_CURSOR);
- } else if (varp == &p_popt) {
- errmsg = parse_printoptions();
- } else if (varp == &p_pmfn) {
- errmsg = parse_printmbfont();
- } else if (varp == &p_langmap) { // 'langmap'
- langmap_set();
- } else if (varp == &p_breakat) { // 'breakat'
- fill_breakat_flags();
- } else if (varp == &p_titlestring || varp == &p_iconstring) {
- // 'titlestring' and 'iconstring'
- int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
-
- // NULL => statusline syntax
- if (vim_strchr((char *)(*varp), '%') && check_stl_option((char *)(*varp)) == NULL) {
- stl_syntax |= flagval;
- } else {
- stl_syntax &= ~flagval;
- }
- did_set_title();
- } else if (varp == &p_sel) { // 'selection'
- if (*p_sel == NUL
- || check_opt_strings(p_sel, p_sel_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_slm) { // 'selectmode'
- if (check_opt_strings(p_slm, p_slm_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_km) { // 'keymodel'
- if (check_opt_strings(p_km, p_km_values, true) != OK) {
- errmsg = e_invarg;
- } else {
- km_stopsel = (vim_strchr((char *)p_km, 'o') != NULL);
- km_startsel = (vim_strchr((char *)p_km, 'a') != NULL);
- }
- } else if (varp == &p_mousem) { // 'mousemodel'
- if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_mousescroll) { // 'mousescroll'
- errmsg = check_mousescroll((char *)p_mousescroll);
- } else if (varp == &p_swb) { // 'switchbuf'
- if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_debug) { // 'debug'
- if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_dy) { // 'display'
- if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
- errmsg = e_invarg;
- } else {
- (void)init_chartab();
- msg_grid_validate();
- }
- } else if (varp == &p_ead) { // 'eadirection'
- if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_cb) { // 'clipboard'
- if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &(curwin->w_s->b_p_spl) // 'spell'
- || varp == &(curwin->w_s->b_p_spf)) {
- // When 'spelllang' or 'spellfile' is set and there is a window for this
- // buffer in which 'spell' is set load the wordlists.
- const bool is_spellfile = varp == &(curwin->w_s->b_p_spf);
-
- if ((is_spellfile && !valid_spellfile(*varp))
- || (!is_spellfile && !valid_spelllang(*varp))) {
- errmsg = e_invarg;
- } else {
- errmsg = did_set_spell_option(is_spellfile);
- }
- } else if (varp == &(curwin->w_s->b_p_spc)) {
- // When 'spellcapcheck' is set compile the regexp program.
- errmsg = compile_cap_prog(curwin->w_s);
- } else if (varp == &(curwin->w_s->b_p_spo)) { // 'spelloptions'
- if (**varp != NUL && STRCMP("camel", *varp) != 0) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_sps) { // 'spellsuggest'
- if (spell_check_sps() != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_msm) { // 'mkspellmem'
- if (spell_check_msm() != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_bh) {
- // When 'bufhidden' is set, check for valid value.
- if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_bt) {
- // When 'buftype' is set, check for valid value.
- if ((curbuf->terminal && curbuf->b_p_bt[0] != 't')
- || (!curbuf->terminal && curbuf->b_p_bt[0] == 't')
- || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
- errmsg = e_invarg;
- } else {
- if (curwin->w_status_height || global_stl_height()) {
- curwin->w_redr_status = true;
- redraw_later(curwin, VALID);
- }
- curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
- redraw_titles();
- }
- } else if (gvarp == &p_stl || gvarp == (char_u **)&p_wbr || varp == &p_tal || varp == &p_ruf) {
- // 'statusline', 'winbar', 'tabline' or 'rulerformat'
- int wid;
-
- if (varp == &p_ruf) { // reset ru_wid first
- ru_wid = 0;
- }
- s = *varp;
- if (varp == &p_ruf && *s == '%') {
- // set ru_wid if 'ruf' starts with "%99("
- if (*++s == '-') { // ignore a '-'
- s++;
- }
- wid = getdigits_int((char **)&s, true, 0);
- if (wid && *s == '(' && (errmsg = check_stl_option((char *)p_ruf)) == NULL) {
- ru_wid = wid;
- } else {
- errmsg = check_stl_option((char *)p_ruf);
- }
- } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
- // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!"
- errmsg = check_stl_option((char *)s);
- }
- if (varp == &p_ruf && errmsg == NULL) {
- comp_col();
- }
- // add / remove window bars for 'winbar'
- if (gvarp == (char_u **)&p_wbr) {
- set_winbar(true);
- }
- } else if (gvarp == &p_cpt) {
- // check if it is a valid value for 'complete' -- Acevedo
- for (s = *varp; *s;) {
- while (*s == ',' || *s == ' ') {
- s++;
- }
- if (!*s) {
- break;
- }
- if (vim_strchr(".wbuksid]tU", *s) == NULL) {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- if (*++s != NUL && *s != ',' && *s != ' ') {
- if (s[-1] == 'k' || s[-1] == 's') {
- // skip optional filename after 'k' and 's'
- while (*s && *s != ',' && *s != ' ') {
- if (*s == '\\' && s[1] != NUL) {
- s++;
- }
- s++;
- }
- } else {
- if (errbuf != NULL) {
- vim_snprintf(errbuf, errbuflen,
- _("E535: Illegal character after <%c>"),
- *--s);
- errmsg = errbuf;
- } else {
- errmsg = "";
- }
- break;
- }
- }
- }
- } else if (varp == &p_cot) { // 'completeopt'
- if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
- errmsg = e_invarg;
- } else {
- completeopt_was_set();
- }
-#ifdef BACKSLASH_IN_FILENAME
- } else if (gvarp == &p_csl) { // 'completeslash'
- if (check_opt_strings(p_csl, p_csl_values, false) != OK
- || check_opt_strings(curbuf->b_p_csl, p_csl_values, false) != OK) {
- errmsg = e_invarg;
- }
-#endif
- } else if (varp == &curwin->w_p_scl) {
- // 'signcolumn'
- if (check_signcolumn(*varp) != OK) {
- errmsg = e_invarg;
- }
- // When changing the 'signcolumn' to or from 'number', recompute the
- // width of the number column if 'number' or 'relativenumber' is set.
- if (((*oldval == 'n' && *(oldval + 1) == 'u')
- || (*curwin->w_p_scl == 'n' && *(curwin->w_p_scl + 1) == 'u'))
- && (curwin->w_p_nu || curwin->w_p_rnu)) {
- curwin->w_nrwidth_line_count = 0;
- }
- } else if (varp == &curwin->w_p_fdc || varp == &curwin->w_allbuf_opt.wo_fdc) {
- // 'foldcolumn'
- if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_pt) {
- // 'pastetoggle': translate key codes like in a mapping
- if (*p_pt) {
- p = NULL;
- (void)replace_termcodes((char *)p_pt,
- STRLEN(p_pt),
- (char **)&p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
- CPO_TO_CPO_FLAGS);
- if (p != NULL) {
- free_string_option(p_pt);
- p_pt = p;
- }
- }
- } else if (varp == &p_bs) { // 'backspace'
- if (ascii_isdigit(*p_bs)) {
- if (*p_bs > '3' || p_bs[1] != NUL) {
- errmsg = e_invarg;
- }
- } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_bo) {
- if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_tc) { // 'tagcase'
- unsigned int *flags;
-
- if (opt_flags & OPT_LOCAL) {
- p = curbuf->b_p_tc;
- flags = &curbuf->b_tc_flags;
- } else {
- p = p_tc;
- flags = &tc_flags;
- }
-
- if ((opt_flags & OPT_LOCAL) && *p == NUL) {
- // make the local value empty: use the global value
- *flags = 0;
- } else if (*p == NUL
- || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_cmp) { // 'casemap'
- if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_dip) { // 'diffopt'
- if (diffopt_changed() == FAIL) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) { // 'foldmethod'
- if (check_opt_strings(*varp, p_fdm_values, false) != OK
- || *curwin->w_p_fdm == NUL) {
- errmsg = e_invarg;
- } else {
- foldUpdateAll(curwin);
- if (foldmethodIsDiff(curwin)) {
- newFoldLevel();
- }
- }
- } else if (varp == &curwin->w_p_fde) { // 'foldexpr'
- if (foldmethodIsExpr(curwin)) {
- foldUpdateAll(curwin);
- }
- } else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) { // 'foldmarker'
- p = (char_u *)vim_strchr((char *)(*varp), ',');
- if (p == NULL) {
- errmsg = N_("E536: comma required");
- } else if (p == *varp || p[1] == NUL) {
- errmsg = e_invarg;
- } else if (foldmethodIsMarker(curwin)) {
- foldUpdateAll(curwin);
- }
- } else if (gvarp == &p_cms) { // 'commentstring'
- if (**varp != NUL && strstr((char *)(*varp), "%s") == NULL) {
- errmsg = N_("E537: 'commentstring' must be empty or contain %s");
- }
- } else if (varp == &p_fdo) { // 'foldopen'
- if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_fcl) { // 'foldclose'
- if (check_opt_strings(p_fcl, p_fcl_values, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) { // 'foldignore'
- if (foldmethodIsIndent(curwin)) {
- foldUpdateAll(curwin);
- }
- } else if (gvarp == &p_ve) { // 'virtualedit'
- char_u *ve = p_ve;
- unsigned int *flags = &ve_flags;
-
- if (opt_flags & OPT_LOCAL) {
- ve = curwin->w_p_ve;
- flags = &curwin->w_ve_flags;
- }
-
- if ((opt_flags & OPT_LOCAL) && *ve == NUL) {
- // make the local value empty: use the global value
- *flags = 0;
- } else {
- if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
- errmsg = e_invarg;
- } else if (STRCMP(p_ve, oldval) != 0) {
- // Recompute cursor position in case the new 've' setting
- // changes something.
- validate_virtcol();
- coladvance(curwin->w_virtcol);
- }
- }
- } else if (varp == &p_csqf) {
- if (p_csqf != NULL) {
- p = p_csqf;
- while (*p != NUL) {
- if (vim_strchr(CSQF_CMDS, *p) == NULL
- || p[1] == NUL
- || vim_strchr(CSQF_FLAGS, p[1]) == NULL
- || (p[2] != NUL && p[2] != ',')) {
- errmsg = e_invarg;
- break;
- } else if (p[2] == NUL) {
- break;
- } else {
- p += 3;
- }
- }
- }
- } else if (gvarp == &p_cino) { // 'cinoptions'
- // TODO(vim): recognize errors
- parse_cino(curbuf);
- // inccommand
- } else if (varp == &p_icm) {
- if (check_opt_strings(p_icm, p_icm_values, false) != OK) {
- errmsg = e_invarg;
- }
- } else if (gvarp == &p_ft) {
- if (!valid_filetype(*varp)) {
- errmsg = e_invarg;
- } else {
- value_changed = STRCMP(oldval, *varp) != 0;
-
- // Since we check the value, there is no need to set P_INSECURE,
- // even when the value comes from a modeline.
- *value_checked = true;
- }
- } else if (gvarp == &p_syn) {
- if (!valid_filetype(*varp)) {
- errmsg = e_invarg;
- } else {
- value_changed = STRCMP(oldval, *varp) != 0;
-
- // Since we check the value, there is no need to set P_INSECURE,
- // even when the value comes from a modeline.
- *value_checked = true;
- }
- } else if (varp == &curwin->w_p_winhl) {
- if (!parse_winhl_opt(curwin)) {
- errmsg = e_invarg;
- }
- } else if (varp == &p_tpf) {
- if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
- errmsg = e_invarg;
- }
- } else if (varp == &(curbuf->b_p_vsts)) { // 'varsofttabstop'
- char_u *cp;
-
- if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
- XFREE_CLEAR(curbuf->b_p_vsts_array);
- } else {
- for (cp = *varp; *cp; cp++) {
- if (ascii_isdigit(*cp)) {
- continue;
- }
- if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
- continue;
- }
- errmsg = e_invarg;
- break;
- }
- if (errmsg == NULL) {
- long *oldarray = curbuf->b_p_vsts_array;
- if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
- xfree(oldarray);
- } else {
- errmsg = e_invarg;
- }
- }
- }
- } else if (varp == &(curbuf->b_p_vts)) { // 'vartabstop'
- char_u *cp;
-
- if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
- XFREE_CLEAR(curbuf->b_p_vts_array);
- } else {
- for (cp = *varp; *cp; cp++) {
- if (ascii_isdigit(*cp)) {
- continue;
- }
- if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
- continue;
- }
- errmsg = e_invarg;
- break;
- }
- if (errmsg == NULL) {
- long *oldarray = curbuf->b_p_vts_array;
- if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
- xfree(oldarray);
- if (foldmethodIsIndent(curwin)) {
- foldUpdateAll(curwin);
- }
- } else {
- errmsg = e_invarg;
- }
- }
- }
- } else if (varp == &p_qftf) {
- if (!qf_process_qftf_option()) {
- errmsg = e_invarg;
- }
- } else {
- // Options that are a list of flags.
- p = NULL;
- if (varp == &p_ww) { // 'whichwrap'
- p = (char_u *)WW_ALL;
- }
- if (varp == &p_shm) { // 'shortmess'
- p = (char_u *)SHM_ALL;
- } else if (varp == (char_u **)&(p_cpo)) { // 'cpoptions'
- p = (char_u *)CPO_VI;
- } else if (varp == &(curbuf->b_p_fo)) { // 'formatoptions'
- p = (char_u *)FO_ALL;
- } else if (varp == &curwin->w_p_cocu) { // 'concealcursor'
- p = (char_u *)COCU_ALL;
- } else if (varp == &p_mouse) { // 'mouse'
- p = (char_u *)MOUSE_ALL;
- }
- if (p != NULL) {
- for (s = *varp; *s; s++) {
- if (vim_strchr((char *)p, *s) == NULL) {
- errmsg = illegal_char(errbuf, errbuflen, *s);
- break;
- }
- }
- }
- }
-
- /*
- * If error detected, restore the previous value.
- */
- if (errmsg != NULL) {
- free_string_option(*varp);
- *varp = oldval;
- /*
- * When resetting some values, need to act on it.
- */
- if (did_chartab) {
- (void)init_chartab();
- }
- } else {
- // Remember where the option was set.
- set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
- // Free string options that are in allocated memory.
- // Use "free_oldval", because recursiveness may change the flags under
- // our fingers (esp. init_highlight()).
- if (free_oldval) {
- free_string_option(oldval);
- }
- options[opt_idx].flags |= P_ALLOCED;
-
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && ((int)options[opt_idx].indir & PV_BOTH)) {
- /* global option with local value set to use global value; free
- * the local value and make it empty */
- p = get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
- free_string_option(*(char_u **)p);
- *(char_u **)p = empty_option;
- } else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
- // May set global value for local option.
- set_string_option_global(opt_idx, varp);
- }
-
- /*
- * Trigger the autocommand only after setting the flags.
- */
- // When 'syntax' is set, load the syntax of that name
- if (varp == &(curbuf->b_p_syn)) {
- static int syn_recursive = 0;
-
- syn_recursive++;
- // Only pass true for "force" when the value changed or not used
- // recursively, to avoid endless recurrence.
- apply_autocmds(EVENT_SYNTAX, (char *)curbuf->b_p_syn, curbuf->b_fname,
- value_changed || syn_recursive == 1, curbuf);
- curbuf->b_flags |= BF_SYN_SET;
- syn_recursive--;
- } else if (varp == &(curbuf->b_p_ft)) {
- // 'filetype' is set, trigger the FileType autocommand
- // Skip this when called from a modeline and the filetype was
- // already set to this value.
- if (!(opt_flags & OPT_MODELINE) || value_changed) {
- static int ft_recursive = 0;
- int secure_save = secure;
-
- // Reset the secure flag, since the value of 'filetype' has
- // been checked to be safe.
- secure = 0;
-
- ft_recursive++;
- did_filetype = true;
- // Only pass true for "force" when the value changed or not
- // used recursively, to avoid endless recurrence.
- apply_autocmds(EVENT_FILETYPE, (char *)curbuf->b_p_ft, curbuf->b_fname,
- value_changed || ft_recursive == 1, curbuf);
- ft_recursive--;
- // Just in case the old "curbuf" is now invalid
- if (varp != &(curbuf->b_p_ft)) {
- varp = NULL;
- }
- secure = secure_save;
- }
- }
- if (varp == &(curwin->w_s->b_p_spl)) {
- char_u fname[200];
- char_u *q = curwin->w_s->b_p_spl;
-
- // Skip the first name if it is "cjk".
- if (STRNCMP(q, "cjk,", 4) == 0) {
- q += 4;
- }
-
- /*
- * Source the spell/LANG.vim in 'runtimepath'.
- * They could set 'spellcapcheck' depending on the language.
- * Use the first name in 'spelllang' up to '_region' or
- * '.encoding'.
- */
- for (p = q; *p != NUL; p++) {
- if (!ASCII_ISALNUM(*p) && *p != '-') {
- break;
- }
- }
- if (p > q) {
- vim_snprintf((char *)fname, sizeof(fname), "spell/%.*s.vim",
- (int)(p - q), q);
- source_runtime((char *)fname, DIP_ALL);
- }
- }
- }
-
- if (varp == &p_mouse) {
- setmouse(); // in case 'mouse' changed
- }
-
- if (curwin->w_curswant != MAXCOL
- && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) {
- curwin->w_set_curswant = true;
- }
-
- check_redraw(options[opt_idx].flags);
-
- return errmsg;
-}
-
-/// Simple int comparison function for use with qsort()
-static int int_cmp(const void *a, const void *b)
-{
- return *(const int *)a - *(const int *)b;
-}
-
-/// Handle setting 'signcolumn' for value 'val'
-///
-/// @return OK when the value is valid, FAIL otherwise
-int check_signcolumn(char_u *val)
-{
- if (*val == NUL) {
- return FAIL;
- }
- // check for basic match
- if (check_opt_strings(val, p_scl_values, false) == OK) {
- return OK;
- }
-
- // check for 'auto:<NUMBER>-<NUMBER>'
- if (STRLEN(val) == 8
- && !STRNCMP(val, "auto:", 5)
- && ascii_isdigit(val[5])
- && val[6] == '-'
- && ascii_isdigit(val[7])) {
- int min = val[5] - '0';
- int max = val[7] - '0';
- if (min < 1 || max < 2 || min > 8 || max > 9 || min >= max) {
- return FAIL;
- }
- return OK;
- }
-
- return FAIL;
-}
-
-/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
-///
-/// @return error message, NULL if it's OK.
-char *check_colorcolumn(win_T *wp)
-{
- char_u *s;
- int col;
- unsigned int count = 0;
- int color_cols[256];
- int j = 0;
-
- if (wp->w_buffer == NULL) {
- return NULL; // buffer was closed
- }
-
- for (s = wp->w_p_cc; *s != NUL && count < 255;) {
- if (*s == '-' || *s == '+') {
- // -N and +N: add to 'textwidth'
- col = (*s == '-') ? -1 : 1;
- s++;
- if (!ascii_isdigit(*s)) {
- return e_invarg;
- }
- col = col * getdigits_int((char **)&s, true, 0);
- if (wp->w_buffer->b_p_tw == 0) {
- goto skip; // 'textwidth' not set, skip this item
- }
- assert((col >= 0
- && wp->w_buffer->b_p_tw <= INT_MAX - col
- && wp->w_buffer->b_p_tw + col >= INT_MIN)
- || (col < 0
- && wp->w_buffer->b_p_tw >= INT_MIN - col
- && wp->w_buffer->b_p_tw + col <= INT_MAX));
- col += (int)wp->w_buffer->b_p_tw;
- if (col < 0) {
- goto skip;
- }
- } else if (ascii_isdigit(*s)) {
- col = getdigits_int((char **)&s, true, 0);
- } else {
- return e_invarg;
- }
- color_cols[count++] = col - 1; // 1-based to 0-based
-skip:
- if (*s == NUL) {
- break;
- }
- if (*s != ',') {
- return e_invarg;
- }
- if (*++s == NUL) {
- return e_invarg; // illegal trailing comma as in "set cc=80,"
- }
- }
-
- xfree(wp->w_p_cc_cols);
- if (count == 0) {
- wp->w_p_cc_cols = NULL;
- } else {
- wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
- /* sort the columns for faster usage on screen redraw inside
- * win_line() */
- qsort(color_cols, count, sizeof(int), int_cmp);
-
- for (unsigned int i = 0; i < count; i++) {
- // skip duplicates
- if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
- wp->w_p_cc_cols[j++] = color_cols[i];
- }
- }
- wp->w_p_cc_cols[j] = -1; // end marker
- }
-
- return NULL; // no error
-}
-
void check_blending(win_T *wp)
{
wp->w_grid_alloc.blending =
wp->w_p_winbl > 0 || (wp->w_floating && wp->w_float_config.shadow);
}
-/// Calls mb_cptr2char_adv(p) and returns the character.
-/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
-/// Returns 0 for invalid hex or invalid UTF-8 byte.
-static int get_encoded_char_adv(char_u **p)
-{
- char_u *s = *p;
-
- if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
- int64_t num = 0;
- int bytes;
- int n;
- for (bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
- *p += 2;
- n = hexhex2nr(*p);
- if (n < 0) {
- return 0;
- }
- num = num * 256 + n;
- }
- *p += 2;
- return (int)num;
- }
-
- // TODO(bfredl): use schar_T representation and utfc_ptr2len
- int clen = utf_ptr2len((char *)s);
- int c = mb_cptr2char_adv((const char_u **)p);
- if (clen == 1 && c > 127) { // Invalid UTF-8 byte
- return 0;
- }
- return c;
-}
-
-/// Handle setting 'listchars' or 'fillchars'.
-/// Assume monocell characters
-///
-/// @param varp either &curwin->w_p_lcs or &curwin->w_p_fcs
-/// @return error message, NULL if it's OK.
-static char *set_chars_option(win_T *wp, char_u **varp, bool set)
-{
- int round, i, len, len2, entries;
- char_u *p, *s;
- int c1;
- int c2 = 0;
- int c3 = 0;
- char_u *last_multispace = NULL; // Last occurrence of "multispace:"
- char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
- int multispace_len = 0; // Length of lcs-multispace string
- int lead_multispace_len = 0; // Length of lcs-leadmultispace string
-
- struct chars_tab {
- int *cp; ///< char value
- char *name; ///< char id
- int def; ///< default value
- };
- struct chars_tab *tab;
-
- struct chars_tab fcs_tab[] = {
- { &wp->w_p_fcs_chars.stl, "stl", ' ' },
- { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
- { &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
- { &wp->w_p_fcs_chars.horiz, "horiz", 9472 }, // ─
- { &wp->w_p_fcs_chars.horizup, "horizup", 9524 }, // â”´
- { &wp->w_p_fcs_chars.horizdown, "horizdown", 9516 }, // ┬
- { &wp->w_p_fcs_chars.vert, "vert", 9474 }, // │
- { &wp->w_p_fcs_chars.vertleft, "vertleft", 9508 }, // ┤
- { &wp->w_p_fcs_chars.vertright, "vertright", 9500 }, // ├
- { &wp->w_p_fcs_chars.verthoriz, "verthoriz", 9532 }, // ┼
- { &wp->w_p_fcs_chars.fold, "fold", 183 }, // ·
- { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
- { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
- { &wp->w_p_fcs_chars.foldsep, "foldsep", 9474 }, // │
- { &wp->w_p_fcs_chars.diff, "diff", '-' },
- { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
- { &wp->w_p_fcs_chars.eob, "eob", '~' },
- };
- struct chars_tab lcs_tab[] = {
- { &wp->w_p_lcs_chars.eol, "eol", NUL },
- { &wp->w_p_lcs_chars.ext, "extends", NUL },
- { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
- { &wp->w_p_lcs_chars.prec, "precedes", NUL },
- { &wp->w_p_lcs_chars.space, "space", NUL },
- { &wp->w_p_lcs_chars.tab2, "tab", NUL },
- { &wp->w_p_lcs_chars.lead, "lead", NUL },
- { &wp->w_p_lcs_chars.trail, "trail", NUL },
- { &wp->w_p_lcs_chars.conceal, "conceal", NUL },
- };
-
- if (varp == &p_lcs || varp == &wp->w_p_lcs) {
- tab = lcs_tab;
- entries = ARRAY_SIZE(lcs_tab);
- if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
- varp = &p_lcs;
- }
- } else {
- tab = fcs_tab;
- entries = ARRAY_SIZE(fcs_tab);
- if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
- varp = &p_fcs;
- }
- if (*p_ambw == 'd') {
- // XXX: If ambiwidth=double then some characters take 2 columns,
- // which is forbidden (TUI limitation?). Set old defaults.
- fcs_tab[3].def = '-';
- fcs_tab[4].def = '-';
- fcs_tab[5].def = '-';
- fcs_tab[6].def = '|';
- fcs_tab[7].def = '|';
- fcs_tab[8].def = '|';
- fcs_tab[9].def = '+';
- fcs_tab[10].def = '-';
- fcs_tab[13].def = '|';
- }
- }
-
- // first round: check for valid value, second round: assign values
- for (round = 0; round <= (set ? 1 : 0); round++) {
- if (round > 0) {
- // After checking that the value is valid: set defaults
- for (i = 0; i < entries; i++) {
- if (tab[i].cp != NULL) {
- *(tab[i].cp) = tab[i].def;
- }
- }
- if (varp == &p_lcs || varp == &wp->w_p_lcs) {
- wp->w_p_lcs_chars.tab1 = NUL;
- wp->w_p_lcs_chars.tab3 = NUL;
-
- xfree(wp->w_p_lcs_chars.multispace);
- if (multispace_len > 0) {
- wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
- wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
- } else {
- wp->w_p_lcs_chars.multispace = NULL;
- }
-
- xfree(wp->w_p_lcs_chars.leadmultispace);
- if (lead_multispace_len > 0) {
- wp->w_p_lcs_chars.leadmultispace
- = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
- wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
- } else {
- wp->w_p_lcs_chars.leadmultispace = NULL;
- }
- }
- }
- p = *varp;
- while (*p) {
- for (i = 0; i < entries; i++) {
- len = (int)STRLEN(tab[i].name);
- if (STRNCMP(p, tab[i].name, len) == 0
- && p[len] == ':'
- && p[len + 1] != NUL) {
- c2 = c3 = 0;
- s = p + len + 1;
- c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
- if (*s == NUL) {
- return e_invarg;
- }
- c2 = get_encoded_char_adv(&s);
- if (c2 == 0 || char2cells(c2) > 1) {
- return e_invarg;
- }
- if (!(*s == ',' || *s == NUL)) {
- c3 = get_encoded_char_adv(&s);
- if (c3 == 0 || char2cells(c3) > 1) {
- return e_invarg;
- }
- }
- }
- if (*s == ',' || *s == NUL) {
- if (round > 0) {
- if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
- wp->w_p_lcs_chars.tab1 = c1;
- wp->w_p_lcs_chars.tab2 = c2;
- wp->w_p_lcs_chars.tab3 = c3;
- } else if (tab[i].cp != NULL) {
- *(tab[i].cp) = c1;
- }
- }
- p = s;
- break;
- }
- }
- }
-
- if (i == entries) {
- len = (int)STRLEN("multispace");
- len2 = (int)STRLEN("leadmultispace");
- if ((varp == &p_lcs || varp == &wp->w_p_lcs)
- && STRNCMP(p, "multispace", len) == 0
- && p[len] == ':'
- && p[len + 1] != NUL) {
- s = p + len + 1;
- if (round == 0) {
- // Get length of lcs-multispace string in the first round
- last_multispace = p;
- multispace_len = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- multispace_len++;
- }
- if (multispace_len == 0) {
- // lcs-multispace cannot be an empty string
- return e_invarg;
- }
- p = s;
- } else {
- int multispace_pos = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (p == last_multispace) {
- wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
- }
- }
- p = s;
- }
- } else if ((varp == &p_lcs || varp == &wp->w_p_lcs)
- && STRNCMP(p, "leadmultispace", len2) == 0
- && p[len2] == ':'
- && p[len2 + 1] != NUL) {
- s = p + len2 + 1;
- if (round == 0) {
- // get length of lcs-leadmultispace string in first round
- last_lmultispace = p;
- lead_multispace_len = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (c1 == 0 || char2cells(c1) > 1) {
- return e_invarg;
- }
- lead_multispace_len++;
- }
- if (lead_multispace_len == 0) {
- // lcs-leadmultispace cannot be an empty string
- return e_invarg;
- }
- p = s;
- } else {
- int multispace_pos = 0;
- while (*s != NUL && *s != ',') {
- c1 = get_encoded_char_adv(&s);
- if (p == last_lmultispace) {
- wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
- }
- }
- p = s;
- }
- } else {
- return e_invarg;
- }
- }
- if (*p == ',') {
- p++;
- }
- }
- }
-
- return NULL; // no error
-}
-
-/// Check validity of options with the 'statusline' format.
-/// Return an untranslated error message or NULL.
-char *check_stl_option(char *s)
-{
- int groupdepth = 0;
- static char errbuf[80];
-
- while (*s) {
- // Check for valid keys after % sequences
- while (*s && *s != '%') {
- s++;
- }
- if (!*s) {
- break;
- }
- s++;
- if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) {
- s++;
- continue;
- }
- if (*s == ')') {
- s++;
- if (--groupdepth < 0) {
- break;
- }
- continue;
- }
- if (*s == '-') {
- s++;
- }
- while (ascii_isdigit(*s)) {
- s++;
- }
- if (*s == STL_USER_HL) {
- continue;
- }
- if (*s == '.') {
- s++;
- while (*s && ascii_isdigit(*s)) {
- s++;
- }
- }
- if (*s == '(') {
- groupdepth++;
- continue;
- }
- if (vim_strchr(STL_ALL, *s) == NULL) {
- return illegal_char(errbuf, sizeof(errbuf), *s);
- }
- if (*s == '{') {
- bool reevaluate = (*++s == '%');
-
- if (reevaluate && *++s == '}') {
- // "}" is not allowed immediately after "%{%"
- return illegal_char(errbuf, sizeof(errbuf), '}');
- }
- while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) {
- s++;
- }
- if (*s != '}') {
- return e_unclosed_expression_sequence;
- }
- }
- }
- if (groupdepth != 0) {
- return e_unbalanced_groups;
- }
- return NULL;
-}
-
-static char *did_set_spell_option(bool is_spellfile)
+/// Handle setting `winhighlight' in window "wp"
+bool parse_winhl_opt(win_T *wp)
{
- char *errmsg = NULL;
+ const char *p = (const char *)wp->w_p_winhl;
- if (is_spellfile) {
- int l = (int)STRLEN(curwin->w_s->b_p_spf);
- if (l > 0
- && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
- errmsg = e_invarg;
+ if (!*p) {
+ if (wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) {
+ wp->w_ns_hl = 0;
+ wp->w_hl_needs_update = true;
}
- }
- if (errmsg == NULL) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == curbuf && wp->w_p_spell) {
- errmsg = did_set_spelllang(wp);
- break;
- }
- }
+ return true;
}
- return errmsg;
-}
-
-/// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
-/// Return error message when failed, NULL when OK.
-static char *compile_cap_prog(synblock_T *synblock)
- FUNC_ATTR_NONNULL_ALL
-{
- regprog_T *rp = synblock->b_cap_prog;
- char_u *re;
-
- if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
- synblock->b_cap_prog = NULL;
+ if (wp->w_ns_hl_winhl == 0) {
+ wp->w_ns_hl_winhl = (int)nvim_create_namespace(NULL_STRING);
} else {
- // Prepend a ^ so that we only match at one column
- re = concat_str((char_u *)"^", synblock->b_p_spc);
- synblock->b_cap_prog = vim_regcomp((char *)re, RE_MAGIC);
- xfree(re);
- if (synblock->b_cap_prog == NULL) {
- synblock->b_cap_prog = rp; // restore the previous program
- return e_invarg;
- }
+ // namespace already exist. invalidate existing items
+ DecorProvider *dp = get_decor_provider(wp->w_ns_hl_winhl, true);
+ dp->hl_valid++;
}
+ wp->w_ns_hl = wp->w_ns_hl_winhl;
+ int ns_hl = wp->w_ns_hl;
- vim_regfree(rp);
- return NULL;
-}
-
-/// Handle setting `winhighlight' in window "wp"
-static bool parse_winhl_opt(win_T *wp)
-{
- int w_hl_id_normal = 0;
- int w_hl_ids[HLF_COUNT] = { 0 };
- int hlf;
-
- const char *p = (const char *)wp->w_p_winhl;
while (*p) {
char *colon = strchr(p, ':');
if (!colon) {
@@ -3973,34 +1910,22 @@ static bool parse_winhl_opt(win_T *wp)
char *commap = xstrchrnul(hi, ',');
size_t len = (size_t)(commap - hi);
int hl_id = len ? syn_check_group(hi, len) : -1;
+ int hl_id_link = nlen ? syn_check_group(p, nlen) : 0;
- if (strncmp("Normal", p, nlen) == 0) {
- w_hl_id_normal = hl_id;
- } else {
- for (hlf = 0; hlf < HLF_COUNT; hlf++) {
- if (strlen(hlf_names[hlf]) == nlen
- && strncmp(hlf_names[hlf], p, nlen) == 0) {
- w_hl_ids[hlf] = hl_id;
- break;
- }
- }
- if (hlf == HLF_COUNT) {
- return false;
- }
- }
+ HlAttrs attrs = HLATTRS_INIT;
+ attrs.rgb_ae_attr |= HL_GLOBAL;
+ ns_hl_def(ns_hl, hl_id_link, attrs, hl_id, NULL);
p = *commap ? commap + 1 : "";
}
- wp->w_hl_id_normal = w_hl_id_normal;
- memcpy(wp->w_hl_ids, w_hl_ids, sizeof(w_hl_ids));
wp->w_hl_needs_update = true;
return true;
}
-// Set the script_ctx for an option, taking care of setting the buffer- or
-// window-local value.
-static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
+/// Set the script_ctx for an option, taking care of setting the buffer- or
+/// window-local value.
+void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
{
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
int indir = (int)options[opt_idx].indir;
@@ -4009,7 +1934,7 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
.script_ctx = {
script_ctx.sc_sid,
script_ctx.sc_seq,
- script_ctx.sc_lnum + sourcing_lnum
+ script_ctx.sc_lnum + SOURCING_LNUM
},
current_channel_id
};
@@ -4041,6 +1966,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
{
int old_value = *(int *)varp;
int old_global_value = 0;
+ char *errmsg = NULL;
// Disallow changing some options from secure mode
if ((secure || sandbox != 0)
@@ -4145,7 +2071,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
paste_option_changed();
} else if ((int *)varp == &p_ic && p_hls) {
// when 'ignorecase' is set or reset and 'hlsearch' is set, redraw
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
} else if ((int *)varp == &p_hls) {
// when 'hlsearch' is set or reset: reset no_hlsearch
set_no_hlsearch(false);
@@ -4162,7 +2088,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
if (win->w_p_pvw && win != curwin) {
curwin->w_p_pvw = false;
- return N_("E590: A preview window already exists");
+ return e_preview_window_already_exists;
}
}
}
@@ -4221,10 +2147,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
} else if ((int *)varp == &curwin->w_p_spell) { // 'spell'
if (curwin->w_p_spell) {
- char *errmsg = did_set_spelllang(curwin);
- if (errmsg != NULL) {
- emsg(_(errmsg));
- }
+ errmsg = did_set_spelllang(curwin);
}
}
@@ -4243,7 +2166,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
// Enable Arabic shaping (major part of what Arabic requires)
if (!p_arshape) {
p_arshape = true;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
}
@@ -4261,7 +2184,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
p_deco = true;
// Force-set the necessary keymap for arabic.
- set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
+ errmsg = set_option_value("keymap", 0L, "arabic", OPT_LOCAL);
} else {
/*
* 'arabic' is reset, handle various sub-settings.
@@ -4341,7 +2264,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, const int va
}
check_redraw(options[opt_idx].flags);
- return NULL;
+ return errmsg;
}
/// Set the value of a number option, taking care of side effects
@@ -4785,51 +2708,8 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf,
return errmsg;
}
-/// Trigger the OptionSet autocommand.
-/// "opt_idx" is the index of the option being set.
-/// "opt_flags" can be OPT_LOCAL etc.
-/// "oldval" the old value
-/// "oldval_l" the old local value (only non-NULL if global and local value are set)
-/// "oldval_g" the old global value (only non-NULL if global and local value are set)
-/// "newval" the new value
-static void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l,
- char *oldval_g, char *newval)
-{
- // Don't do this recursively.
- if (oldval != NULL
- && newval != NULL
- && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
- char buf_type[7];
-
- vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
- (opt_flags & OPT_LOCAL) ? "local" : "global");
- set_vim_var_string(VV_OPTION_OLD, oldval, -1);
- set_vim_var_string(VV_OPTION_NEW, newval, -1);
- set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
- if (opt_flags & OPT_LOCAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
- }
- if (opt_flags & OPT_GLOBAL) {
- set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1);
- }
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1);
- set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1);
- }
- if (opt_flags & OPT_MODELINE) {
- set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
- set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
- }
- apply_autocmds(EVENT_OPTIONSET, options[opt_idx].fullname, NULL, false, NULL);
- reset_v_option_vars();
- }
-}
-
/// Called after an option changed: check if something needs to be redrawn.
-static void check_redraw(uint32_t flags)
+void check_redraw(uint32_t flags)
{
// Careful: P_RCLR and P_RALL are a combination of other P_ flags
bool doclear = (flags & P_RCLR) == P_RCLR;
@@ -4843,15 +2723,15 @@ static void check_redraw(uint32_t flags)
changed_window_setting();
}
if (flags & P_RBUF) {
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
}
if (flags & P_RWINONLY) {
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
if (doclear) {
- redraw_all_later(CLEAR);
+ redraw_all_later(UPD_CLEAR);
} else if (all) {
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
}
@@ -5001,7 +2881,7 @@ bool set_tty_option(const char *name, char *value)
void set_tty_background(const char *value)
{
- if (option_was_set("bg") || strequal((char *)p_bg, value)) {
+ if (option_was_set("bg") || strequal(p_bg, value)) {
// background is already set... ignore
return;
}
@@ -5011,7 +2891,7 @@ void set_tty_background(const char *value)
? "autocmd VimEnter * ++once ++nested set bg=light"
: "autocmd VimEnter * ++once ++nested set bg=dark");
} else {
- set_option_value("bg", 0L, value, 0);
+ set_option_value_give_err("bg", 0L, value, 0);
reset_option_was_set("bg");
}
}
@@ -5021,7 +2901,7 @@ void set_tty_background(const char *value)
/// @param[in] arg Option name.
///
/// @return Option index or -1 if option was not found.
-static int findoption(const char *const arg)
+int findoption(const char *const arg)
FUNC_ATTR_NONNULL_ALL
{
return findoption_len(arg, strlen(arg));
@@ -5050,14 +2930,14 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, i
return gov_unknown;
}
- char_u *varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ char_u *varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
if (options[opt_idx].flags & P_STRING) {
if (varp == NULL) { // hidden option
return gov_hidden_string;
}
if (stringval != NULL) {
- if ((char_u **)varp == &p_pt) { // 'pastetoggle'
+ if ((char **)varp == &p_pt) { // 'pastetoggle'
*stringval = str2special_save(*(char **)(varp), false, false);
} else {
*stringval = xstrdup(*(char **)(varp));
@@ -5176,7 +3056,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
// only getting a pointer, no need to use aucmd_prepbuf()
curbuf = (buf_T *)from;
curwin->w_buffer = curbuf;
- varp = get_varp_scope(p, OPT_LOCAL);
+ varp = (char_u *)get_varp_scope(p, OPT_LOCAL);
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
}
@@ -5184,7 +3064,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
win_T *save_curwin = curwin;
curwin = (win_T *)from;
curbuf = curwin->w_buffer;
- varp = get_varp_scope(p, OPT_LOCAL);
+ varp = (char_u *)get_varp_scope(p, OPT_LOCAL);
curwin = save_curwin;
curbuf = curwin->w_buffer;
}
@@ -5207,6 +3087,49 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
return rv;
}
+/// Return the flags for the option at 'opt_idx'.
+uint32_t get_option_flags(int opt_idx)
+{
+ return options[opt_idx].flags;
+}
+
+/// Set a flag for the option at 'opt_idx'.
+void set_option_flag(int opt_idx, uint32_t flag)
+{
+ options[opt_idx].flags |= flag;
+}
+
+/// Clear a flag for the option at 'opt_idx'.
+void clear_option_flag(int opt_idx, uint32_t flag)
+{
+ options[opt_idx].flags &= ~flag;
+}
+
+/// Returns true if the option at 'opt_idx' is a global option
+bool is_global_option(int opt_idx)
+{
+ return options[opt_idx].indir == PV_NONE;
+}
+
+/// Returns true if the option at 'opt_idx' is a global option which also has a
+/// local value.
+int is_global_local_option(int opt_idx)
+{
+ return options[opt_idx].indir & PV_BOTH;
+}
+
+/// Returns true if the option at 'opt_idx' is a window-local option
+bool is_window_local_option(int opt_idx)
+{
+ return options[opt_idx].var == VAR_WIN;
+}
+
+/// Returns true if the option at 'opt_idx' is a hidden option
+bool is_hidden_option(int opt_idx)
+{
+ return options[opt_idx].var == NULL;
+}
+
/// Set the value of an option
///
/// @param[in] name Option name.
@@ -5217,7 +3140,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o
/// is cleared (the exact semantics of this depend
/// on the option).
///
-/// @return NULL on success, error message on error.
+/// @return NULL on success, an untranslated error message on error.
char *set_option_value(const char *const name, const long number, const char *const string,
const int opt_flags)
FUNC_ATTR_NONNULL_ARG(1)
@@ -5247,7 +3170,7 @@ char *set_option_value(const char *const name, const long number, const char *co
return set_string_option(opt_idx, s, opt_flags);
}
- varp = get_varp_scope(&(options[opt_idx]), opt_flags);
+ varp = (char_u *)get_varp_scope(&(options[opt_idx]), opt_flags);
if (varp != NULL) { // hidden option is not changed
if (number == 0 && string != NULL) {
int idx;
@@ -5287,6 +3210,18 @@ char *set_option_value(const char *const name, const long number, const char *co
return NULL;
}
+/// Call set_option_value() and when an error is returned report it.
+///
+/// @param opt_flags OPT_LOCAL or 0 (both)
+void set_option_value_give_err(const char *name, long number, const char *string, int opt_flags)
+{
+ char *errmsg = set_option_value(name, number, string, opt_flags);
+
+ if (errmsg != NULL) {
+ emsg(_(errmsg));
+ }
+}
+
/// Return true if "name" is a string option.
/// Returns false if option "name" does not exist.
bool is_string_option(const char *name)
@@ -5364,14 +3299,14 @@ static void showoptions(int all, int opt_flags)
item_count = 0;
for (p = &options[0]; p->fullname != NULL; p++) {
// apply :filter /pat/
- if (message_filtered((char_u *)p->fullname)) {
+ if (message_filtered(p->fullname)) {
continue;
}
varp = NULL;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) {
if (p->indir != PV_NONE) {
- varp = get_varp_scope(p, opt_flags);
+ varp = (char_u *)get_varp_scope(p, opt_flags);
}
} else {
varp = get_varp(p);
@@ -5475,13 +3410,12 @@ void ui_refresh_options(void)
/// @param opt_flags OPT_LOCAL or OPT_GLOBAL
static void showoneopt(vimoption_T *p, int opt_flags)
{
- char_u *varp;
int save_silent = silent_mode;
silent_mode = false;
info_message = true; // use mch_msg(), not mch_errmsg()
- varp = get_varp_scope(p, opt_flags);
+ char_u *varp = (char_u *)get_varp_scope(p, opt_flags);
// for 'modified' we also need to check if 'ff' or 'fenc' changed.
if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed
@@ -5527,7 +3461,7 @@ static void showoneopt(vimoption_T *p, int opt_flags)
int makeset(FILE *fd, int opt_flags, int local_only)
{
vimoption_T *p;
- char_u *varp; // currently used value
+ char *varp; // currently used value
char_u *varp_fresh; // local value
char_u *varp_local = NULL; // fresh value
char *cmd;
@@ -5564,7 +3498,7 @@ int makeset(FILE *fd, int opt_flags, int local_only)
continue;
}
// Global values are only written when not at the default value.
- if ((opt_flags & OPT_GLOBAL) && optval_default(p, varp)) {
+ if ((opt_flags & OPT_GLOBAL) && optval_default(p, (char_u *)varp)) {
continue;
}
@@ -5583,11 +3517,11 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// When fresh value of window-local option is not at the
// default, need to write it too.
if (!(opt_flags & OPT_GLOBAL) && !local_only) {
- varp_fresh = get_varp_scope(p, OPT_GLOBAL);
+ varp_fresh = (char_u *)get_varp_scope(p, OPT_GLOBAL);
if (!optval_default(p, varp_fresh)) {
round = 1;
- varp_local = varp;
- varp = varp_fresh;
+ varp_local = (char_u *)varp;
+ varp = (char *)varp_fresh;
}
}
}
@@ -5595,7 +3529,7 @@ int makeset(FILE *fd, int opt_flags, int local_only)
// Round 1: fresh value for window-local options.
// Round 2: other values
- for (; round <= 2; varp = varp_local, round++) {
+ for (; round <= 2; varp = (char *)varp_local, round++) {
if (round == 1 || (opt_flags & OPT_GLOBAL)) {
cmd = "set";
} else {
@@ -5623,8 +3557,7 @@ int makeset(FILE *fd, int opt_flags, int local_only)
}
do_endif = true;
}
- if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
- p->flags) == FAIL) {
+ if (put_setstring(fd, cmd, p->fullname, (char **)varp, p->flags) == FAIL) {
return FAIL;
}
if (do_endif) {
@@ -5645,29 +3578,25 @@ int makeset(FILE *fd, int opt_flags, int local_only)
int makefoldset(FILE *fd)
{
if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0)
- == FAIL
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0)
- == FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0)
- == FAIL
+ || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0) == FAIL
+ || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0) == FAIL
+ || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0) == FAIL
|| put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
|| put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
|| put_setnum(fd, "setlocal", "fdn", &curwin->w_p_fdn) == FAIL
- || put_setbool(fd, "setlocal", "fen",
- curwin->w_p_fen) == FAIL) {
+ || put_setbool(fd, "setlocal", "fen", curwin->w_p_fen) == FAIL) {
return FAIL;
}
return OK;
}
-static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint64_t flags)
+static int put_setstring(FILE *fd, char *cmd, char *name, char **valuep, uint64_t flags)
{
char_u *s;
char_u *buf = NULL;
char_u *part = NULL;
- char_u *p;
+ char *p;
if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
@@ -5677,7 +3606,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
// options some characters have to be escaped with
// CTRL-V or backslash
if (valuep == &p_pt) {
- s = *valuep;
+ s = (char_u *)(*valuep);
while (*s != NUL) {
if (put_escstr(fd, (char_u *)str2special((const char **)&s, false,
false), 2)
@@ -5690,27 +3619,27 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
// replace home directory in the whole option value into "buf"
buf = xmalloc(size);
- home_replace(NULL, (char *)(*valuep), (char *)buf, size, false);
+ home_replace(NULL, *valuep, (char *)buf, size, false);
// If the option value is longer than MAXPATHL, we need to append
// each comma separated part of the option separately, so that it
// can be expanded when read back.
if (size >= MAXPATHL && (flags & P_COMMA) != 0
- && vim_strchr((char *)(*valuep), ',') != NULL) {
+ && vim_strchr(*valuep, ',') != NULL) {
part = xmalloc(size);
// write line break to clear the option, e.g. ':set rtp='
if (put_eol(fd) == FAIL) {
goto fail;
}
- p = buf;
+ p = (char *)buf;
while (*p != NUL) {
// for each comma separated option part, append value to
// the option, :set rtp+=value
if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
goto fail;
}
- (void)copy_option_part((char **)&p, (char *)part, size, ",");
+ (void)copy_option_part(&p, (char *)part, size, ",");
if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
goto fail;
}
@@ -5724,7 +3653,7 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, uint6
return FAIL;
}
xfree(buf);
- } else if (put_escstr(fd, *valuep, 2) == FAIL) {
+ } else if (put_escstr(fd, (char_u *)(*valuep), 2) == FAIL) {
return FAIL;
}
}
@@ -5771,47 +3700,6 @@ static int put_setbool(FILE *fd, char *cmd, char *name, int value)
return OK;
}
-/// Compute columns for ruler and shown command. 'sc_col' is also used to
-/// decide what the maximum length of a message on the status line can be.
-/// If there is a status line for the last window, 'sc_col' is independent
-/// of 'ru_col'.
-
-#define COL_RULER 17 // columns needed by standard ruler
-
-void comp_col(void)
-{
- int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
-
- sc_col = 0;
- ru_col = 0;
- if (p_ru) {
- ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
- // no last status line, adjust sc_col
- if (!last_has_status) {
- sc_col = ru_col;
- }
- }
- if (p_sc) {
- sc_col += SHOWCMD_COLS;
- if (!p_ru || last_has_status) { // no need for separating space
- sc_col++;
- }
- }
- assert(sc_col >= 0
- && INT_MIN + sc_col <= Columns);
- sc_col = Columns - sc_col;
- assert(ru_col >= 0
- && INT_MIN + ru_col <= Columns);
- ru_col = Columns - ru_col;
- if (sc_col <= 0) { // screen too narrow, will become a mess
- sc_col = 1;
- }
- if (ru_col <= 0) {
- ru_col = 1;
- }
- set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
-}
-
// Unset local option value, similar to ":set opt<".
void unset_global_local_option(char *name, void *from)
{
@@ -5891,7 +3779,7 @@ void unset_global_local_option(char *name, void *from)
clear_string_option(&((win_T *)from)->w_p_stl);
break;
case PV_WBR:
- clear_string_option((char_u **)&((win_T *)from)->w_p_wbr);
+ clear_string_option(&((win_T *)from)->w_p_wbr);
break;
case PV_UL:
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
@@ -5905,12 +3793,12 @@ void unset_global_local_option(char *name, void *from)
case PV_LCS:
clear_string_option(&((win_T *)from)->w_p_lcs);
set_chars_option((win_T *)from, &((win_T *)from)->w_p_lcs, true);
- redraw_later((win_T *)from, NOT_VALID);
+ redraw_later((win_T *)from, UPD_NOT_VALID);
break;
case PV_FCS:
clear_string_option(&((win_T *)from)->w_p_fcs);
set_chars_option((win_T *)from, &((win_T *)from)->w_p_fcs, true);
- redraw_later((win_T *)from, NOT_VALID);
+ redraw_later((win_T *)from, UPD_NOT_VALID);
break;
case PV_VE:
clear_string_option(&((win_T *)from)->w_p_ve);
@@ -5920,76 +3808,83 @@ void unset_global_local_option(char *name, void *from)
}
/// Get pointer to option variable, depending on local or global scope.
-static char_u *get_varp_scope(vimoption_T *p, int opt_flags)
+static char *get_varp_scope(vimoption_T *p, int opt_flags)
{
if ((opt_flags & OPT_GLOBAL) && p->indir != PV_NONE) {
if (p->var == VAR_WIN) {
- return (char_u *)GLOBAL_WO(get_varp(p));
+ return GLOBAL_WO(get_varp(p));
}
- return p->var;
+ return (char *)p->var;
}
if ((opt_flags & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) {
switch ((int)p->indir) {
case PV_FP:
- return (char_u *)&(curbuf->b_p_fp);
+ return (char *)&(curbuf->b_p_fp);
case PV_EFM:
- return (char_u *)&(curbuf->b_p_efm);
+ return (char *)&(curbuf->b_p_efm);
case PV_GP:
- return (char_u *)&(curbuf->b_p_gp);
+ return (char *)&(curbuf->b_p_gp);
case PV_MP:
- return (char_u *)&(curbuf->b_p_mp);
+ return (char *)&(curbuf->b_p_mp);
case PV_EP:
- return (char_u *)&(curbuf->b_p_ep);
+ return (char *)&(curbuf->b_p_ep);
case PV_KP:
- return (char_u *)&(curbuf->b_p_kp);
+ return (char *)&(curbuf->b_p_kp);
case PV_PATH:
- return (char_u *)&(curbuf->b_p_path);
+ return (char *)&(curbuf->b_p_path);
case PV_AR:
- return (char_u *)&(curbuf->b_p_ar);
+ return (char *)&(curbuf->b_p_ar);
case PV_TAGS:
- return (char_u *)&(curbuf->b_p_tags);
+ return (char *)&(curbuf->b_p_tags);
case PV_TC:
- return (char_u *)&(curbuf->b_p_tc);
+ return (char *)&(curbuf->b_p_tc);
case PV_SISO:
- return (char_u *)&(curwin->w_p_siso);
+ return (char *)&(curwin->w_p_siso);
case PV_SO:
- return (char_u *)&(curwin->w_p_so);
+ return (char *)&(curwin->w_p_so);
case PV_DEF:
- return (char_u *)&(curbuf->b_p_def);
+ return (char *)&(curbuf->b_p_def);
case PV_INC:
- return (char_u *)&(curbuf->b_p_inc);
+ return (char *)&(curbuf->b_p_inc);
case PV_DICT:
- return (char_u *)&(curbuf->b_p_dict);
+ return (char *)&(curbuf->b_p_dict);
case PV_TSR:
- return (char_u *)&(curbuf->b_p_tsr);
+ return (char *)&(curbuf->b_p_tsr);
case PV_TSRFU:
- return (char_u *)&(curbuf->b_p_tsrfu);
+ return (char *)&(curbuf->b_p_tsrfu);
case PV_TFU:
- return (char_u *)&(curbuf->b_p_tfu);
+ return (char *)&(curbuf->b_p_tfu);
case PV_SBR:
- return (char_u *)&(curwin->w_p_sbr);
+ return (char *)&(curwin->w_p_sbr);
case PV_STL:
- return (char_u *)&(curwin->w_p_stl);
+ return (char *)&(curwin->w_p_stl);
case PV_WBR:
- return (char_u *)&(curwin->w_p_wbr);
+ return (char *)&(curwin->w_p_wbr);
case PV_UL:
- return (char_u *)&(curbuf->b_p_ul);
+ return (char *)&(curbuf->b_p_ul);
case PV_LW:
- return (char_u *)&(curbuf->b_p_lw);
+ return (char *)&(curbuf->b_p_lw);
case PV_BKC:
- return (char_u *)&(curbuf->b_p_bkc);
+ return (char *)&(curbuf->b_p_bkc);
case PV_MENC:
- return (char_u *)&(curbuf->b_p_menc);
+ return (char *)&(curbuf->b_p_menc);
case PV_FCS:
- return (char_u *)&(curwin->w_p_fcs);
+ return (char *)&(curwin->w_p_fcs);
case PV_LCS:
- return (char_u *)&(curwin->w_p_lcs);
+ return (char *)&(curwin->w_p_lcs);
case PV_VE:
- return (char_u *)&(curwin->w_p_ve);
+ return (char *)&(curwin->w_p_ve);
}
return NULL; // "cannot happen"
}
- return get_varp(p);
+ return (char *)get_varp(p);
+}
+
+/// Get pointer to option variable at 'opt_idx', depending on local or global
+/// scope.
+char *get_option_varp_scope(int opt_idx, int opt_flags)
+{
+ return get_varp_scope(&(options[opt_idx]), opt_flags);
}
/// Get pointer to option variable.
@@ -6303,13 +4198,25 @@ static char_u *get_varp(vimoption_T *p)
return (char_u *)&(curbuf->b_p_wm);
}
+/// Return a pointer to the variable for option at 'opt_idx'
+char_u *get_option_var(int opt_idx)
+{
+ return options[opt_idx].var;
+}
+
+/// Return the full name of the option at 'opt_idx'
+char *get_option_fullname(int opt_idx)
+{
+ return options[opt_idx].fullname;
+}
+
/// Get the value of 'equalprg', either the buffer-local one or the global one.
char_u *get_equalprg(void)
{
if (*curbuf->b_p_ep == NUL) {
return p_ep;
}
- return curbuf->b_p_ep;
+ return (char_u *)curbuf->b_p_ep;
}
/// Copy options from one window to another.
@@ -6318,7 +4225,15 @@ void win_copy_options(win_T *wp_from, win_T *wp_to)
{
copy_winopt(&wp_from->w_onebuf_opt, &wp_to->w_onebuf_opt);
copy_winopt(&wp_from->w_allbuf_opt, &wp_to->w_allbuf_opt);
- didset_window_options(wp_to);
+ didset_window_options(wp_to, true);
+}
+
+static char *copy_option_val(const char *val)
+{
+ if (val == empty_option) {
+ return empty_option; // no need to allocate memory
+ }
+ return xstrdup(val);
}
/// Copy the options from one winopt_T to another.
@@ -6329,21 +4244,23 @@ void copy_winopt(winopt_T *from, winopt_T *to)
{
to->wo_arab = from->wo_arab;
to->wo_list = from->wo_list;
+ to->wo_lcs = copy_option_val(from->wo_lcs);
+ to->wo_fcs = copy_option_val(from->wo_fcs);
to->wo_nu = from->wo_nu;
to->wo_rnu = from->wo_rnu;
- to->wo_ve = vim_strsave(from->wo_ve);
+ to->wo_ve = copy_option_val(from->wo_ve);
to->wo_ve_flags = from->wo_ve_flags;
to->wo_nuw = from->wo_nuw;
to->wo_rl = from->wo_rl;
- to->wo_rlc = vim_strsave(from->wo_rlc);
- to->wo_sbr = vim_strsave(from->wo_sbr);
- to->wo_stl = vim_strsave(from->wo_stl);
- to->wo_wbr = xstrdup(from->wo_wbr);
+ to->wo_rlc = copy_option_val(from->wo_rlc);
+ to->wo_sbr = copy_option_val(from->wo_sbr);
+ to->wo_stl = copy_option_val(from->wo_stl);
+ to->wo_wbr = copy_option_val(from->wo_wbr);
to->wo_wrap = from->wo_wrap;
to->wo_wrap_save = from->wo_wrap_save;
to->wo_lbr = from->wo_lbr;
to->wo_bri = from->wo_bri;
- to->wo_briopt = vim_strsave(from->wo_briopt);
+ to->wo_briopt = copy_option_val(from->wo_briopt);
to->wo_scb = from->wo_scb;
to->wo_scb_save = from->wo_scb_save;
to->wo_crb = from->wo_crb;
@@ -6351,32 +4268,28 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_spell = from->wo_spell;
to->wo_cuc = from->wo_cuc;
to->wo_cul = from->wo_cul;
- to->wo_culopt = vim_strsave(from->wo_culopt);
- to->wo_cc = vim_strsave(from->wo_cc);
+ to->wo_culopt = copy_option_val(from->wo_culopt);
+ to->wo_cc = copy_option_val(from->wo_cc);
to->wo_diff = from->wo_diff;
to->wo_diff_saved = from->wo_diff_saved;
- to->wo_cocu = vim_strsave(from->wo_cocu);
+ to->wo_cocu = copy_option_val(from->wo_cocu);
to->wo_cole = from->wo_cole;
- to->wo_fdc = vim_strsave(from->wo_fdc);
- to->wo_fdc_save = from->wo_diff_saved
- ? vim_strsave(from->wo_fdc_save) : empty_option;
+ to->wo_fdc = copy_option_val(from->wo_fdc);
+ to->wo_fdc_save = from->wo_diff_saved ? xstrdup(from->wo_fdc_save) : empty_option;
to->wo_fen = from->wo_fen;
to->wo_fen_save = from->wo_fen_save;
- to->wo_fdi = vim_strsave(from->wo_fdi);
+ to->wo_fdi = copy_option_val(from->wo_fdi);
to->wo_fml = from->wo_fml;
to->wo_fdl = from->wo_fdl;
to->wo_fdl_save = from->wo_fdl_save;
- to->wo_fdm = vim_strsave(from->wo_fdm);
- to->wo_fdm_save = from->wo_diff_saved
- ? vim_strsave(from->wo_fdm_save) : empty_option;
+ to->wo_fdm = copy_option_val(from->wo_fdm);
+ to->wo_fdm_save = from->wo_diff_saved ? xstrdup(from->wo_fdm_save) : empty_option;
to->wo_fdn = from->wo_fdn;
- to->wo_fde = vim_strsave(from->wo_fde);
- to->wo_fdt = vim_strsave(from->wo_fdt);
- to->wo_fmr = vim_strsave(from->wo_fmr);
- to->wo_scl = vim_strsave(from->wo_scl);
- to->wo_winhl = vim_strsave(from->wo_winhl);
- to->wo_fcs = vim_strsave(from->wo_fcs);
- to->wo_lcs = vim_strsave(from->wo_lcs);
+ to->wo_fde = copy_option_val(from->wo_fde);
+ to->wo_fdt = copy_option_val(from->wo_fdt);
+ to->wo_fmr = copy_option_val(from->wo_fmr);
+ to->wo_scl = copy_option_val(from->wo_scl);
+ to->wo_winhl = copy_option_val(from->wo_winhl);
to->wo_winbl = from->wo_winbl;
// Copy the script context so that we know were the value was last set.
@@ -6411,10 +4324,10 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_cocu);
check_string_option(&wop->wo_briopt);
check_string_option(&wop->wo_winhl);
- check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_lcs);
+ check_string_option(&wop->wo_fcs);
check_string_option(&wop->wo_ve);
- check_string_option((char_u **)&wop->wo_wbr);
+ check_string_option(&wop->wo_wbr);
}
/// Free the allocated memory inside a winopt_T.
@@ -6437,13 +4350,13 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_cocu);
clear_string_option(&wop->wo_briopt);
clear_string_option(&wop->wo_winhl);
- clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_lcs);
+ clear_string_option(&wop->wo_fcs);
clear_string_option(&wop->wo_ve);
- clear_string_option((char_u **)&wop->wo_wbr);
+ clear_string_option(&wop->wo_wbr);
}
-void didset_window_options(win_T *wp)
+void didset_window_options(win_T *wp, bool valid_cursor)
{
check_colorcolumn(wp);
briopt_check(wp);
@@ -6452,7 +4365,7 @@ void didset_window_options(win_T *wp)
set_chars_option(wp, &wp->w_p_lcs, true);
parse_winhl_opt(wp); // sets w_hl_needs_update also for w_p_winbl
check_blending(wp);
- set_winbar_win(wp, false);
+ set_winbar_win(wp, false, valid_cursor);
wp->w_grid_alloc.blending = wp->w_p_winbl > 0;
}
@@ -6515,14 +4428,14 @@ void buf_copy_options(buf_T *buf, int flags)
}
if (should_copy || (flags & BCO_ALWAYS)) {
- memset(buf->b_p_script_ctx, 0, sizeof(buf->b_p_script_ctx));
+ CLEAR_FIELD(buf->b_p_script_ctx);
init_buf_opt_idx();
// Don't copy the options specific to a help buffer when
// BCO_NOHELP is given or the options were initialized already
// (jumping back to a help file with CTRL-T or CTRL-O)
dont_do_help = ((flags & BCO_NOHELP) && buf->b_help) || buf->b_p_initialized;
if (dont_do_help) { // don't free b_p_isk
- save_p_isk = buf->b_p_isk;
+ save_p_isk = (char_u *)buf->b_p_isk;
buf->b_p_isk = NULL;
}
// Always free the allocated strings. If not already initialized,
@@ -6530,19 +4443,19 @@ void buf_copy_options(buf_T *buf, int flags)
if (!buf->b_p_initialized) {
free_buf_options(buf, true);
buf->b_p_ro = false; // don't copy readonly
- buf->b_p_fenc = vim_strsave(p_fenc);
+ buf->b_p_fenc = xstrdup(p_fenc);
switch (*p_ffs) {
case 'm':
- buf->b_p_ff = vim_strsave((char_u *)FF_MAC);
+ buf->b_p_ff = xstrdup(FF_MAC);
break;
case 'd':
- buf->b_p_ff = vim_strsave((char_u *)FF_DOS);
+ buf->b_p_ff = xstrdup(FF_DOS);
break;
case 'u':
- buf->b_p_ff = vim_strsave((char_u *)FF_UNIX);
+ buf->b_p_ff = xstrdup(FF_UNIX);
break;
default:
- buf->b_p_ff = vim_strsave(p_ff);
+ buf->b_p_ff = xstrdup(p_ff);
break;
}
buf->b_p_bh = empty_option;
@@ -6587,44 +4500,42 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_swf = p_swf;
COPY_OPT_SCTX(buf, BV_SWF);
}
- buf->b_p_cpt = vim_strsave(p_cpt);
+ buf->b_p_cpt = xstrdup(p_cpt);
COPY_OPT_SCTX(buf, BV_CPT);
#ifdef BACKSLASH_IN_FILENAME
buf->b_p_csl = vim_strsave(p_csl);
COPY_OPT_SCTX(buf, BV_CSL);
#endif
- buf->b_p_cfu = vim_strsave(p_cfu);
+ buf->b_p_cfu = xstrdup(p_cfu);
COPY_OPT_SCTX(buf, BV_CFU);
- buf->b_p_ofu = vim_strsave(p_ofu);
+ buf->b_p_ofu = xstrdup(p_ofu);
COPY_OPT_SCTX(buf, BV_OFU);
- buf->b_p_tfu = vim_strsave(p_tfu);
+ buf->b_p_tfu = xstrdup(p_tfu);
COPY_OPT_SCTX(buf, BV_TFU);
- buf->b_p_umf = vim_strsave(p_umf);
+ buf->b_p_umf = xstrdup(p_umf);
COPY_OPT_SCTX(buf, BV_UMF);
buf->b_p_sts = p_sts;
COPY_OPT_SCTX(buf, BV_STS);
buf->b_p_sts_nopaste = p_sts_nopaste;
- buf->b_p_vsts = vim_strsave(p_vsts);
+ buf->b_p_vsts = xstrdup(p_vsts);
COPY_OPT_SCTX(buf, BV_VSTS);
if (p_vsts && p_vsts != empty_option) {
(void)tabstop_set(p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = NULL;
}
- buf->b_p_vsts_nopaste = p_vsts_nopaste
- ? vim_strsave(p_vsts_nopaste)
- : NULL;
- buf->b_p_com = vim_strsave(p_com);
+ buf->b_p_vsts_nopaste = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : NULL;
+ buf->b_p_com = xstrdup(p_com);
COPY_OPT_SCTX(buf, BV_COM);
- buf->b_p_cms = vim_strsave(p_cms);
+ buf->b_p_cms = xstrdup(p_cms);
COPY_OPT_SCTX(buf, BV_CMS);
- buf->b_p_fo = vim_strsave(p_fo);
+ buf->b_p_fo = xstrdup(p_fo);
COPY_OPT_SCTX(buf, BV_FO);
- buf->b_p_flp = vim_strsave(p_flp);
+ buf->b_p_flp = xstrdup(p_flp);
COPY_OPT_SCTX(buf, BV_FLP);
- buf->b_p_nf = vim_strsave(p_nf);
+ buf->b_p_nf = xstrdup(p_nf);
COPY_OPT_SCTX(buf, BV_NF);
- buf->b_p_mps = vim_strsave(p_mps);
+ buf->b_p_mps = xstrdup(p_mps);
COPY_OPT_SCTX(buf, BV_MPS);
buf->b_p_si = p_si;
COPY_OPT_SCTX(buf, BV_SI);
@@ -6634,18 +4545,18 @@ void buf_copy_options(buf_T *buf, int flags)
COPY_OPT_SCTX(buf, BV_CI);
buf->b_p_cin = p_cin;
COPY_OPT_SCTX(buf, BV_CIN);
- buf->b_p_cink = vim_strsave(p_cink);
+ buf->b_p_cink = xstrdup(p_cink);
COPY_OPT_SCTX(buf, BV_CINK);
- buf->b_p_cino = vim_strsave(p_cino);
+ buf->b_p_cino = xstrdup(p_cino);
COPY_OPT_SCTX(buf, BV_CINO);
- buf->b_p_cinsd = vim_strsave(p_cinsd);
+ buf->b_p_cinsd = xstrdup(p_cinsd);
COPY_OPT_SCTX(buf, BV_CINSD);
// Don't copy 'filetype', it must be detected
buf->b_p_ft = empty_option;
buf->b_p_pi = p_pi;
COPY_OPT_SCTX(buf, BV_PI);
- buf->b_p_cinw = vim_strsave(p_cinw);
+ buf->b_p_cinw = xstrdup(p_cinw);
COPY_OPT_SCTX(buf, BV_CINW);
buf->b_p_lisp = p_lisp;
COPY_OPT_SCTX(buf, BV_LISP);
@@ -6654,25 +4565,25 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_smc = p_smc;
COPY_OPT_SCTX(buf, BV_SMC);
buf->b_s.b_syn_isk = empty_option;
- buf->b_s.b_p_spc = vim_strsave(p_spc);
+ buf->b_s.b_p_spc = xstrdup(p_spc);
COPY_OPT_SCTX(buf, BV_SPC);
(void)compile_cap_prog(&buf->b_s);
- buf->b_s.b_p_spf = vim_strsave(p_spf);
+ buf->b_s.b_p_spf = xstrdup(p_spf);
COPY_OPT_SCTX(buf, BV_SPF);
- buf->b_s.b_p_spl = vim_strsave(p_spl);
+ buf->b_s.b_p_spl = xstrdup(p_spl);
COPY_OPT_SCTX(buf, BV_SPL);
- buf->b_s.b_p_spo = vim_strsave(p_spo);
+ buf->b_s.b_p_spo = xstrdup(p_spo);
COPY_OPT_SCTX(buf, BV_SPO);
- buf->b_p_inde = vim_strsave(p_inde);
+ buf->b_p_inde = xstrdup(p_inde);
COPY_OPT_SCTX(buf, BV_INDE);
- buf->b_p_indk = vim_strsave(p_indk);
+ buf->b_p_indk = xstrdup(p_indk);
COPY_OPT_SCTX(buf, BV_INDK);
buf->b_p_fp = empty_option;
- buf->b_p_fex = vim_strsave(p_fex);
+ buf->b_p_fex = xstrdup(p_fex);
COPY_OPT_SCTX(buf, BV_FEX);
- buf->b_p_sua = vim_strsave(p_sua);
+ buf->b_p_sua = xstrdup(p_sua);
COPY_OPT_SCTX(buf, BV_SUA);
- buf->b_p_keymap = vim_strsave(p_keymap);
+ buf->b_p_keymap = xstrdup(p_keymap);
COPY_OPT_SCTX(buf, BV_KMAP);
buf->b_kmap_state |= KEYMAP_INIT;
// This isn't really an option, but copying the langmap and IME
@@ -6699,12 +4610,12 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_tc_flags = 0;
buf->b_p_def = empty_option;
buf->b_p_inc = empty_option;
- buf->b_p_inex = vim_strsave(p_inex);
+ buf->b_p_inex = xstrdup(p_inex);
COPY_OPT_SCTX(buf, BV_INEX);
buf->b_p_dict = empty_option;
buf->b_p_tsr = empty_option;
buf->b_p_tsrfu = empty_option;
- buf->b_p_qe = vim_strsave(p_qe);
+ buf->b_p_qe = xstrdup(p_qe);
COPY_OPT_SCTX(buf, BV_QE);
buf->b_p_udf = p_udf;
COPY_OPT_SCTX(buf, BV_UDF);
@@ -6718,19 +4629,19 @@ void buf_copy_options(buf_T *buf, int flags)
* or to a help buffer.
*/
if (dont_do_help) {
- buf->b_p_isk = save_p_isk;
+ buf->b_p_isk = (char *)save_p_isk;
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
(void)tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
} else {
- buf->b_p_isk = vim_strsave(p_isk);
+ buf->b_p_isk = xstrdup(p_isk);
COPY_OPT_SCTX(buf, BV_ISK);
did_isk = true;
buf->b_p_ts = p_ts;
COPY_OPT_SCTX(buf, BV_TS);
- buf->b_p_vts = vim_strsave(p_vts);
+ buf->b_p_vts = xstrdup(p_vts);
COPY_OPT_SCTX(buf, BV_VTS);
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
(void)tabstop_set(p_vts, &buf->b_p_vts_array);
@@ -7051,7 +4962,7 @@ void ExpandOldSetting(int *num_file, char ***file)
if (expand_option_idx >= 0) {
// Put string of option value in NameBuff.
option_value2string(&options[expand_option_idx], expand_option_flags);
- var = NameBuff;
+ var = (char_u *)NameBuff;
} else {
var = (char_u *)"";
}
@@ -7084,9 +4995,7 @@ void ExpandOldSetting(int *num_file, char ***file)
/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
static void option_value2string(vimoption_T *opp, int opt_flags)
{
- char_u *varp;
-
- varp = get_varp_scope(opp, opt_flags);
+ char_u *varp = (char_u *)get_varp_scope(opp, opt_flags);
if (opp->flags & P_NUM) {
long wc = 0;
@@ -7108,7 +5017,7 @@ static void option_value2string(vimoption_T *opp, int opt_flags)
} else if (opp->flags & P_EXPAND) {
home_replace(NULL, (char *)varp, (char *)NameBuff, MAXPATHL, false);
// Translate 'pastetoggle' into special key names.
- } else if ((char_u **)opp->var == &p_pt) {
+ } else if ((char **)opp->var == &p_pt) {
str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL);
} else {
STRLCPY(NameBuff, varp, MAXPATHL);
@@ -7130,25 +5039,14 @@ static int wc_use_keyname(char_u *varp, long *wcp)
return false;
}
-/// Return true if format option 'x' is in effect.
-/// Take care of no formatting when 'paste' is set.
-bool has_format_option(int x)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- if (p_paste) {
- return false;
- }
- return vim_strchr((char *)curbuf->b_p_fo, x) != NULL;
-}
-
/// @returns true if "x" is present in 'shortmess' option, or
/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS.
bool shortmess(int x)
{
return (p_shm != NULL
- && (vim_strchr((char *)p_shm, x) != NULL
- || (vim_strchr((char *)p_shm, 'a') != NULL
- && vim_strchr((char *)SHM_ALL_ABBREVIATIONS, x) != NULL)));
+ && (vim_strchr(p_shm, x) != NULL
+ || (vim_strchr(p_shm, 'a') != NULL
+ && vim_strchr(SHM_ALL_ABBREVIATIONS, x) != NULL)));
}
/// paste_option_changed() - Called after p_paste was set or reset.
@@ -7178,7 +5076,7 @@ static void paste_option_changed(void)
xfree(buf->b_p_vsts_nopaste);
}
buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
- ? vim_strsave(buf->b_p_vsts)
+ ? xstrdup(buf->b_p_vsts)
: NULL;
}
@@ -7197,9 +5095,7 @@ static void paste_option_changed(void)
if (p_vsts_nopaste) {
xfree(p_vsts_nopaste);
}
- p_vsts_nopaste = p_vsts && p_vsts != empty_option
- ? vim_strsave(p_vsts)
- : NULL;
+ p_vsts_nopaste = p_vsts && p_vsts != empty_option ? xstrdup(p_vsts) : NULL;
}
// Always set the option values, also when 'paste' is set when it is
@@ -7249,9 +5145,7 @@ static void paste_option_changed(void)
if (buf->b_p_vsts) {
free_string_option(buf->b_p_vsts);
}
- buf->b_p_vsts = buf->b_p_vsts_nopaste
- ? vim_strsave(buf->b_p_vsts_nopaste)
- : empty_option;
+ buf->b_p_vsts = buf->b_p_vsts_nopaste ? xstrdup(buf->b_p_vsts_nopaste) : empty_option;
xfree(buf->b_p_vsts_array);
if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
(void)tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
@@ -7278,7 +5172,7 @@ static void paste_option_changed(void)
if (p_vsts) {
free_string_option(p_vsts);
}
- p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
+ p_vsts = p_vsts_nopaste ? xstrdup(p_vsts_nopaste) : empty_option;
}
old_p_paste = p_paste;
@@ -7336,7 +5230,7 @@ void reset_option_was_set(const char *name)
}
/// fill_breakat_flags() -- called when 'breakat' changes value.
-static void fill_breakat_flags(void)
+void fill_breakat_flags(void)
{
char_u *p;
int i;
@@ -7346,16 +5240,16 @@ static void fill_breakat_flags(void)
}
if (p_breakat != NULL) {
- for (p = p_breakat; *p; p++) {
+ for (p = (char_u *)p_breakat; *p; p++) {
breakat_flags[*p] = true;
}
}
}
/// fill_culopt_flags() -- called when 'culopt' changes value
-static int fill_culopt_flags(char_u *val, win_T *wp)
+int fill_culopt_flags(char *val, win_T *wp)
{
- char_u *p;
+ char *p;
char_u culopt_flags_new = 0;
if (val == NULL) {
@@ -7395,101 +5289,41 @@ static int fill_culopt_flags(char_u *val, win_T *wp)
return OK;
}
-/// Check an option that can be a range of string values.
-///
-/// @param list when true: accept a list of values
-///
-/// @return OK for correct value, FAIL otherwise. Empty is always OK.
-static int check_opt_strings(char_u *val, char **values, int list)
-{
- return opt_strings_flags(val, values, NULL, list);
-}
-
-/// Handle an option that can be a range of string values.
-/// Set a flag in "*flagp" for each string present.
-///
-/// @param val new value
-/// @param values array of valid string values
-/// @param list when true: accept a list of values
-///
-/// @return OK for correct value, FAIL otherwise. Empty is always OK.
-static int opt_strings_flags(char_u *val, char **values, unsigned *flagp, bool list)
-{
- unsigned int new_flags = 0;
-
- while (*val) {
- for (unsigned int i = 0;; i++) {
- if (values[i] == NULL) { // val not found in values[]
- return FAIL;
- }
-
- size_t len = STRLEN(values[i]);
- if (STRNCMP(values[i], val, len) == 0
- && ((list && val[len] == ',') || val[len] == NUL)) {
- val += len + (val[len] == ',');
- assert(i < sizeof(1U) * 8);
- new_flags |= (1U << i);
- break; // check next item in val list
- }
- }
- }
- if (flagp != NULL) {
- *flagp = new_flags;
- }
-
- return OK;
-}
-
-/// Read the 'wildmode' option, fill wim_flags[].
-static int check_opt_wim(void)
+/// Set the callback function value for an option that accepts a function name,
+/// lambda, et al. (e.g. 'operatorfunc', 'tagfunc', etc.)
+/// @return OK if the option is successfully set to a function, otherwise FAIL
+int option_set_callback_func(char_u *optval, Callback *optcb)
{
- char_u new_wim_flags[4];
- char_u *p;
- int i;
- int idx = 0;
-
- for (i = 0; i < 4; i++) {
- new_wim_flags[i] = 0;
+ if (optval == NULL || *optval == NUL) {
+ callback_free(optcb);
+ return OK;
}
- for (p = p_wim; *p; p++) {
- for (i = 0; ASCII_ISALPHA(p[i]); i++) {}
- if (p[i] != NUL && p[i] != ',' && p[i] != ':') {
- return FAIL;
- }
- if (i == 7 && STRNCMP(p, "longest", 7) == 0) {
- new_wim_flags[idx] |= WIM_LONGEST;
- } else if (i == 4 && STRNCMP(p, "full", 4) == 0) {
- new_wim_flags[idx] |= WIM_FULL;
- } else if (i == 4 && STRNCMP(p, "list", 4) == 0) {
- new_wim_flags[idx] |= WIM_LIST;
- } else if (i == 8 && STRNCMP(p, "lastused", 8) == 0) {
- new_wim_flags[idx] |= WIM_BUFLASTUSED;
- } else {
+ typval_T *tv;
+ if (*optval == '{'
+ || (STRNCMP(optval, "function(", 9) == 0)
+ || (STRNCMP(optval, "funcref(", 8) == 0)) {
+ // Lambda expression or a funcref
+ tv = eval_expr((char *)optval);
+ if (tv == NULL) {
return FAIL;
}
- p += i;
- if (*p == NUL) {
- break;
- }
- if (*p == ',') {
- if (idx == 3) {
- return FAIL;
- }
- idx++;
- }
+ } else {
+ // treat everything else as a function name string
+ tv = xcalloc(1, sizeof(*tv));
+ tv->v_type = VAR_STRING;
+ tv->vval.v_string = (char *)vim_strsave(optval);
}
- // fill remaining entries with last flag
- while (idx < 3) {
- new_wim_flags[idx + 1] = new_wim_flags[idx];
- idx++;
+ Callback cb;
+ if (!callback_from_typval(&cb, tv)) {
+ tv_free(tv);
+ return FAIL;
}
- // only when there are no errors, wim_flags[] is changed
- for (i = 0; i < 4; i++) {
- wim_flags[i] = new_wim_flags[i];
- }
+ callback_free(optcb);
+ *optcb = cb;
+ tv_free(tv);
return OK;
}
@@ -7510,412 +5344,7 @@ bool can_bs(int what)
case '0':
return false;
}
- return vim_strchr((char *)p_bs, what) != NULL;
-}
-
-/// Save the current values of 'fileformat' and 'fileencoding', so that we know
-/// the file must be considered changed when the value is different.
-void save_file_ff(buf_T *buf)
-{
- buf->b_start_ffc = *buf->b_p_ff;
- buf->b_start_eol = buf->b_p_eol;
- buf->b_start_bomb = buf->b_p_bomb;
-
- // Only use free/alloc when necessary, they take time.
- if (buf->b_start_fenc == NULL
- || STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0) {
- xfree(buf->b_start_fenc);
- buf->b_start_fenc = (char *)vim_strsave(buf->b_p_fenc);
- }
-}
-
-/// Return true if 'fileformat' and/or 'fileencoding' has a different value
-/// from when editing started (save_file_ff() called).
-/// Also when 'endofline' was changed and 'binary' is set, or when 'bomb' was
-/// changed and 'binary' is not set.
-/// Also when 'endofline' was changed and 'fixeol' is not set.
-/// When "ignore_empty" is true don't consider a new, empty buffer to be
-/// changed.
-bool file_ff_differs(buf_T *buf, bool ignore_empty)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- // In a buffer that was never loaded the options are not valid.
- if (buf->b_flags & BF_NEVERLOADED) {
- return false;
- }
- if (ignore_empty
- && (buf->b_flags & BF_NEW)
- && buf->b_ml.ml_line_count == 1
- && *ml_get_buf(buf, (linenr_T)1, false) == NUL) {
- return false;
- }
- if (buf->b_start_ffc != *buf->b_p_ff) {
- return true;
- }
- if ((buf->b_p_bin || !buf->b_p_fixeol) && buf->b_start_eol != buf->b_p_eol) {
- return true;
- }
- if (!buf->b_p_bin && buf->b_start_bomb != buf->b_p_bomb) {
- return true;
- }
- if (buf->b_start_fenc == NULL) {
- return *buf->b_p_fenc != NUL;
- }
- return STRCMP(buf->b_start_fenc, buf->b_p_fenc) != 0;
-}
-
-/// return OK if "p" is a valid fileformat name, FAIL otherwise.
-int check_ff_value(char_u *p)
-{
- return check_opt_strings(p, p_ff_values, false);
-}
-
-// Set the integer values corresponding to the string setting of 'vartabstop'.
-// "array" will be set, caller must free it if needed.
-// Return false for an error.
-bool tabstop_set(char_u *var, long **array)
-{
- long valcount = 1;
- int t;
- char_u *cp;
-
- if (var[0] == NUL || (var[0] == '0' && var[1] == NUL)) {
- *array = NULL;
- return true;
- }
-
- for (cp = var; *cp != NUL; cp++) {
- if (cp == var || cp[-1] == ',') {
- char_u *end;
-
- if (strtol((char *)cp, (char **)&end, 10) <= 0) {
- if (cp != end) {
- emsg(_(e_positive));
- } else {
- semsg(_(e_invarg2), cp);
- }
- return false;
- }
- }
-
- if (ascii_isdigit(*cp)) {
- continue;
- }
- if (cp[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
- valcount++;
- continue;
- }
- semsg(_(e_invarg2), var);
- return false;
- }
-
- *array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
- (*array)[0] = valcount;
-
- t = 1;
- for (cp = var; *cp != NUL;) {
- int n = atoi((char *)cp);
-
- // Catch negative values, overflow and ridiculous big values.
- if (n <= 0 || n > TABSTOP_MAX) {
- semsg(_(e_invarg2), cp);
- XFREE_CLEAR(*array);
- return false;
- }
- (*array)[t++] = n;
- while (*cp != NUL && *cp != ',') {
- cp++;
- }
- if (*cp != NUL) {
- cp++;
- }
- }
-
- return true;
-}
-
-// Calculate the number of screen spaces a tab will occupy.
-// If "vts" is set then the tab widths are taken from that array,
-// otherwise the value of ts is used.
-int tabstop_padding(colnr_T col, long ts_arg, long *vts)
-{
- long ts = ts_arg == 0 ? 8 : ts_arg;
- colnr_T tabcol = 0;
- int t;
- long padding = 0;
-
- if (vts == NULL || vts[0] == 0) {
- return (int)(ts - (col % ts));
- }
-
- const long tabcount = vts[0];
-
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > col) {
- padding = tabcol - col;
- break;
- }
- }
- if (t > tabcount) {
- padding = vts[tabcount] - ((col - tabcol) % vts[tabcount]);
- }
-
- return (int)padding;
-}
-
-// Find the size of the tab that covers a particular column.
-int tabstop_at(colnr_T col, long ts, long *vts)
-{
- colnr_T tabcol = 0;
- int t;
- long tab_size = 0;
-
- if (vts == NULL || vts[0] == 0) {
- return (int)ts;
- }
-
- const long tabcount = vts[0];
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > col) {
- tab_size = vts[t];
- break;
- }
- }
- if (t > tabcount) {
- tab_size = vts[tabcount];
- }
-
- return (int)tab_size;
-}
-
-// Find the column on which a tab starts.
-colnr_T tabstop_start(colnr_T col, long ts, long *vts)
-{
- colnr_T tabcol = 0;
- int t;
-
- if (vts == NULL || vts[0] == 0) {
- return (int)((col / ts) * ts);
- }
-
- const long tabcount = vts[0];
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > col) {
- return (int)(tabcol - vts[t]);
- }
- }
-
- const int excess = (int)(tabcol % vts[tabcount]);
- return (int)(excess + ((col - excess) / vts[tabcount]) * vts[tabcount]);
-}
-
-// Find the number of tabs and spaces necessary to get from one column
-// to another.
-void tabstop_fromto(colnr_T start_col, colnr_T end_col, long ts_arg, long *vts, int *ntabs,
- int *nspcs)
-{
- int spaces = end_col - start_col;
- colnr_T tabcol = 0;
- long padding = 0;
- int t;
- long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
-
- if (vts == NULL || vts[0] == 0) {
- int tabs = 0;
-
- const int initspc = (int)(ts - (start_col % ts));
- if (spaces >= initspc) {
- spaces -= initspc;
- tabs++;
- }
- tabs += (int)(spaces / ts);
- spaces -= (int)((spaces / ts) * ts);
-
- *ntabs = tabs;
- *nspcs = spaces;
- return;
- }
-
- // Find the padding needed to reach the next tabstop.
- const long tabcount = vts[0];
- for (t = 1; t <= tabcount; t++) {
- tabcol += (colnr_T)vts[t];
- if (tabcol > start_col) {
- padding = tabcol - start_col;
- break;
- }
- }
- if (t > tabcount) {
- padding = vts[tabcount] - ((start_col - tabcol) % vts[tabcount]);
- }
-
- // If the space needed is less than the padding no tabs can be used.
- if (spaces < padding) {
- *ntabs = 0;
- *nspcs = spaces;
- return;
- }
-
- *ntabs = 1;
- spaces -= (int)padding;
-
- // At least one tab has been used. See if any more will fit.
- while (spaces != 0 && ++t <= tabcount) {
- padding = vts[t];
- if (spaces < padding) {
- *nspcs = spaces;
- return;
- }
- *ntabs += 1;
- spaces -= (int)padding;
- }
-
- *ntabs += spaces / (int)vts[tabcount];
- *nspcs = spaces % (int)vts[tabcount];
-}
-
-// See if two tabstop arrays contain the same values.
-bool tabstop_eq(long *ts1, long *ts2)
-{
- int t;
-
- if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
- return false;
- }
- if (ts1 == ts2) {
- return true;
- }
- if (ts1[0] != ts2[0]) {
- return false;
- }
-
- for (t = 1; t <= ts1[0]; t++) {
- if (ts1[t] != ts2[t]) {
- return false;
- }
- }
-
- return true;
-}
-
-// Copy a tabstop array, allocating space for the new array.
-int *tabstop_copy(long *oldts)
-{
- long *newts;
- int t;
-
- if (oldts == 0) {
- return 0;
- }
-
- newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
- for (t = 0; t <= oldts[0]; t++) {
- newts[t] = oldts[t];
- }
-
- return (int *)newts;
-}
-
-// Return a count of the number of tabstops.
-int tabstop_count(long *ts)
-{
- return ts != NULL ? (int)ts[0] : 0;
-}
-
-// Return the first tabstop, or 8 if there are no tabstops defined.
-int tabstop_first(long *ts)
-{
- return ts != NULL ? (int)ts[1] : 8;
-}
-
-/// Return the effective shiftwidth value for current buffer, using the
-/// 'tabstop' value when 'shiftwidth' is zero.
-int get_sw_value(buf_T *buf)
-{
- long result = get_sw_value_col(buf, 0);
- assert(result >= 0 && result <= INT_MAX);
- return (int)result;
-}
-
-// Idem, using the first non-black in the current line.
-long get_sw_value_indent(buf_T *buf)
-{
- pos_T pos = curwin->w_cursor;
-
- pos.col = (colnr_T)getwhitecols_curline();
- return get_sw_value_pos(buf, &pos);
-}
-
-// Idem, using "pos".
-long get_sw_value_pos(buf_T *buf, pos_T *pos)
-{
- pos_T save_cursor = curwin->w_cursor;
- long sw_value;
-
- curwin->w_cursor = *pos;
- sw_value = get_sw_value_col(buf, get_nolist_virtcol());
- curwin->w_cursor = save_cursor;
- return sw_value;
-}
-
-// Idem, using virtual column "col".
-long get_sw_value_col(buf_T *buf, colnr_T col)
-{
- return buf->b_p_sw ? buf->b_p_sw
- : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
-}
-
-/// Return the effective softtabstop value for the current buffer,
-/// using the shiftwidth value when 'softtabstop' is negative.
-int get_sts_value(void)
-{
- long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;
- assert(result >= 0 && result <= INT_MAX);
- return (int)result;
-}
-
-/// This is called when 'breakindentopt' is changed and when a window is
-/// initialized
-static bool briopt_check(win_T *wp)
-{
- int bri_shift = 0;
- int bri_min = 20;
- bool bri_sbr = false;
- int bri_list = 0;
-
- char *p = (char *)wp->w_p_briopt;
- while (*p != NUL) {
- if (STRNCMP(p, "shift:", 6) == 0
- && ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
- p += 6;
- bri_shift = getdigits_int(&p, true, 0);
- } else if (STRNCMP(p, "min:", 4) == 0 && ascii_isdigit(p[4])) {
- p += 4;
- bri_min = getdigits_int(&p, true, 0);
- } else if (STRNCMP(p, "sbr", 3) == 0) {
- p += 3;
- bri_sbr = true;
- } else if (STRNCMP(p, "list:", 5) == 0) {
- p += 5;
- bri_list = (int)getdigits(&p, false, 0);
- }
- if (*p != ',' && *p != NUL) {
- return false;
- }
- if (*p == ',') {
- p++;
- }
- }
-
- wp->w_briopt_shift = bri_shift;
- wp->w_briopt_min = bri_min;
- wp->w_briopt_sbr = bri_sbr;
- wp->w_briopt_list = bri_list;
-
- return true;
+ return vim_strchr(p_bs, what) != NULL;
}
/// Get the local or global value of 'backupcopy'.
@@ -7940,19 +5369,19 @@ char_u *get_showbreak_value(win_T *const win)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (win->w_p_sbr == NULL || *win->w_p_sbr == NUL) {
- return p_sbr;
+ return (char_u *)p_sbr;
}
if (STRCMP(win->w_p_sbr, "NONE") == 0) {
- return empty_option;
+ return (char_u *)empty_option;
}
- return win->w_p_sbr;
+ return (char_u *)win->w_p_sbr;
}
/// Return the current end-of-line type: EOL_DOS, EOL_UNIX or EOL_MAC.
int get_fileformat(const buf_T *buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
- int c = *buf->b_p_ff;
+ int c = (unsigned char)(*buf->b_p_ff);
if (buf->b_p_bin || c == 'u') {
return EOL_UNIX;
@@ -7979,7 +5408,7 @@ int get_fileformat_force(const buf_T *buf, const exarg_T *eap)
? (eap->force_bin == FORCE_BIN) : buf->b_p_bin) {
return EOL_UNIX;
}
- c = *buf->b_p_ff;
+ c = (unsigned char)(*buf->b_p_ff);
}
if (c == 'u') {
return EOL_UNIX;
@@ -8036,7 +5465,7 @@ void set_fileformat(int eol_style, int opt_flags)
}
/// Skip to next part of an option argument: skip space and comma
-char_u *skip_to_option_part(const char_u *p)
+char *skip_to_option_part(const char *p)
{
if (*p == ',') {
p++;
@@ -8044,7 +5473,7 @@ char_u *skip_to_option_part(const char_u *p)
while (*p == ' ') {
p++;
}
- return (char_u *)p;
+ return (char *)p;
}
/// Isolate one part of a string option separated by `sep_chars`.
@@ -8079,7 +5508,7 @@ size_t copy_option_part(char **option, char *buf, size_t maxlen, char *sep_chars
if (*p != NUL && *p != ',') { // skip non-standard separator
p++;
}
- p = (char *)skip_to_option_part((char_u *)p); // p points to next file name
+ p = skip_to_option_part(p); // p points to next file name
*option = p;
return len;
diff --git a/src/nvim/option.h b/src/nvim/option.h
index a5a57cc66d..c65d2ee182 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -19,23 +19,6 @@ typedef enum {
#define BCO_ALWAYS 2 // always copy the options
#define BCO_NOHELP 4 // don't touch the help related options
-/// Flags for option-setting functions
-///
-/// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global
-/// values, get local value.
-typedef enum {
- OPT_FREE = 0x01, ///< Free old value if it was allocated.
- OPT_GLOBAL = 0x02, ///< Use global value.
- OPT_LOCAL = 0x04, ///< Use local value.
- OPT_MODELINE = 0x08, ///< Option in modeline.
- OPT_WINONLY = 0x10, ///< Only set window-local options.
- OPT_NOWIN = 0x20, ///< Don’t set window-local options.
- OPT_ONECOLUMN = 0x40, ///< list options one per line
- OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option
- OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions'
- OPT_CLEAR = 0x200, ///< Clear local value of an option.
-} OptionFlags;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "option.h.generated.h"
#endif
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 92ab7489bb..78cb324fe4 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -7,6 +7,69 @@
// option_defs.h: definition of global variables for settable options
+// Flags
+#define P_BOOL 0x01U ///< the option is boolean
+#define P_NUM 0x02U ///< the option is numeric
+#define P_STRING 0x04U ///< the option is a string
+#define P_ALLOCED 0x08U ///< the string option is in allocated memory,
+ ///< must use free_string_option() when
+ ///< assigning new value. Not set if default is
+ ///< the same.
+#define P_EXPAND 0x10U ///< environment expansion. NOTE: P_EXPAND can
+ ///< never be used for local or hidden options
+#define P_NODEFAULT 0x40U ///< don't set to default value
+#define P_DEF_ALLOCED 0x80U ///< default value is in allocated memory, must
+ ///< use free() when assigning new value
+#define P_WAS_SET 0x100U ///< option has been set/reset
+#define P_NO_MKRC 0x200U ///< don't include in :mkvimrc output
+
+// when option changed, what to display:
+#define P_RSTAT 0x1000U ///< redraw status lines
+#define P_RWIN 0x2000U ///< redraw current window and recompute text
+#define P_RBUF 0x4000U ///< redraw current buffer and recompute text
+#define P_RALL 0x6000U ///< redraw all windows
+#define P_RCLR 0x7000U ///< clear and redraw all
+
+#define P_COMMA 0x8000U ///< comma separated list
+#define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive
+ ///< commas
+#define P_NODUP 0x20000U ///< don't allow duplicate strings
+#define P_FLAGLIST 0x40000U ///< list of single-char flags
+
+#define P_SECURE 0x80000U ///< cannot change in modeline or secure mode
+#define P_GETTEXT 0x100000U ///< expand default value with _()
+#define P_NOGLOB 0x200000U ///< do not use local value for global vimrc
+#define P_NFNAME 0x400000U ///< only normal file name chars allowed
+#define P_INSECURE 0x800000U ///< option was set from a modeline
+#define P_PRI_MKRC 0x1000000U ///< priority for :mkvimrc (setting option
+ ///< has side effects)
+#define P_NO_ML 0x2000000U ///< not allowed in modeline
+#define P_CURSWANT 0x4000000U ///< update curswant required; not needed
+ ///< when there is a redraw flag
+#define P_NDNAME 0x8000000U ///< only normal dir name chars allowed
+#define P_RWINONLY 0x10000000U ///< only redraw current window
+#define P_MLE 0x20000000U ///< under control of 'modelineexpr'
+
+#define P_NO_DEF_EXP 0x40000000U ///< Do not expand default value.
+#define P_UI_OPTION 0x80000000U ///< send option to remote ui
+
+/// Flags for option-setting functions
+///
+/// When OPT_GLOBAL and OPT_LOCAL are both missing, set both local and global
+/// values, get local value.
+typedef enum {
+ OPT_FREE = 0x01, ///< Free old value if it was allocated.
+ OPT_GLOBAL = 0x02, ///< Use global value.
+ OPT_LOCAL = 0x04, ///< Use local value.
+ OPT_MODELINE = 0x08, ///< Option in modeline.
+ OPT_WINONLY = 0x10, ///< Only set window-local options.
+ OPT_NOWIN = 0x20, ///< Don’t set window-local options.
+ OPT_ONECOLUMN = 0x40, ///< list options one per line
+ OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option
+ OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions'
+ OPT_CLEAR = 0x200, ///< Clear local value of an option.
+} OptionFlags;
+
// Return value from get_option_value_strict
#define SOPT_BOOL 0x01 // Boolean option
#define SOPT_NUM 0x02 // Number option
@@ -21,6 +84,16 @@
#define SREQ_WIN 1 // Request window-local option value
#define SREQ_BUF 2 // Request buffer-local option value
+#define HIGHLIGHT_INIT \
+ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
+ "i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow," \
+ "N:CursorLineNr,G:CursorLineSign,O:CursorLineFold" \
+ "r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg," \
+ "W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn," \
+ "-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,x:PmenuSbar," \
+ "X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn," \
+ "q:QuickFixLine,0:Whitespace,I:NormalNC"
+
// Default values for 'errorformat'.
// The "%f|%l| %m" one is used for when the contents of the quickfix window is
// written to a file.
@@ -185,7 +258,7 @@ enum {
SHM_SEARCHCOUNT = 'S', ///< Search sats: '[1/10]'
};
/// Represented by 'a' flag.
-#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \
+#define SHM_ALL_ABBREVIATIONS ((char[]) { \
SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
0, \
})
@@ -311,37 +384,39 @@ enum {
*/
EXTERN long p_aleph; // 'aleph'
-EXTERN int p_acd; // 'autochdir'
-EXTERN char_u *p_ambw; // 'ambiwidth'
+EXTERN char *p_ambw; ///< 'ambiwidth'
+EXTERN int p_acd; ///< 'autochdir'
+EXTERN int p_ai; ///< 'autoindent'
+EXTERN int p_bin; ///< 'binary'
+EXTERN int p_bomb; ///< 'bomb'
+EXTERN int p_bl; ///< 'buflisted'
+EXTERN int p_cin; ///< 'cindent'
+EXTERN long p_channel; ///< 'channel'
+EXTERN char *p_cink; ///< 'cinkeys'
+EXTERN char *p_cinsd; ///< 'cinscopedecls'
+EXTERN char *p_cinw; ///< 'cinwords'
+EXTERN char *p_cfu; ///< 'completefunc'
+EXTERN char *p_ofu; ///< 'omnifunc'
+EXTERN char *p_tsrfu; ///< 'thesaurusfunc'
+EXTERN int p_ci; ///< 'copyindent'
EXTERN int p_ar; // 'autoread'
EXTERN int p_aw; // 'autowrite'
EXTERN int p_awa; // 'autowriteall'
-EXTERN char_u *p_bs; // 'backspace'
-EXTERN char_u *p_bg; // 'background'
+EXTERN char *p_bs; // 'backspace'
+EXTERN char *p_bg; // 'background'
EXTERN int p_bk; // 'backup'
-EXTERN char_u *p_bkc; // 'backupcopy'
+EXTERN char *p_bkc; // 'backupcopy'
EXTERN unsigned int bkc_flags; ///< flags from 'backupcopy'
-#ifdef IN_OPTION_C
-static char *(p_bkc_values[]) =
-{ "yes", "auto", "no", "breaksymlink", "breakhardlink", NULL };
-#endif
#define BKC_YES 0x001
#define BKC_AUTO 0x002
#define BKC_NO 0x004
#define BKC_BREAKSYMLINK 0x008
#define BKC_BREAKHARDLINK 0x010
-EXTERN char_u *p_bdir; // 'backupdir'
-EXTERN char_u *p_bex; // 'backupext'
-EXTERN char_u *p_bo; // 'belloff'
+EXTERN char *p_bdir; // 'backupdir'
+EXTERN char *p_bex; // 'backupext'
+EXTERN char *p_bo; // 'belloff'
EXTERN char breakat_flags[256]; // which characters are in 'breakat'
EXTERN unsigned bo_flags;
-#ifdef IN_OPTION_C
-static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete",
- "copy", "ctrlg", "error", "esc", "ex",
- "hangul", "lang", "mess", "showmatch",
- "operator", "register", "shell", "spell",
- "wildmode", NULL };
-#endif
// values for the 'belloff' option
#define BO_ALL 0x0001
@@ -365,90 +440,88 @@ static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete",
#define BO_WILD 0x40000
EXTERN char_u *p_bsk; // 'backupskip'
-EXTERN char_u *p_breakat; // 'breakat'
-EXTERN char_u *p_cmp; // 'casemap'
+EXTERN char *p_breakat; // 'breakat'
+EXTERN char *p_bh; ///< 'bufhidden'
+EXTERN char *p_bt; ///< 'buftype'
+EXTERN char *p_cmp; // 'casemap'
EXTERN unsigned cmp_flags;
-#ifdef IN_OPTION_C
-static char *(p_cmp_values[]) = { "internal", "keepascii", NULL };
-#endif
#define CMP_INTERNAL 0x001
#define CMP_KEEPASCII 0x002
-EXTERN char_u *p_enc; // 'encoding'
-EXTERN int p_deco; // 'delcombine'
-EXTERN char_u *p_ccv; // 'charconvert'
-EXTERN char_u *p_cedit; // 'cedit'
-EXTERN char_u *p_cb; // 'clipboard'
+EXTERN char *p_enc; // 'encoding'
+EXTERN int p_deco; // 'delcombine'
+EXTERN char *p_ccv; // 'charconvert'
+EXTERN char *p_cino; ///< 'cinoptions'
+EXTERN char *p_cedit; // 'cedit'
+EXTERN char *p_cb; // 'clipboard'
EXTERN unsigned cb_flags;
-#ifdef IN_OPTION_C
-static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
-#endif
#define CB_UNNAMED 0x001
#define CB_UNNAMEDPLUS 0x002
#define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS)
EXTERN long p_cwh; // 'cmdwinheight'
EXTERN long p_ch; // 'cmdheight'
+EXTERN char *p_cms; ///< 'commentstring'
+EXTERN char *p_cpt; ///< 'complete'
EXTERN long p_columns; // 'columns'
EXTERN int p_confirm; // 'confirm'
-EXTERN char_u *p_cot; // 'completeopt'
+EXTERN char *p_cot; // 'completeopt'
#ifdef BACKSLASH_IN_FILENAME
EXTERN char_u *p_csl; // 'completeslash'
#endif
EXTERN long p_pb; // 'pumblend'
EXTERN long p_ph; // 'pumheight'
EXTERN long p_pw; // 'pumwidth'
+EXTERN char *p_com; ///< 'comments'
EXTERN char *p_cpo; // 'cpoptions'
-EXTERN char_u *p_csprg; // 'cscopeprg'
+EXTERN char *p_csprg; // 'cscopeprg'
EXTERN int p_csre; // 'cscoperelative'
-EXTERN char_u *p_csqf; // 'cscopequickfix'
+EXTERN char *p_csqf; // 'cscopequickfix'
#define CSQF_CMDS "sgdctefia"
#define CSQF_FLAGS "+-0"
EXTERN int p_cst; // 'cscopetag'
EXTERN long p_csto; // 'cscopetagorder'
EXTERN long p_cspc; // 'cscopepathcomp'
EXTERN int p_csverbose; // 'cscopeverbose'
-EXTERN char_u *p_debug; // 'debug'
-EXTERN char_u *p_def; // 'define'
-EXTERN char_u *p_inc;
-EXTERN char_u *p_dip; // 'diffopt'
-EXTERN char_u *p_dex; // 'diffexpr'
-EXTERN char_u *p_dict; // 'dictionary'
+EXTERN char *p_debug; // 'debug'
+EXTERN char *p_def; // 'define'
+EXTERN char *p_inc;
+EXTERN char *p_dip; // 'diffopt'
+EXTERN char *p_dex; // 'diffexpr'
+EXTERN char *p_dict; // 'dictionary'
EXTERN int p_dg; // 'digraph'
-EXTERN char_u *p_dir; // 'directory'
-EXTERN char_u *p_dy; // 'display'
+EXTERN char *p_dir; // 'directory'
+EXTERN char *p_dy; // 'display'
EXTERN unsigned dy_flags;
-#ifdef IN_OPTION_C
-static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep",
- NULL };
-#endif
#define DY_LASTLINE 0x001
#define DY_TRUNCATE 0x002
#define DY_UHEX 0x004
// code should use msg_use_msgsep() to check if msgsep is active
#define DY_MSGSEP 0x008
EXTERN int p_ed; // 'edcompatible'
+EXTERN char *p_ead; // 'eadirection'
EXTERN int p_emoji; // 'emoji'
-EXTERN char_u *p_ead; // 'eadirection'
EXTERN int p_ea; // 'equalalways'
-EXTERN char_u *p_ep; // 'equalprg'
+EXTERN char_u *p_ep; // 'equalprg'
EXTERN int p_eb; // 'errorbells'
-EXTERN char_u *p_ef; // 'errorfile'
-EXTERN char *p_efm; // 'errorformat'
-EXTERN char *p_gefm; // 'grepformat'
-EXTERN char_u *p_gp; // 'grepprg'
-EXTERN char_u *p_ei; // 'eventignore'
+EXTERN char_u *p_ef; // 'errorfile'
+EXTERN char *p_efm; // 'errorformat'
+EXTERN char *p_gefm; // 'grepformat'
+EXTERN char_u *p_gp; // 'grepprg'
+EXTERN int p_eol; ///< 'endofline'
+EXTERN char *p_ei; // 'eventignore'
+EXTERN int p_et; ///< 'expandtab'
EXTERN int p_exrc; // 'exrc'
-EXTERN char_u *p_fencs; // 'fileencodings'
-EXTERN char *p_ffs; // 'fileformats'
+EXTERN char *p_fenc; ///< 'fileencoding'
+EXTERN char *p_fencs; // 'fileencodings'
+EXTERN char *p_ff; ///< 'fileformat'
+EXTERN char *p_ffs; // 'fileformats'
EXTERN int p_fic; // 'fileignorecase'
-EXTERN char_u *p_fcl; // 'foldclose'
+EXTERN char *p_ft; ///< 'filetype'
+EXTERN char *p_fcs; ///< 'fillchar'
+EXTERN int p_fixeol; ///< 'fixendofline'
+EXTERN char *p_fcl; // 'foldclose'
EXTERN long p_fdls; // 'foldlevelstart'
-EXTERN char_u *p_fdo; // 'foldopen'
+EXTERN char *p_fdo; // 'foldopen'
EXTERN unsigned fdo_flags;
-#ifdef IN_OPTION_C
-static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent",
- "quickfix", "search", "tag", "insert",
- "undo", "jump", NULL };
-#endif
#define FDO_ALL 0x001
#define FDO_BLOCK 0x002
#define FDO_HOR 0x004
@@ -460,66 +533,76 @@ static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent",
#define FDO_INSERT 0x100
#define FDO_UNDO 0x200
#define FDO_JUMP 0x400
-EXTERN char_u *p_fp; // 'formatprg'
+EXTERN char *p_fex; ///< 'formatexpr'
+EXTERN char *p_flp; ///< 'formatlistpat'
+EXTERN char *p_fo; ///< 'formatoptions'
+EXTERN char_u *p_fp; // 'formatprg'
EXTERN int p_fs; // 'fsync'
EXTERN int p_gd; // 'gdefault'
-EXTERN char_u *p_pdev; // 'printdevice'
-EXTERN char_u *p_penc; // 'printencoding'
-EXTERN char_u *p_pexpr; // 'printexpr'
-EXTERN char_u *p_pmfn; // 'printmbfont'
-EXTERN char_u *p_pmcs; // 'printmbcharset'
-EXTERN char_u *p_pfn; // 'printfont'
-EXTERN char_u *p_popt; // 'printoptions'
-EXTERN char_u *p_header; // 'printheader'
-EXTERN char_u *p_guicursor; // 'guicursor'
-EXTERN char_u *p_guifont; // 'guifont'
-EXTERN char_u *p_guifontwide; // 'guifontwide'
-EXTERN char_u *p_hf; // 'helpfile'
+EXTERN char_u *p_pdev; // 'printdevice'
+EXTERN char *p_penc; // 'printencoding'
+EXTERN char *p_pexpr; // 'printexpr'
+EXTERN char *p_pmfn; // 'printmbfont'
+EXTERN char *p_pmcs; // 'printmbcharset'
+EXTERN char *p_pfn; // 'printfont'
+EXTERN char *p_popt; // 'printoptions'
+EXTERN char_u *p_header; // 'printheader'
+EXTERN char *p_guicursor; // 'guicursor'
+EXTERN char_u *p_guifont; // 'guifont'
+EXTERN char_u *p_guifontwide; // 'guifontwide'
+EXTERN char *p_hf; // 'helpfile'
EXTERN long p_hh; // 'helpheight'
-EXTERN char_u *p_hlg; // 'helplang'
+EXTERN char_u *p_hlg; // 'helplang'
EXTERN int p_hid; // 'hidden'
-EXTERN char_u *p_hl; // 'highlight'
+EXTERN char *p_hl; // 'highlight'
EXTERN int p_hls; // 'hlsearch'
EXTERN long p_hi; // 'history'
EXTERN int p_hkmap; // 'hkmap'
EXTERN int p_hkmapp; // 'hkmapp'
EXTERN int p_arshape; // 'arabicshape'
EXTERN int p_icon; // 'icon'
-EXTERN char_u *p_iconstring; // 'iconstring'
+EXTERN char *p_iconstring; // 'iconstring'
EXTERN int p_ic; // 'ignorecase'
+EXTERN long p_iminsert; ///< 'iminsert'
+EXTERN long p_imsearch; ///< 'imsearch'
+EXTERN int p_inf; ///< 'infercase'
+EXTERN char *p_inex; ///< 'includeexpr'
EXTERN int p_is; // 'incsearch'
-EXTERN char_u *p_icm; // 'inccommand'
-EXTERN char_u *p_isf; // 'isfname'
-EXTERN char_u *p_isi; // 'isident'
-EXTERN char_u *p_isp; // 'isprint'
+EXTERN char *p_inde; ///< 'indentexpr'
+EXTERN char *p_indk; ///< 'indentkeys'
+EXTERN char *p_icm; // 'inccommand'
+EXTERN char *p_isf; // 'isfname'
+EXTERN char *p_isi; // 'isident'
+EXTERN char *p_isk; ///< 'iskeyword'
+EXTERN char *p_isp; // 'isprint'
EXTERN int p_js; // 'joinspaces'
-EXTERN char_u *p_jop; // 'jumpooptions'
+EXTERN char *p_jop; // 'jumpooptions'
EXTERN unsigned jop_flags;
-#ifdef IN_OPTION_C
-static char *(p_jop_values[]) = { "stack", "view", NULL };
-#endif
#define JOP_STACK 0x01
#define JOP_VIEW 0x02
-EXTERN char_u *p_kp; // 'keywordprg'
-EXTERN char_u *p_km; // 'keymodel'
-EXTERN char_u *p_langmap; // 'langmap'
+EXTERN char *p_keymap; ///< 'keymap'
+EXTERN char_u *p_kp; // 'keywordprg'
+EXTERN char *p_km; // 'keymodel'
+EXTERN char *p_langmap; // 'langmap'
EXTERN int p_lnr; // 'langnoremap'
EXTERN int p_lrm; // 'langremap'
-EXTERN char_u *p_lm; // 'langmenu'
-EXTERN long p_lines; // 'lines'
-EXTERN long p_linespace; // 'linespace'
-EXTERN char_u *p_lispwords; // 'lispwords'
+EXTERN char_u *p_lm; // 'langmenu'
+EXTERN long p_lines; // 'lines'
+EXTERN long p_linespace; // 'linespace'
+EXTERN int p_lisp; ///< 'lisp'
+EXTERN char_u *p_lispwords; // 'lispwords'
EXTERN long p_ls; // 'laststatus'
EXTERN long p_stal; // 'showtabline'
-EXTERN char_u *p_lcs; // 'listchars'
+EXTERN char *p_lcs; // 'listchars'
EXTERN int p_lz; // 'lazyredraw'
EXTERN int p_lpl; // 'loadplugins'
EXTERN int p_magic; // 'magic'
-EXTERN char_u *p_menc; // 'makeencoding'
-EXTERN char *p_mef; // 'makeef'
-EXTERN char_u *p_mp; // 'makeprg'
-EXTERN char_u *p_cc; // 'colorcolumn'
+EXTERN char *p_menc; // 'makeencoding'
+EXTERN char *p_mef; // 'makeef'
+EXTERN char_u *p_mp; // 'makeprg'
+EXTERN char *p_mps; ///< 'matchpairs'
+EXTERN char_u *p_cc; // 'colorcolumn'
EXTERN int p_cc_cols[256]; // array for 'colorcolumn' columns
EXTERN long p_mat; // 'matchtime'
EXTERN long p_mco; // 'maxcombine'
@@ -527,72 +610,62 @@ EXTERN long p_mfd; // 'maxfuncdepth'
EXTERN long p_mmd; // 'maxmapdepth'
EXTERN long p_mmp; // 'maxmempattern'
EXTERN long p_mis; // 'menuitems'
-EXTERN char_u *p_msm; // 'mkspellmem'
-EXTERN int p_mle; // 'modelineexpr'
+EXTERN char *p_msm; // 'mkspellmem'
+EXTERN int p_ml; ///< 'modeline'
+EXTERN int p_mle; // 'modelineexpr'
EXTERN long p_mls; // 'modelines'
-EXTERN char_u *p_mouse; // 'mouse'
-EXTERN char_u *p_mousem; // 'mousemodel'
-EXTERN int p_mousef; // 'mousefocus'
-EXTERN char_u *p_mousescroll; // 'mousescroll'
+EXTERN int p_ma; ///< 'modifiable'
+EXTERN int p_mod; ///< 'modified'
+EXTERN char *p_mouse; // 'mouse'
+EXTERN char *p_mousem; // 'mousemodel'
+EXTERN int p_mousef; // 'mousefocus'
+EXTERN char *p_mousescroll; // 'mousescroll'
EXTERN long p_mousescroll_vert INIT(= MOUSESCROLL_VERT_DFLT);
EXTERN long p_mousescroll_hor INIT(= MOUSESCROLL_HOR_DFLT);
EXTERN long p_mouset; // 'mousetime'
EXTERN int p_more; // 'more'
-EXTERN char_u *p_opfunc; // 'operatorfunc'
-EXTERN char_u *p_para; // 'paragraphs'
+EXTERN char *p_nf; ///< 'nrformats'
+EXTERN char *p_opfunc; // 'operatorfunc'
+EXTERN char_u *p_para; // 'paragraphs'
EXTERN int p_paste; // 'paste'
-EXTERN char_u *p_pt; // 'pastetoggle'
-EXTERN char_u *p_pex; // 'patchexpr'
-EXTERN char_u *p_pm; // 'patchmode'
-EXTERN char_u *p_path; // 'path'
-EXTERN char_u *p_cdpath; // 'cdpath'
+EXTERN char *p_pt; // 'pastetoggle'
+EXTERN char_u *p_pex; // 'patchexpr'
+EXTERN char *p_pm; // 'patchmode'
+EXTERN char_u *p_path; // 'path'
+EXTERN char_u *p_cdpath; // 'cdpath'
+EXTERN int p_pi; ///< 'preserveindent'
EXTERN long p_pyx; // 'pyxversion'
-EXTERN char_u *p_rdb; // 'redrawdebug'
+EXTERN char *p_qe; ///< 'quoteescape'
+EXTERN int p_ro; ///< 'readonly'
+EXTERN char *p_rdb; // 'redrawdebug'
EXTERN unsigned rdb_flags;
-#ifdef IN_OPTION_C
-static char *(p_rdb_values[]) = {
- "compositor",
- "nothrottle",
- "invalid",
- "nodelta",
- NULL
-};
-#endif
#define RDB_COMPOSITOR 0x001
#define RDB_NOTHROTTLE 0x002
#define RDB_INVALID 0x004
#define RDB_NODELTA 0x008
-EXTERN long p_rdt; // 'redrawtime'
-EXTERN long p_re; // 'regexpengine'
-EXTERN long p_report; // 'report'
-EXTERN long p_pvh; // 'previewheight'
-EXTERN int p_ari; // 'allowrevins'
-EXTERN int p_ri; // 'revins'
-EXTERN int p_ru; // 'ruler'
-EXTERN char_u *p_ruf; // 'rulerformat'
-EXTERN char_u *p_pp; // 'packpath'
-EXTERN char_u *p_qftf; // 'quickfixtextfunc'
-EXTERN char_u *p_rtp; // 'runtimepath'
-EXTERN long p_scbk; // 'scrollback'
-EXTERN long p_sj; // 'scrolljump'
-EXTERN long p_so; // 'scrolloff'
-EXTERN char *p_sbo; // 'scrollopt'
-EXTERN char_u *p_sections; // 'sections'
-EXTERN int p_secure; // 'secure'
-EXTERN char_u *p_sel; // 'selection'
-EXTERN char_u *p_slm; // 'selectmode'
-EXTERN char_u *p_ssop; // 'sessionoptions'
+EXTERN long p_rdt; // 'redrawtime'
+EXTERN long p_re; // 'regexpengine'
+EXTERN long p_report; // 'report'
+EXTERN long p_pvh; // 'previewheight'
+EXTERN int p_ari; // 'allowrevins'
+EXTERN int p_ri; // 'revins'
+EXTERN int p_ru; // 'ruler'
+EXTERN char *p_ruf; // 'rulerformat'
+EXTERN char *p_pp; // 'packpath'
+EXTERN char *p_qftf; // 'quickfixtextfunc'
+EXTERN char *p_rtp; // 'runtimepath'
+EXTERN long p_scbk; // 'scrollback'
+EXTERN long p_sj; // 'scrolljump'
+EXTERN long p_so; // 'scrolloff'
+EXTERN char *p_sbo; // 'scrollopt'
+EXTERN char *p_sections; // 'sections'
+EXTERN int p_secure; // 'secure'
+EXTERN char *p_sel; // 'selection'
+EXTERN char *p_slm; // 'selectmode'
+EXTERN char *p_ssop; // 'sessionoptions'
EXTERN unsigned ssop_flags;
-#ifdef IN_OPTION_C
-// Also used for 'viewoptions'! Keep in sync with SSOP_ flags.
-static char *(p_ssop_values[]) = {
- "buffers", "winpos", "resize", "winsize",
- "localoptions", "options", "help", "blank", "globals", "slash", "unix",
- "sesdir", "curdir", "folds", "cursor", "tabpages", "terminal", "skiprtp",
- NULL
-};
-#endif
+
#define SSOP_BUFFERS 0x001
#define SSOP_WINPOS 0x002
#define SSOP_RESIZE 0x004
@@ -612,22 +685,23 @@ static char *(p_ssop_values[]) = {
#define SSOP_TERMINAL 0x10000
#define SSOP_SKIP_RTP 0x20000
-EXTERN char_u *p_sh; // 'shell'
-EXTERN char_u *p_shcf; // 'shellcmdflag'
-EXTERN char_u *p_sp; // 'shellpipe'
-EXTERN char_u *p_shq; // 'shellquote'
-EXTERN char_u *p_sxq; // 'shellxquote'
-EXTERN char_u *p_sxe; // 'shellxescape'
-EXTERN char_u *p_srr; // 'shellredir'
+EXTERN char_u *p_sh; // 'shell'
+EXTERN char_u *p_shcf; // 'shellcmdflag'
+EXTERN char *p_sp; // 'shellpipe'
+EXTERN char_u *p_shq; // 'shellquote'
+EXTERN char_u *p_sxq; // 'shellxquote'
+EXTERN char_u *p_sxe; // 'shellxescape'
+EXTERN char *p_srr; // 'shellredir'
EXTERN int p_stmp; // 'shelltemp'
#ifdef BACKSLASH_IN_FILENAME
EXTERN int p_ssl; // 'shellslash'
#endif
-EXTERN char_u *p_stl; // 'statusline'
-EXTERN char *p_wbr; // 'winbar'
+EXTERN char *p_stl; // 'statusline'
+EXTERN char *p_wbr; // 'winbar'
EXTERN int p_sr; // 'shiftround'
-EXTERN char_u *p_shm; // 'shortmess'
-EXTERN char_u *p_sbr; // 'showbreak'
+EXTERN long p_sw; ///< 'shiftwidth'
+EXTERN char *p_shm; // 'shortmess'
+EXTERN char *p_sbr; // 'showbreak'
EXTERN int p_sc; // 'showcmd'
EXTERN int p_sft; // 'showfulltag'
EXTERN int p_sm; // 'showmatch'
@@ -635,16 +709,17 @@ EXTERN int p_smd; // 'showmode'
EXTERN long p_ss; // 'sidescroll'
EXTERN long p_siso; // 'sidescrolloff'
EXTERN int p_scs; // 'smartcase'
+EXTERN int p_si; ///< 'smartindent'
EXTERN int p_sta; // 'smarttab'
+EXTERN long p_sts; ///< 'softtabstop'
EXTERN int p_sb; // 'splitbelow'
+EXTERN char *p_sua; ///< 'suffixesadd'
+EXTERN int p_swf; ///< 'swapfile'
+EXTERN long p_smc; ///< 'synmaxcol'
EXTERN long p_tpm; // 'tabpagemax'
-EXTERN char_u *p_tal; // 'tabline'
-EXTERN char_u *p_tpf; // 'termpastefilter'
+EXTERN char *p_tal; // 'tabline'
+EXTERN char *p_tpf; // 'termpastefilter'
EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter'
-#ifdef IN_OPTION_C
-static char *(p_tpf_values[]) =
-{ "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
-#endif
#define TPF_BS 0x001
#define TPF_HT 0x002
#define TPF_FF 0x004
@@ -652,29 +727,30 @@ static char *(p_tpf_values[]) =
#define TPF_DEL 0x010
#define TPF_C0 0x020
#define TPF_C1 0x040
-EXTERN char_u *p_sps; // 'spellsuggest'
+EXTERN char *p_tfu; ///< 'tagfunc'
+EXTERN char *p_umf; ///< 'usermarkfunc'
+EXTERN char *p_spc; ///< 'spellcapcheck'
+EXTERN char *p_spf; ///< 'spellfile'
+EXTERN char *p_spl; ///< 'spelllang'
+EXTERN char *p_spo; // 'spelloptions'
+EXTERN char *p_sps; // 'spellsuggest'
EXTERN int p_spr; // 'splitright'
EXTERN int p_sol; // 'startofline'
-EXTERN char_u *p_su; // 'suffixes'
-EXTERN char_u *p_swb; // 'switchbuf'
+EXTERN char_u *p_su; // 'suffixes'
+EXTERN char *p_swb; // 'switchbuf'
EXTERN unsigned swb_flags;
-#ifdef IN_OPTION_C
-static char *(p_swb_values[]) =
-{ "useopen", "usetab", "split", "newtab", "vsplit", "uselast", NULL };
-#endif
+// Keep in sync with p_swb_values in optionstr.c
#define SWB_USEOPEN 0x001
#define SWB_USETAB 0x002
#define SWB_SPLIT 0x004
#define SWB_NEWTAB 0x008
#define SWB_VSPLIT 0x010
#define SWB_USELAST 0x020
+EXTERN char *p_syn; ///< 'syntax'
+EXTERN long p_ts; ///< 'tabstop'
EXTERN int p_tbs; ///< 'tagbsearch'
-EXTERN char_u *p_tc; ///< 'tagcase'
+EXTERN char *p_tc; ///< 'tagcase'
EXTERN unsigned tc_flags; ///< flags from 'tagcase'
-#ifdef IN_OPTION_C
-static char *(p_tc_values[]) =
-{ "followic", "ignore", "match", "followscs", "smart", NULL };
-#endif
#define TC_FOLLOWIC 0x01
#define TC_IGNORE 0x02
#define TC_MATCH 0x04
@@ -685,35 +761,34 @@ EXTERN int p_tr; ///< 'tagrelative'
EXTERN char_u *p_tags; ///< 'tags'
EXTERN int p_tgst; ///< 'tagstack'
EXTERN int p_tbidi; ///< 'termbidi'
+EXTERN long p_tw; ///< 'textwidth'
EXTERN int p_to; ///< 'tildeop'
EXTERN int p_timeout; ///< 'timeout'
EXTERN long p_tm; ///< 'timeoutlen'
EXTERN int p_title; ///< 'title'
EXTERN long p_titlelen; ///< 'titlelen'
EXTERN char_u *p_titleold; ///< 'titleold'
-EXTERN char_u *p_titlestring; ///< 'titlestring'
+EXTERN char *p_titlestring; ///< 'titlestring'
EXTERN char_u *p_tsr; ///< 'thesaurus'
-EXTERN char_u *p_tsrfu; ///< 'thesaurusfunc'
EXTERN int p_tgc; ///< 'termguicolors'
EXTERN int p_ttimeout; ///< 'ttimeout'
EXTERN long p_ttm; ///< 'ttimeoutlen'
EXTERN char_u *p_udir; ///< 'undodir'
+EXTERN int p_udf; ///< 'undofile'
EXTERN long p_ul; ///< 'undolevels'
EXTERN long p_ur; ///< 'undoreload'
EXTERN long p_uc; ///< 'updatecount'
EXTERN long p_ut; ///< 'updatetime'
-EXTERN char_u *p_fcs; ///< 'fillchar'
-EXTERN char_u *p_shada; ///< 'shada'
+EXTERN char *p_shada; ///< 'shada'
EXTERN char *p_shadafile; ///< 'shadafile'
+EXTERN char *p_vsts; ///< 'varsofttabstop'
+EXTERN char *p_vts; ///< 'vartabstop'
EXTERN char_u *p_vdir; ///< 'viewdir'
-EXTERN char_u *p_vop; ///< 'viewoptions'
+EXTERN char *p_vop; ///< 'viewoptions'
EXTERN unsigned vop_flags; ///< uses SSOP_ flags
EXTERN int p_vb; ///< 'visualbell'
-EXTERN char_u *p_ve; ///< 'virtualedit'
+EXTERN char *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
-#ifdef IN_OPTION_C
-static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
-#endif
#define VE_BLOCK 5U // includes "all"
#define VE_INSERT 6U // includes "all"
#define VE_ALL 4U
@@ -724,29 +799,27 @@ EXTERN long p_verbose; // 'verbose'
#ifdef IN_OPTION_C
char_u *p_vfile = (char_u *)""; // used before options are initialized
#else
-extern char_u *p_vfile; // 'verbosefile'
+extern char *p_vfile; // 'verbosefile'
#endif
EXTERN int p_warn; // 'warn'
-EXTERN char_u *p_wop; // 'wildoptions'
+EXTERN char *p_wop; // 'wildoptions'
EXTERN unsigned wop_flags;
-#ifdef IN_OPTION_C
-static char *(p_wop_values[]) = { "tagfile", "pum", NULL };
-#endif
#define WOP_TAGFILE 0x01
#define WOP_PUM 0x02
EXTERN long p_window; // 'window'
-EXTERN char_u *p_wak; // 'winaltkeys'
-EXTERN char_u *p_wig; // 'wildignore'
-EXTERN char_u *p_ww; // 'whichwrap'
+EXTERN char *p_wak; // 'winaltkeys'
+EXTERN char *p_wig; // 'wildignore'
+EXTERN char *p_ww; // 'whichwrap'
EXTERN long p_wc; // 'wildchar'
EXTERN long p_wcm; // 'wildcharm'
EXTERN int p_wic; // 'wildignorecase'
-EXTERN char_u *p_wim; // 'wildmode'
+EXTERN char *p_wim; // 'wildmode'
EXTERN int p_wmnu; // 'wildmenu'
EXTERN long p_wh; // 'winheight'
EXTERN long p_wmh; // 'winminheight'
EXTERN long p_wmw; // 'winminwidth'
EXTERN long p_wiw; // 'winwidth'
+EXTERN long p_wm; ///< 'wrapmargin'
EXTERN int p_ws; // 'wrapscan'
EXTERN int p_write; // 'write'
EXTERN int p_wa; // 'writeany'
@@ -901,8 +974,8 @@ enum {
WV_WRAP,
WV_SCL,
WV_WINHL,
- WV_FCS,
WV_LCS,
+ WV_FCS,
WV_WINBL,
WV_WBR,
WV_COUNT, // must be the last one
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
new file mode 100644
index 0000000000..1bdfcbecb9
--- /dev/null
+++ b/src/nvim/optionstr.c
@@ -0,0 +1,1663 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nvim/api/private/helpers.h"
+#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/cursor_shape.h"
+#include "nvim/diff.h"
+#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
+#include "nvim/eval/vars.h"
+#include "nvim/ex_getln.h"
+#include "nvim/hardcopy.h"
+#include "nvim/highlight_group.h"
+#include "nvim/indent.h"
+#include "nvim/indent_c.h"
+#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
+#include "nvim/mapping.h"
+#include "nvim/memline.h"
+#include "nvim/mouse.h"
+#include "nvim/move.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
+#include "nvim/optionstr.h"
+#include "nvim/quickfix.h"
+#include "nvim/runtime.h"
+#include "nvim/spell.h"
+#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "optionstr.c.generated.h"
+#endif
+
+static char e_unclosed_expression_sequence[]
+ = N_("E540: Unclosed expression sequence");
+static char e_unbalanced_groups[]
+ = N_("E542: unbalanced groups");
+static char e_backupext_and_patchmode_are_equal[]
+ = N_("E589: 'backupext' and 'patchmode' are equal");
+static char e_showbreak_contains_unprintable_or_wide_character[]
+ = N_("E595: 'showbreak' contains unprintable or wide character");
+
+static char *(p_ambw_values[]) = { "single", "double", NULL };
+static char *(p_bg_values[]) = { "light", "dark", NULL };
+static char *(p_bkc_values[]) = { "yes", "auto", "no", "breaksymlink", "breakhardlink", NULL };
+static char *(p_bo_values[]) = { "all", "backspace", "cursor", "complete", "copy", "ctrlg", "error",
+ "esc", "ex", "hangul", "lang", "mess", "showmatch", "operator",
+ "register", "shell", "spell", "wildmode", NULL };
+static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", NULL };
+static char *(p_ff_values[]) = { FF_UNIX, FF_DOS, FF_MAC, NULL };
+static char *(p_cmp_values[]) = { "internal", "keepascii", NULL };
+static char *(p_dy_values[]) = { "lastline", "truncate", "uhex", "msgsep", NULL };
+static char *(p_fdo_values[]) = { "all", "block", "hor", "mark", "percent", "quickfix", "search",
+ "tag", "insert", "undo", "jump", NULL };
+/// Also used for 'viewoptions'! Keep in sync with SSOP_ flags.
+static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "localoptions",
+ "options", "help", "blank", "globals", "slash", "unix", "sesdir",
+ "curdir", "folds", "cursor", "tabpages", "terminal", "skiprtp",
+ NULL };
+// Keep in sync with SWB_ flags in option_defs.h
+static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vsplit", "uselast",
+ NULL };
+static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL };
+static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
+static char *(p_wop_values[]) = { "tagfile", "pum", NULL };
+static char *(p_wak_values[]) = { "yes", "menu", "no", NULL };
+static char *(p_mousem_values[]) = { "extend", "popup", "popup_setpos", "mac", NULL };
+static char *(p_sel_values[]) = { "inclusive", "exclusive", "old", NULL };
+static char *(p_slm_values[]) = { "mouse", "key", "cmd", NULL };
+static char *(p_km_values[]) = { "startsel", "stopsel", NULL };
+static char *(p_scbopt_values[]) = { "ver", "hor", "jump", NULL };
+static char *(p_debug_values[]) = { "msg", "throw", "beep", NULL };
+static char *(p_ead_values[]) = { "both", "ver", "hor", NULL };
+static char *(p_buftype_values[]) = { "nofile", "nowrite", "quickfix", "help", "acwrite",
+ "terminal", "prompt", NULL };
+static char *(p_bufhidden_values[]) = { "hide", "unload", "delete", "wipe", NULL };
+static char *(p_bs_values[]) = { "indent", "eol", "start", "nostop", NULL };
+static char *(p_fdm_values[]) = { "manual", "expr", "marker", "indent",
+ "syntax", "diff", NULL };
+static char *(p_fcl_values[]) = { "all", NULL };
+static char *(p_cot_values[]) = { "menu", "menuone", "longest", "preview", "noinsert", "noselect",
+ NULL };
+#ifdef BACKSLASH_IN_FILENAME
+static char *(p_csl_values[]) = { "slash", "backslash", NULL };
+#endif
+
+static char *(p_scl_values[]) = { "yes", "no", "auto", "auto:1", "auto:2", "auto:3", "auto:4",
+ "auto:5", "auto:6", "auto:7", "auto:8", "auto:9", "yes:1",
+ "yes:2", "yes:3", "yes:4", "yes:5", "yes:6", "yes:7", "yes:8",
+ "yes:9", "number", NULL };
+static char *(p_fdc_values[]) = { "auto", "auto:1", "auto:2", "auto:3", "auto:4", "auto:5",
+ "auto:6", "auto:7", "auto:8", "auto:9", "0", "1", "2", "3", "4",
+ "5", "6", "7", "8", "9", NULL };
+static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
+static char *(p_icm_values[]) = { "nosplit", "split", NULL };
+static char *(p_jop_values[]) = { "stack", "view", NULL };
+static char *(p_tpf_values[]) = { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
+static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", NULL };
+
+/// All possible flags for 'shm'.
+static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW,
+ SHM_WRI, SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL,
+ SHM_OVER, SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO,
+ SHM_COMPLETIONMENU, SHM_RECORDING, SHM_FILEINFO, SHM_SEARCHCOUNT, 0, };
+
+/// After setting various option values: recompute variables that depend on
+/// option values.
+void didset_string_options(void)
+{
+ (void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
+ (void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true);
+ (void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, true);
+ (void)opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true);
+ (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
+ (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
+ (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
+ (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
+ (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
+ (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
+ (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
+ (void)opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
+ (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
+ (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
+ (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
+}
+
+/// Trigger the OptionSet autocommand.
+/// "opt_idx" is the index of the option being set.
+/// "opt_flags" can be OPT_LOCAL etc.
+/// "oldval" the old value
+/// "oldval_l" the old local value (only non-NULL if global and local value are set)
+/// "oldval_g" the old global value (only non-NULL if global and local value are set)
+/// "newval" the new value
+void trigger_optionsset_string(int opt_idx, int opt_flags, char *oldval, char *oldval_l,
+ char *oldval_g, char *newval)
+{
+ // Don't do this recursively.
+ if (oldval != NULL
+ && newval != NULL
+ && *get_vim_var_str(VV_OPTION_TYPE) == NUL) {
+ char buf_type[7];
+
+ vim_snprintf(buf_type, ARRAY_SIZE(buf_type), "%s",
+ (opt_flags & OPT_LOCAL) ? "local" : "global");
+ set_vim_var_string(VV_OPTION_OLD, oldval, -1);
+ set_vim_var_string(VV_OPTION_NEW, newval, -1);
+ set_vim_var_string(VV_OPTION_TYPE, buf_type, -1);
+ if (opt_flags & OPT_LOCAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setlocal", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
+ }
+ if (opt_flags & OPT_GLOBAL) {
+ set_vim_var_string(VV_OPTION_COMMAND, "setglobal", -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval, -1);
+ }
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ set_vim_var_string(VV_OPTION_COMMAND, "set", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval_l, -1);
+ set_vim_var_string(VV_OPTION_OLDGLOBAL, oldval_g, -1);
+ }
+ if (opt_flags & OPT_MODELINE) {
+ set_vim_var_string(VV_OPTION_COMMAND, "modeline", -1);
+ set_vim_var_string(VV_OPTION_OLDLOCAL, oldval, -1);
+ }
+ apply_autocmds(EVENT_OPTIONSET, get_option_fullname(opt_idx), NULL, false, NULL);
+ reset_v_option_vars();
+ }
+}
+
+static char *illegal_char(char *errbuf, size_t errbuflen, int c)
+{
+ if (errbuf == NULL) {
+ return "";
+ }
+ vim_snprintf(errbuf, errbuflen, _("E539: Illegal character <%s>"),
+ (char *)transchar(c));
+ return errbuf;
+}
+
+/// Check string options in a buffer for NULL value.
+void check_buf_options(buf_T *buf)
+{
+ check_string_option(&buf->b_p_bh);
+ check_string_option(&buf->b_p_bt);
+ check_string_option(&buf->b_p_fenc);
+ check_string_option(&buf->b_p_ff);
+ check_string_option(&buf->b_p_def);
+ check_string_option(&buf->b_p_inc);
+ check_string_option(&buf->b_p_inex);
+ check_string_option(&buf->b_p_inde);
+ check_string_option(&buf->b_p_indk);
+ check_string_option(&buf->b_p_fp);
+ check_string_option(&buf->b_p_fex);
+ check_string_option(&buf->b_p_kp);
+ check_string_option(&buf->b_p_mps);
+ check_string_option(&buf->b_p_fo);
+ check_string_option(&buf->b_p_flp);
+ check_string_option(&buf->b_p_isk);
+ check_string_option(&buf->b_p_com);
+ check_string_option(&buf->b_p_cms);
+ check_string_option(&buf->b_p_nf);
+ check_string_option(&buf->b_p_qe);
+ check_string_option(&buf->b_p_syn);
+ check_string_option(&buf->b_s.b_syn_isk);
+ check_string_option(&buf->b_s.b_p_spc);
+ check_string_option(&buf->b_s.b_p_spf);
+ check_string_option(&buf->b_s.b_p_spl);
+ check_string_option(&buf->b_s.b_p_spo);
+ check_string_option(&buf->b_p_sua);
+ check_string_option(&buf->b_p_cink);
+ check_string_option(&buf->b_p_cino);
+ parse_cino(buf);
+ check_string_option(&buf->b_p_ft);
+ check_string_option(&buf->b_p_cinw);
+ check_string_option(&buf->b_p_cinsd);
+ check_string_option(&buf->b_p_cpt);
+ check_string_option(&buf->b_p_cfu);
+ check_string_option(&buf->b_p_ofu);
+ check_string_option(&buf->b_p_keymap);
+ check_string_option(&buf->b_p_gp);
+ check_string_option(&buf->b_p_mp);
+ check_string_option(&buf->b_p_efm);
+ check_string_option(&buf->b_p_ep);
+ check_string_option(&buf->b_p_path);
+ check_string_option(&buf->b_p_tags);
+ check_string_option(&buf->b_p_tfu);
+ check_string_option(&buf->b_p_tc);
+ check_string_option(&buf->b_p_dict);
+ check_string_option(&buf->b_p_tsr);
+ check_string_option(&buf->b_p_tsrfu);
+ check_string_option(&buf->b_p_lw);
+ check_string_option(&buf->b_p_bkc);
+ check_string_option(&buf->b_p_menc);
+ check_string_option(&buf->b_p_vsts);
+ check_string_option(&buf->b_p_vts);
+}
+
+/// Free the string allocated for an option.
+/// Checks for the string being empty_option. This may happen if we're out of
+/// memory, vim_strsave() returned NULL, which was replaced by empty_option by
+/// check_options().
+/// Does NOT check for P_ALLOCED flag!
+void free_string_option(char *p)
+{
+ if (p != empty_option) {
+ xfree(p);
+ }
+}
+
+void clear_string_option(char **pp)
+{
+ if (*pp != empty_option) {
+ xfree(*pp);
+ }
+ *pp = empty_option;
+}
+
+void check_string_option(char **pp)
+{
+ if (*pp == NULL) {
+ *pp = empty_option;
+ }
+}
+
+/// Set global value for string option when it's a local option.
+///
+/// @param opt_idx option index
+/// @param varp pointer to option variable
+static void set_string_option_global(int opt_idx, char **varp)
+{
+ char **p;
+
+ // the global value is always allocated
+ if (is_window_local_option(opt_idx)) {
+ p = (char **)GLOBAL_WO(varp);
+ } else {
+ p = (char **)get_option_var(opt_idx);
+ }
+ if (!is_global_option(opt_idx) && p != varp) {
+ char *s = xstrdup(*varp);
+ free_string_option(*p);
+ *p = s;
+ }
+}
+
+/// Set a string option to a new value (without checking the effect).
+/// The string is copied into allocated memory.
+/// if ("opt_idx" == -1) "name" is used, otherwise "opt_idx" is used.
+/// When "set_sid" is zero set the scriptID to current_sctx.sc_sid. When
+/// "set_sid" is SID_NONE don't set the scriptID. Otherwise set the scriptID to
+/// "set_sid".
+///
+/// @param opt_flags OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL
+void set_string_option_direct(const char *name, int opt_idx, const char *val, int opt_flags,
+ int set_sid)
+{
+ char *s;
+ char **varp;
+ int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
+ int idx = opt_idx;
+
+ if (idx == -1) { // Use name.
+ idx = findoption(name);
+ if (idx < 0) { // Not found (should not happen).
+ internal_error("set_string_option_direct()");
+ siemsg(_("For option %s"), name);
+ return;
+ }
+ }
+
+ if (is_hidden_option(idx)) { // can't set hidden option
+ return;
+ }
+
+ assert((void *)get_option_var(idx) != (void *)&p_shada);
+
+ s = xstrdup(val);
+ {
+ varp = (char **)get_option_varp_scope(idx, both ? OPT_LOCAL : opt_flags);
+ if ((opt_flags & OPT_FREE) && (get_option_flags(idx) & P_ALLOCED)) {
+ free_string_option(*varp);
+ }
+ *varp = s;
+
+ // For buffer/window local option may also set the global value.
+ if (both) {
+ set_string_option_global(idx, varp);
+ }
+
+ set_option_flag(idx, P_ALLOCED);
+
+ // When setting both values of a global option with a local value,
+ // make the local value empty, so that the global value is used.
+ if (is_global_local_option(idx) && both) {
+ free_string_option(*varp);
+ *varp = empty_option;
+ }
+ if (set_sid != SID_NONE) {
+ sctx_T script_ctx;
+
+ if (set_sid == 0) {
+ script_ctx = current_sctx;
+ } else {
+ script_ctx.sc_sid = set_sid;
+ script_ctx.sc_seq = 0;
+ script_ctx.sc_lnum = 0;
+ }
+ set_option_sctx_idx(idx, opt_flags, script_ctx);
+ }
+ }
+}
+
+/// Like set_string_option_direct(), but for a window-local option in "wp".
+/// Blocks autocommands to avoid the old curwin becoming invalid.
+void set_string_option_direct_in_win(win_T *wp, const char *name, int opt_idx, const char *val,
+ int opt_flags, int set_sid)
+{
+ win_T *save_curwin = curwin;
+
+ block_autocmds();
+ curwin = wp;
+ curbuf = curwin->w_buffer;
+ set_string_option_direct(name, opt_idx, val, opt_flags, set_sid);
+ curwin = save_curwin;
+ curbuf = curwin->w_buffer;
+ unblock_autocmds();
+}
+
+/// Set a string option to a new value, handling the effects
+///
+/// @param[in] opt_idx Option to set.
+/// @param[in] value New value.
+/// @param[in] opt_flags Option flags: expected to contain #OPT_LOCAL and/or
+/// #OPT_GLOBAL.
+///
+/// @return NULL on success, an untranslated error message on error.
+char *set_string_option(const int opt_idx, const char *const value, const int opt_flags)
+ FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (is_hidden_option(opt_idx)) { // don't set hidden option
+ return NULL;
+ }
+
+ char *const s = xstrdup(value);
+ char **const varp
+ = (char **)get_option_varp_scope(opt_idx,
+ (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ ? (is_global_local_option(opt_idx)
+ ? OPT_GLOBAL : OPT_LOCAL)
+ : opt_flags);
+ char *const oldval = *varp;
+ char *oldval_l = NULL;
+ char *oldval_g = NULL;
+
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ oldval_l = *(char **)get_option_varp_scope(opt_idx, OPT_LOCAL);
+ oldval_g = *(char **)get_option_varp_scope(opt_idx, OPT_GLOBAL);
+ }
+
+ *varp = s;
+
+ char *const saved_oldval = xstrdup(oldval);
+ char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup(oldval_l) : 0;
+ char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup(oldval_g) : 0;
+ char *const saved_newval = xstrdup(s);
+
+ int value_checked = false;
+ char *const errmsg = did_set_string_option(opt_idx, varp, oldval,
+ NULL, 0,
+ opt_flags, &value_checked);
+ if (errmsg == NULL) {
+ did_set_option(opt_idx, opt_flags, true, value_checked);
+ }
+
+ // call autocommand after handling side effects
+ if (errmsg == NULL) {
+ if (!starting) {
+ trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
+ saved_newval);
+ }
+ if (get_option_flags(opt_idx) & P_UI_OPTION) {
+ ui_call_option_set(cstr_as_string(get_option_fullname(opt_idx)),
+ STRING_OBJ(cstr_as_string(saved_newval)));
+ }
+ }
+ xfree(saved_oldval);
+ xfree(saved_oldval_l);
+ xfree(saved_oldval_g);
+ xfree(saved_newval);
+
+ return errmsg;
+}
+
+/// Return true if "val" is a valid 'filetype' name.
+/// Also used for 'syntax' and 'keymap'.
+static bool valid_filetype(const char *val)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return valid_name(val, ".-_");
+}
+
+/// Handle setting 'mousescroll'.
+/// @return error message, NULL if it's OK.
+static char *check_mousescroll(char *string)
+{
+ long vertical = -1;
+ long horizontal = -1;
+
+ for (;;) {
+ char *end = vim_strchr(string, ',');
+ size_t length = end ? (size_t)(end - string) : STRLEN(string);
+
+ // Both "ver:" and "hor:" are 4 bytes long.
+ // They should be followed by at least one digit.
+ if (length <= 4) {
+ return e_invarg;
+ }
+
+ long *direction;
+
+ if (memcmp(string, "ver:", 4) == 0) {
+ direction = &vertical;
+ } else if (memcmp(string, "hor:", 4) == 0) {
+ direction = &horizontal;
+ } else {
+ return e_invarg;
+ }
+
+ // If the direction has already been set, this is a duplicate.
+ if (*direction != -1) {
+ return e_invarg;
+ }
+
+ // Verify that only digits follow the colon.
+ for (size_t i = 4; i < length; i++) {
+ if (!ascii_isdigit(string[i])) {
+ return N_("E548: digit expected");
+ }
+ }
+
+ string += 4;
+ *direction = getdigits_int(&string, false, -1);
+
+ // Num options are generally kept within the signed int range.
+ // We know this number won't be negative because we've already checked for
+ // a minus sign. We'll allow 0 as a means of disabling mouse scrolling.
+ if (*direction == -1) {
+ return e_invarg;
+ }
+
+ if (!end) {
+ break;
+ }
+
+ string = end + 1;
+ }
+
+ // If a direction wasn't set, fallback to the default value.
+ p_mousescroll_vert = (vertical == -1) ? MOUSESCROLL_VERT_DFLT : vertical;
+ p_mousescroll_hor = (horizontal == -1) ? MOUSESCROLL_HOR_DFLT : horizontal;
+
+ return NULL;
+}
+
+/// Handle setting 'signcolumn' for value 'val'
+///
+/// @return OK when the value is valid, FAIL otherwise
+static int check_signcolumn(char *val)
+{
+ if (*val == NUL) {
+ return FAIL;
+ }
+ // check for basic match
+ if (check_opt_strings(val, p_scl_values, false) == OK) {
+ return OK;
+ }
+
+ // check for 'auto:<NUMBER>-<NUMBER>'
+ if (STRLEN(val) == 8
+ && !STRNCMP(val, "auto:", 5)
+ && ascii_isdigit(val[5])
+ && val[6] == '-'
+ && ascii_isdigit(val[7])) {
+ int min = val[5] - '0';
+ int max = val[7] - '0';
+ if (min < 1 || max < 2 || min > 8 || max > 9 || min >= max) {
+ return FAIL;
+ }
+ return OK;
+ }
+
+ return FAIL;
+}
+
+/// Check validity of options with the 'statusline' format.
+/// Return an untranslated error message or NULL.
+char *check_stl_option(char *s)
+{
+ int groupdepth = 0;
+ static char errbuf[80];
+
+ while (*s) {
+ // Check for valid keys after % sequences
+ while (*s && *s != '%') {
+ s++;
+ }
+ if (!*s) {
+ break;
+ }
+ s++;
+ if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE) {
+ s++;
+ continue;
+ }
+ if (*s == ')') {
+ s++;
+ if (--groupdepth < 0) {
+ break;
+ }
+ continue;
+ }
+ if (*s == '-') {
+ s++;
+ }
+ while (ascii_isdigit(*s)) {
+ s++;
+ }
+ if (*s == STL_USER_HL) {
+ continue;
+ }
+ if (*s == '.') {
+ s++;
+ while (*s && ascii_isdigit(*s)) {
+ s++;
+ }
+ }
+ if (*s == '(') {
+ groupdepth++;
+ continue;
+ }
+ if (vim_strchr(STL_ALL, *s) == NULL) {
+ return illegal_char(errbuf, sizeof(errbuf), *s);
+ }
+ if (*s == '{') {
+ bool reevaluate = (*++s == '%');
+
+ if (reevaluate && *++s == '}') {
+ // "}" is not allowed immediately after "%{%"
+ return illegal_char(errbuf, sizeof(errbuf), '}');
+ }
+ while ((*s != '}' || (reevaluate && s[-1] != '%')) && *s) {
+ s++;
+ }
+ if (*s != '}') {
+ return e_unclosed_expression_sequence;
+ }
+ }
+ }
+ if (groupdepth != 0) {
+ return e_unbalanced_groups;
+ }
+ return NULL;
+}
+
+static int shada_idx = -1;
+
+/// Handle string options that need some action to perform when changed.
+/// The new value must be allocated.
+///
+/// @param opt_idx index in options[] table
+/// @param varp pointer to the option variable
+/// @param oldval previous value of the option
+/// @param errbuf buffer for errors, or NULL
+/// @param errbuflen length of errors buffer
+/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param value_checked value was checked to be safe, no need to set P_INSECURE
+///
+/// @return NULL for success, or an untranslated error message for an error
+char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf, size_t errbuflen,
+ int opt_flags, int *value_checked)
+{
+ char *errmsg = NULL;
+ char *s, *p;
+ int did_chartab = false;
+ bool free_oldval = (get_option_flags(opt_idx) & P_ALLOCED);
+ bool value_changed = false;
+
+ // Get the global option to compare with, otherwise we would have to check
+ // two values for all local options.
+ char **gvarp = (char **)get_option_varp_scope(opt_idx, OPT_GLOBAL);
+
+ // Disallow changing some options from secure mode
+ if ((secure || sandbox != 0)
+ && (get_option_flags(opt_idx) & P_SECURE)) {
+ errmsg = e_secure;
+ } else if (((get_option_flags(opt_idx) & P_NFNAME)
+ && strpbrk(*varp, (secure ? "/\\*?[|;&<>\r\n" : "/\\*?[<>\r\n")) != NULL)
+ || ((get_option_flags(opt_idx) & P_NDNAME)
+ && strpbrk(*varp, "*?[|;&<>\r\n") != NULL)) {
+ // Check for a "normal" directory or file name in some options. Disallow a
+ // path separator (slash and/or backslash), wildcards and characters that
+ // are often illegal in a file name. Be more permissive if "secure" is off.
+ errmsg = e_invarg;
+ } else if (gvarp == &p_bkc) { // 'backupcopy'
+ char *bkc = p_bkc;
+ unsigned int *flags = &bkc_flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ bkc = curbuf->b_p_bkc;
+ flags = &curbuf->b_bkc_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *bkc == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else {
+ if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+
+ if (((*flags & BKC_AUTO) != 0)
+ + ((*flags & BKC_YES) != 0)
+ + ((*flags & BKC_NO) != 0) != 1) {
+ // Must have exactly one of "auto", "yes" and "no".
+ (void)opt_strings_flags(oldval, p_bkc_values, flags, true);
+ errmsg = e_invarg;
+ }
+ }
+ } else if (varp == &p_bex || varp == &p_pm) { // 'backupext' and 'patchmode'
+ if (STRCMP(*p_bex == '.' ? p_bex + 1 : p_bex,
+ *p_pm == '.' ? p_pm + 1 : p_pm) == 0) {
+ errmsg = e_backupext_and_patchmode_are_equal;
+ }
+ } else if (varp == &curwin->w_p_briopt) { // 'breakindentopt'
+ if (briopt_check(curwin) == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_isi
+ || varp == &(curbuf->b_p_isk)
+ || varp == &p_isp
+ || varp == &p_isf) {
+ // 'isident', 'iskeyword', 'isprint or 'isfname' option: refill g_chartab[]
+ // If the new option is invalid, use old value. 'lisp' option: refill
+ // g_chartab[] for '-' char
+ if (init_chartab() == FAIL) {
+ did_chartab = true; // need to restore it below
+ errmsg = e_invarg; // error in value
+ }
+ } else if (varp == &p_hf) { // 'helpfile'
+ // May compute new values for $VIM and $VIMRUNTIME
+ if (didset_vim) {
+ vim_unsetenv_ext("VIM");
+ }
+ if (didset_vimruntime) {
+ vim_unsetenv_ext("VIMRUNTIME");
+ }
+ } else if (varp == &p_rtp || varp == &p_pp) { // 'runtimepath' 'packpath'
+ runtime_search_path_invalidate();
+ } else if (varp == &curwin->w_p_culopt
+ || gvarp == &curwin->w_allbuf_opt.wo_culopt) { // 'cursorlineopt'
+ if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &curwin->w_p_cc) { // 'colorcolumn'
+ errmsg = check_colorcolumn(curwin);
+ } else if (varp == (char **)&p_hlg) { // 'helplang'
+ // Check for "", "ab", "ab,cd", etc.
+ for (s = (char *)p_hlg; *s != NUL; s += 3) {
+ if (s[1] == NUL || ((s[2] != ',' || s[3] == NUL) && s[2] != NUL)) {
+ errmsg = e_invarg;
+ break;
+ }
+ if (s[2] == NUL) {
+ break;
+ }
+ }
+ } else if (varp == &p_hl) { // 'highlight'
+ if (STRCMP(*varp, HIGHLIGHT_INIT) != 0) {
+ errmsg = e_unsupportedoption;
+ }
+ } else if (varp == &p_jop) { // 'jumpoptions'
+ if (opt_strings_flags(p_jop, p_jop_values, &jop_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_nf) { // 'nrformats'
+ if (check_opt_strings(*varp, p_nf_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_ssop) { // 'sessionoptions'
+ if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
+ // Don't allow both "sesdir" and "curdir".
+ (void)opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_vop) { // 'viewoptions'
+ if (opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_rdb) { // 'redrawdebug'
+ if (opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_sbo) { // 'scrollopt'
+ if (check_opt_strings(p_sbo, p_scbopt_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_ambw || (int *)varp == &p_emoji) { // 'ambiwidth'
+ if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
+ errmsg = e_invarg;
+ } else {
+ errmsg = check_chars_options();
+ }
+ } else if (varp == &p_bg) { // 'background'
+ if (check_opt_strings(p_bg, p_bg_values, false) == OK) {
+ int dark = (*p_bg == 'd');
+
+ init_highlight(false, false);
+
+ if (dark != (*p_bg == 'd') && get_var_value("g:colors_name") != NULL) {
+ // The color scheme must have set 'background' back to another
+ // value, that's not what we want here. Disable the color
+ // scheme and set the colors again.
+ do_unlet(S_LEN("g:colors_name"), true);
+ free_string_option(p_bg);
+ p_bg = xstrdup((dark ? "dark" : "light"));
+ check_string_option(&p_bg);
+ init_highlight(false, false);
+ }
+ } else {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_wim) { // 'wildmode'
+ if (check_opt_wim() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_wop) { // 'wildoptions'
+ if (opt_strings_flags(p_wop, p_wop_values, &wop_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_wak) { // 'winaltkeys'
+ if (*p_wak == NUL
+ || check_opt_strings(p_wak, p_wak_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_ei) { // 'eventignore'
+ if (check_ei() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_enc || gvarp == &p_fenc || gvarp == &p_menc) {
+ // 'encoding', 'fileencoding' and 'makeencoding'
+ if (gvarp == &p_fenc) {
+ if (!MODIFIABLE(curbuf) && opt_flags != OPT_GLOBAL) {
+ errmsg = e_modifiable;
+ } else if (vim_strchr(*varp, ',') != NULL) {
+ // No comma allowed in 'fileencoding'; catches confusing it
+ // with 'fileencodings'.
+ errmsg = e_invarg;
+ } else {
+ // May show a "+" in the title now.
+ redraw_titles();
+ // Add 'fileencoding' to the swap file.
+ ml_setflags(curbuf);
+ }
+ }
+
+ if (errmsg == NULL) {
+ // canonize the value, so that STRCMP() can be used on it
+ p = enc_canonize(*varp);
+ xfree(*varp);
+ *varp = p;
+ if (varp == &p_enc) {
+ // only encoding=utf-8 allowed
+ if (STRCMP(p_enc, "utf-8") != 0) {
+ errmsg = e_unsupportedoption;
+ } else {
+ spell_reload();
+ }
+ }
+ }
+ } else if (varp == &p_penc) {
+ // Canonize printencoding if VIM standard one
+ p = enc_canonize(p_penc);
+ xfree(p_penc);
+ p_penc = p;
+ } else if (varp == &curbuf->b_p_keymap) {
+ if (!valid_filetype(*varp)) {
+ errmsg = e_invarg;
+ } else {
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'keymap' has
+ // been checked to be safe.
+ secure = 0;
+
+ // load or unload key mapping tables
+ errmsg = keymap_init();
+
+ secure = secure_save;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
+ }
+
+ if (errmsg == NULL) {
+ if (*curbuf->b_p_keymap != NUL) {
+ // Installed a new keymap, switch on using it.
+ curbuf->b_p_iminsert = B_IMODE_LMAP;
+ if (curbuf->b_p_imsearch != B_IMODE_USE_INSERT) {
+ curbuf->b_p_imsearch = B_IMODE_LMAP;
+ }
+ } else {
+ // Cleared the keymap, may reset 'iminsert' and 'imsearch'.
+ if (curbuf->b_p_iminsert == B_IMODE_LMAP) {
+ curbuf->b_p_iminsert = B_IMODE_NONE;
+ }
+ if (curbuf->b_p_imsearch == B_IMODE_LMAP) {
+ curbuf->b_p_imsearch = B_IMODE_USE_INSERT;
+ }
+ }
+ if ((opt_flags & OPT_LOCAL) == 0) {
+ set_iminsert_global();
+ set_imsearch_global();
+ }
+ status_redraw_curbuf();
+ }
+ } else if (gvarp == &p_ff) { // 'fileformat'
+ if (!MODIFIABLE(curbuf) && !(opt_flags & OPT_GLOBAL)) {
+ errmsg = e_modifiable;
+ } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
+ errmsg = e_invarg;
+ } else {
+ redraw_titles();
+ // update flag in swap file
+ ml_setflags(curbuf);
+ // Redraw needed when switching to/from "mac": a CR in the text
+ // will be displayed differently.
+ if (get_fileformat(curbuf) == EOL_MAC || *oldval == 'm') {
+ redraw_curbuf_later(UPD_NOT_VALID);
+ }
+ }
+ } else if (varp == &p_ffs) { // 'fileformats'
+ if (check_opt_strings(p_ffs, p_ff_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_mps) { // 'matchpairs'
+ for (p = *varp; *p != NUL; p++) {
+ int x2 = -1;
+ int x3 = -1;
+
+ p += utfc_ptr2len(p);
+ if (*p != NUL) {
+ x2 = (unsigned char)(*p++);
+ }
+ if (*p != NUL) {
+ x3 = utf_ptr2char(p);
+ p += utfc_ptr2len(p);
+ }
+ if (x2 != ':' || x3 == -1 || (*p != NUL && *p != ',')) {
+ errmsg = e_invarg;
+ break;
+ }
+ if (*p == NUL) {
+ break;
+ }
+ }
+ } else if (gvarp == &p_com) { // 'comments'
+ for (s = *varp; *s;) {
+ while (*s && *s != ':') {
+ if (vim_strchr(COM_ALL, *s) == NULL
+ && !ascii_isdigit(*s) && *s != '-') {
+ errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
+ }
+ s++;
+ }
+ if (*s++ == NUL) {
+ errmsg = N_("E524: Missing colon");
+ } else if (*s == ',' || *s == NUL) {
+ errmsg = N_("E525: Zero length string");
+ }
+ if (errmsg != NULL) {
+ break;
+ }
+ while (*s && *s != ',') {
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
+ }
+ s++;
+ }
+ s = skip_to_option_part(s);
+ }
+ } else if (varp == &p_lcs || varp == &p_fcs) { // global 'listchars' or 'fillchars'
+ char **local_ptr = varp == &p_lcs ? &curwin->w_p_lcs : &curwin->w_p_fcs;
+ // only apply the global value to "curwin" when it does not have a local value
+ errmsg =
+ set_chars_option(curwin, varp, **local_ptr == NUL || !(opt_flags & OPT_GLOBAL));
+ if (errmsg == NULL) {
+ // If the current window is set to use the global
+ // 'listchars'/'fillchars' value, clear the window-local value.
+ if (!(opt_flags & OPT_GLOBAL)) {
+ clear_string_option(local_ptr);
+ }
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ // If the current window has a local value need to apply it
+ // again, it was changed when setting the global value.
+ // If no error was returned above, we don't expect an error
+ // here, so ignore the return value.
+ local_ptr = varp == &p_lcs ? &wp->w_p_lcs : &wp->w_p_fcs;
+ if (**local_ptr == NUL) {
+ (void)set_chars_option(wp, local_ptr, true);
+ }
+ }
+ redraw_all_later(UPD_NOT_VALID);
+ }
+ } else if (varp == &curwin->w_p_lcs) { // local 'listchars'
+ errmsg = set_chars_option(curwin, varp, true);
+ } else if (varp == &curwin->w_p_fcs) { // local 'fillchars'
+ errmsg = set_chars_option(curwin, varp, true);
+ } else if (varp == &p_cedit) { // 'cedit'
+ errmsg = check_cedit();
+ } else if (varp == &p_vfile) { // 'verbosefile'
+ verbose_stop();
+ if (*p_vfile != NUL && verbose_open() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_shada) { // 'shada'
+ // TODO(ZyX-I): Remove this code in the future, alongside with &viminfo
+ // option.
+ opt_idx = ((get_option_fullname(opt_idx)[0] == 'v')
+ ? (shada_idx == -1 ? ((shada_idx = findoption("shada"))) : shada_idx)
+ : opt_idx);
+ // Update free_oldval now that we have the opt_idx for 'shada', otherwise
+ // there would be a disconnect between the check for P_ALLOCED at the start
+ // of the function and the set of P_ALLOCED at the end of the function.
+ free_oldval = (get_option_flags(opt_idx) & P_ALLOCED);
+ for (s = p_shada; *s;) {
+ // Check it's a valid character
+ if (vim_strchr("!\"%'/:<@cfhnrs", *s) == NULL) {
+ errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
+ }
+ if (*s == 'n') { // name is always last one
+ break;
+ } else if (*s == 'r') { // skip until next ','
+ while (*++s && *s != ',') {}
+ } else if (*s == '%') {
+ // optional number
+ while (ascii_isdigit(*++s)) {}
+ } else if (*s == '!' || *s == 'h' || *s == 'c') {
+ s++; // no extra chars
+ } else { // must have a number
+ while (ascii_isdigit(*++s)) {}
+
+ if (!ascii_isdigit(*(s - 1))) {
+ if (errbuf != NULL) {
+ vim_snprintf(errbuf, errbuflen,
+ _("E526: Missing number after <%s>"),
+ transchar_byte(*(s - 1)));
+ errmsg = errbuf;
+ } else {
+ errmsg = "";
+ }
+ break;
+ }
+ }
+ if (*s == ',') {
+ s++;
+ } else if (*s) {
+ if (errbuf != NULL) {
+ errmsg = N_("E527: Missing comma");
+ } else {
+ errmsg = "";
+ }
+ break;
+ }
+ }
+ if (*p_shada && errmsg == NULL && get_shada_parameter('\'') < 0) {
+ errmsg = N_("E528: Must specify a ' value");
+ }
+ } else if (gvarp == &p_sbr) { // 'showbreak'
+ for (s = *varp; *s;) {
+ if (ptr2cells(s) != 1) {
+ errmsg = e_showbreak_contains_unprintable_or_wide_character;
+ }
+ MB_PTR_ADV(s);
+ }
+ } else if (varp == &p_guicursor) { // 'guicursor'
+ errmsg = parse_shape_opt(SHAPE_CURSOR);
+ } else if (varp == &p_popt) {
+ errmsg = parse_printoptions();
+ } else if (varp == &p_pmfn) {
+ errmsg = parse_printmbfont();
+ } else if (varp == &p_langmap) { // 'langmap'
+ langmap_set();
+ } else if (varp == &p_breakat) { // 'breakat'
+ fill_breakat_flags();
+ } else if (varp == &p_titlestring || varp == &p_iconstring) {
+ // 'titlestring' and 'iconstring'
+ int flagval = (varp == &p_titlestring) ? STL_IN_TITLE : STL_IN_ICON;
+
+ // NULL => statusline syntax
+ if (vim_strchr(*varp, '%') && check_stl_option(*varp) == NULL) {
+ stl_syntax |= flagval;
+ } else {
+ stl_syntax &= ~flagval;
+ }
+ did_set_title();
+ } else if (varp == &p_sel) { // 'selection'
+ if (*p_sel == NUL
+ || check_opt_strings(p_sel, p_sel_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_slm) { // 'selectmode'
+ if (check_opt_strings(p_slm, p_slm_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_km) { // 'keymodel'
+ if (check_opt_strings(p_km, p_km_values, true) != OK) {
+ errmsg = e_invarg;
+ } else {
+ km_stopsel = (vim_strchr(p_km, 'o') != NULL);
+ km_startsel = (vim_strchr(p_km, 'a') != NULL);
+ }
+ } else if (varp == &p_mousem) { // 'mousemodel'
+ if (check_opt_strings(p_mousem, p_mousem_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_mousescroll) { // 'mousescroll'
+ errmsg = check_mousescroll(p_mousescroll);
+ } else if (varp == &p_swb) { // 'switchbuf'
+ if (opt_strings_flags(p_swb, p_swb_values, &swb_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_debug) { // 'debug'
+ if (check_opt_strings(p_debug, p_debug_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_dy) { // 'display'
+ if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
+ errmsg = e_invarg;
+ } else {
+ (void)init_chartab();
+ msg_grid_validate();
+ }
+ } else if (varp == &p_ead) { // 'eadirection'
+ if (check_opt_strings(p_ead, p_ead_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_cb) { // 'clipboard'
+ if (opt_strings_flags(p_cb, p_cb_values, &cb_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &(curwin->w_s->b_p_spl) // 'spell'
+ || varp == &(curwin->w_s->b_p_spf)) {
+ // When 'spelllang' or 'spellfile' is set and there is a window for this
+ // buffer in which 'spell' is set load the wordlists.
+ const bool is_spellfile = varp == &(curwin->w_s->b_p_spf);
+
+ if ((is_spellfile && !valid_spellfile(*varp))
+ || (!is_spellfile && !valid_spelllang(*varp))) {
+ errmsg = e_invarg;
+ } else {
+ errmsg = did_set_spell_option(is_spellfile);
+ }
+ } else if (varp == &(curwin->w_s->b_p_spc)) {
+ // When 'spellcapcheck' is set compile the regexp program.
+ errmsg = compile_cap_prog(curwin->w_s);
+ } else if (varp == &(curwin->w_s->b_p_spo)) { // 'spelloptions'
+ if (**varp != NUL && STRCMP("camel", *varp) != 0) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_sps) { // 'spellsuggest'
+ if (spell_check_sps() != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_msm) { // 'mkspellmem'
+ if (spell_check_msm() != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_bh) {
+ // When 'bufhidden' is set, check for valid value.
+ if (check_opt_strings(curbuf->b_p_bh, p_bufhidden_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_bt) {
+ // When 'buftype' is set, check for valid value.
+ if ((curbuf->terminal && curbuf->b_p_bt[0] != 't')
+ || (!curbuf->terminal && curbuf->b_p_bt[0] == 't')
+ || check_opt_strings(curbuf->b_p_bt, p_buftype_values, false) != OK) {
+ errmsg = e_invarg;
+ } else {
+ if (curwin->w_status_height || global_stl_height()) {
+ curwin->w_redr_status = true;
+ redraw_later(curwin, UPD_VALID);
+ }
+ curbuf->b_help = (curbuf->b_p_bt[0] == 'h');
+ redraw_titles();
+ }
+ } else if (gvarp == &p_stl || gvarp == &p_wbr || varp == &p_tal
+ || varp == &p_ruf) {
+ // 'statusline', 'winbar', 'tabline' or 'rulerformat'
+ int wid;
+
+ if (varp == &p_ruf) { // reset ru_wid first
+ ru_wid = 0;
+ }
+ s = *varp;
+ if (varp == &p_ruf && *s == '%') {
+ // set ru_wid if 'ruf' starts with "%99("
+ if (*++s == '-') { // ignore a '-'
+ s++;
+ }
+ wid = getdigits_int(&s, true, 0);
+ if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
+ ru_wid = wid;
+ } else {
+ errmsg = check_stl_option(p_ruf);
+ }
+ } else if (varp == &p_ruf || s[0] != '%' || s[1] != '!') {
+ // check 'statusline', 'winbar' or 'tabline' only if it doesn't start with "%!"
+ errmsg = check_stl_option(s);
+ }
+ if (varp == &p_ruf && errmsg == NULL) {
+ comp_col();
+ }
+ // add / remove window bars for 'winbar'
+ if (gvarp == &p_wbr) {
+ set_winbar(true);
+ }
+ } else if (gvarp == &p_cpt) {
+ // check if it is a valid value for 'complete' -- Acevedo
+ for (s = *varp; *s;) {
+ while (*s == ',' || *s == ' ') {
+ s++;
+ }
+ if (!*s) {
+ break;
+ }
+ if (vim_strchr(".wbuksid]tU", *s) == NULL) {
+ errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
+ }
+ if (*++s != NUL && *s != ',' && *s != ' ') {
+ if (s[-1] == 'k' || s[-1] == 's') {
+ // skip optional filename after 'k' and 's'
+ while (*s && *s != ',' && *s != ' ') {
+ if (*s == '\\' && s[1] != NUL) {
+ s++;
+ }
+ s++;
+ }
+ } else {
+ if (errbuf != NULL) {
+ vim_snprintf(errbuf, errbuflen,
+ _("E535: Illegal character after <%c>"),
+ *--s);
+ errmsg = errbuf;
+ } else {
+ errmsg = "";
+ }
+ break;
+ }
+ }
+ }
+ } else if (varp == &p_cot) { // 'completeopt'
+ if (check_opt_strings(p_cot, p_cot_values, true) != OK) {
+ errmsg = e_invarg;
+ } else {
+ completeopt_was_set();
+ }
+#ifdef BACKSLASH_IN_FILENAME
+ } else if (gvarp == &p_csl) { // 'completeslash'
+ if (check_opt_strings(p_csl, p_csl_values, false) != OK
+ || check_opt_strings(curbuf->b_p_csl, p_csl_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+#endif
+ } else if (varp == &curwin->w_p_scl) { // 'signcolumn'
+ if (check_signcolumn(*varp) != OK) {
+ errmsg = e_invarg;
+ }
+ // When changing the 'signcolumn' to or from 'number', recompute the
+ // width of the number column if 'number' or 'relativenumber' is set.
+ if (((*oldval == 'n' && *(oldval + 1) == 'u')
+ || (*curwin->w_p_scl == 'n' && *(curwin->w_p_scl + 1) == 'u'))
+ && (curwin->w_p_nu || curwin->w_p_rnu)) {
+ curwin->w_nrwidth_line_count = 0;
+ }
+ } else if (varp == &curwin->w_p_fdc
+ || varp == &curwin->w_allbuf_opt.wo_fdc) {
+ // 'foldcolumn'
+ if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_pt) {
+ // 'pastetoggle': translate key codes like in a mapping
+ if (*p_pt) {
+ p = NULL;
+ (void)replace_termcodes(p_pt,
+ STRLEN(p_pt),
+ &p, REPTERM_FROM_PART | REPTERM_DO_LT, NULL,
+ CPO_TO_CPO_FLAGS);
+ if (p != NULL) {
+ free_string_option(p_pt);
+ p_pt = p;
+ }
+ }
+ } else if (varp == &p_bs) { // 'backspace'
+ if (ascii_isdigit(*p_bs)) {
+ if (*p_bs > '3' || p_bs[1] != NUL) {
+ errmsg = e_invarg;
+ }
+ } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_bo) {
+ if (opt_strings_flags(p_bo, p_bo_values, &bo_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_tc) { // 'tagcase'
+ unsigned int *flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ p = curbuf->b_p_tc;
+ flags = &curbuf->b_tc_flags;
+ } else {
+ p = p_tc;
+ flags = &tc_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *p == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else if (*p == NUL
+ || opt_strings_flags(p, p_tc_values, flags, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_cmp) { // 'casemap'
+ if (opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_dip) { // 'diffopt'
+ if (diffopt_changed() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fdm) { // 'foldmethod'
+ if (check_opt_strings(*varp, p_fdm_values, false) != OK
+ || *curwin->w_p_fdm == NUL) {
+ errmsg = e_invarg;
+ } else {
+ foldUpdateAll(curwin);
+ if (foldmethodIsDiff(curwin)) {
+ newFoldLevel();
+ }
+ }
+ } else if (varp == &curwin->w_p_fde) { // 'foldexpr'
+ if (foldmethodIsExpr(curwin)) {
+ foldUpdateAll(curwin);
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fmr) { // 'foldmarker'
+ p = vim_strchr(*varp, ',');
+ if (p == NULL) {
+ errmsg = N_("E536: comma required");
+ } else if (p == *varp || p[1] == NUL) {
+ errmsg = e_invarg;
+ } else if (foldmethodIsMarker(curwin)) {
+ foldUpdateAll(curwin);
+ }
+ } else if (gvarp == &p_cms) { // 'commentstring'
+ if (**varp != NUL && strstr(*varp, "%s") == NULL) {
+ errmsg = N_("E537: 'commentstring' must be empty or contain %s");
+ }
+ } else if (varp == &p_fdo) { // 'foldopen'
+ if (opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_fcl) { // 'foldclose'
+ if (check_opt_strings(p_fcl, p_fcl_values, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &curwin->w_allbuf_opt.wo_fdi) { // 'foldignore'
+ if (foldmethodIsIndent(curwin)) {
+ foldUpdateAll(curwin);
+ }
+ } else if (gvarp == &p_ve) { // 'virtualedit'
+ char *ve = p_ve;
+ unsigned int *flags = &ve_flags;
+
+ if (opt_flags & OPT_LOCAL) {
+ ve = curwin->w_p_ve;
+ flags = &curwin->w_ve_flags;
+ }
+
+ if ((opt_flags & OPT_LOCAL) && *ve == NUL) {
+ // make the local value empty: use the global value
+ *flags = 0;
+ } else {
+ if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
+ errmsg = e_invarg;
+ } else if (STRCMP(p_ve, oldval) != 0) {
+ // Recompute cursor position in case the new 've' setting
+ // changes something.
+ validate_virtcol();
+ coladvance(curwin->w_virtcol);
+ }
+ }
+ } else if (varp == &p_csqf) {
+ if (p_csqf != NULL) {
+ p = p_csqf;
+ while (*p != NUL) {
+ if (vim_strchr(CSQF_CMDS, *p) == NULL
+ || p[1] == NUL
+ || vim_strchr(CSQF_FLAGS, p[1]) == NULL
+ || (p[2] != NUL && p[2] != ',')) {
+ errmsg = e_invarg;
+ break;
+ } else if (p[2] == NUL) {
+ break;
+ } else {
+ p += 3;
+ }
+ }
+ }
+ } else if (gvarp == &p_cino) { // 'cinoptions'
+ // TODO(vim): recognize errors
+ parse_cino(curbuf);
+ } else if (varp == &p_icm) { // 'inccommand'
+ if (check_opt_strings(p_icm, p_icm_values, false) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (gvarp == &p_ft) {
+ if (!valid_filetype(*varp)) {
+ errmsg = e_invarg;
+ } else {
+ value_changed = STRCMP(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
+ }
+ } else if (gvarp == &p_syn) {
+ if (!valid_filetype(*varp)) {
+ errmsg = e_invarg;
+ } else {
+ value_changed = STRCMP(oldval, *varp) != 0;
+
+ // Since we check the value, there is no need to set P_INSECURE,
+ // even when the value comes from a modeline.
+ *value_checked = true;
+ }
+ } else if (varp == &curwin->w_p_winhl) {
+ if (!parse_winhl_opt(curwin)) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_tpf) {
+ if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &(curbuf->b_p_vsts)) { // 'varsofttabstop'
+ char *cp;
+
+ if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
+ XFREE_CLEAR(curbuf->b_p_vsts_array);
+ } else {
+ for (cp = *varp; *cp; cp++) {
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
+ continue;
+ }
+ errmsg = e_invarg;
+ break;
+ }
+ if (errmsg == NULL) {
+ long *oldarray = curbuf->b_p_vsts_array;
+ if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
+ xfree(oldarray);
+ } else {
+ errmsg = e_invarg;
+ }
+ }
+ }
+ } else if (varp == &(curbuf->b_p_vts)) { // 'vartabstop'
+ char *cp;
+
+ if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
+ XFREE_CLEAR(curbuf->b_p_vts_array);
+ } else {
+ for (cp = *varp; *cp; cp++) {
+ if (ascii_isdigit(*cp)) {
+ continue;
+ }
+ if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
+ continue;
+ }
+ errmsg = e_invarg;
+ break;
+ }
+ if (errmsg == NULL) {
+ long *oldarray = curbuf->b_p_vts_array;
+ if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
+ xfree(oldarray);
+ if (foldmethodIsIndent(curwin)) {
+ foldUpdateAll(curwin);
+ }
+ } else {
+ errmsg = e_invarg;
+ }
+ }
+ }
+ } else if (varp == &p_opfunc) { // 'operatorfunc'
+ if (set_operatorfunc_option() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else if (varp == &p_qftf) { // 'quickfixtextfunc'
+ if (qf_process_qftf_option() == FAIL) {
+ errmsg = e_invarg;
+ }
+ } else {
+ // Options that are a list of flags.
+ p = NULL;
+ if (varp == &p_ww) { // 'whichwrap'
+ p = WW_ALL;
+ }
+ if (varp == &p_shm) { // 'shortmess'
+ p = SHM_ALL;
+ } else if (varp == &(p_cpo)) { // 'cpoptions'
+ p = CPO_VI;
+ } else if (varp == &(curbuf->b_p_fo)) { // 'formatoptions'
+ p = FO_ALL;
+ } else if (varp == &curwin->w_p_cocu) { // 'concealcursor'
+ p = COCU_ALL;
+ } else if (varp == &p_mouse) { // 'mouse'
+ p = MOUSE_ALL;
+ }
+ if (p != NULL) {
+ for (s = *varp; *s; s++) {
+ if (vim_strchr(p, *s) == NULL) {
+ errmsg = illegal_char(errbuf, errbuflen, *s);
+ break;
+ }
+ }
+ }
+ }
+
+ // If error detected, restore the previous value.
+ if (errmsg != NULL) {
+ free_string_option(*varp);
+ *varp = oldval;
+ // When resetting some values, need to act on it.
+ if (did_chartab) {
+ (void)init_chartab();
+ }
+ } else {
+ // Remember where the option was set.
+ set_option_sctx_idx(opt_idx, opt_flags, current_sctx);
+ // Free string options that are in allocated memory.
+ // Use "free_oldval", because recursiveness may change the flags under
+ // our fingers (esp. init_highlight()).
+ if (free_oldval) {
+ free_string_option(oldval);
+ }
+ set_option_flag(opt_idx, P_ALLOCED);
+
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ && is_global_local_option(opt_idx)) {
+ // global option with local value set to use global value; free
+ // the local value and make it empty
+ p = get_option_varp_scope(opt_idx, OPT_LOCAL);
+ free_string_option(*(char **)p);
+ *(char **)p = empty_option;
+ } else if (!(opt_flags & OPT_LOCAL) && opt_flags != OPT_GLOBAL) {
+ // May set global value for local option.
+ set_string_option_global(opt_idx, varp);
+ }
+
+ // Trigger the autocommand only after setting the flags.
+ // When 'syntax' is set, load the syntax of that name
+ if (varp == &(curbuf->b_p_syn)) {
+ static int syn_recursive = 0;
+
+ syn_recursive++;
+ // Only pass true for "force" when the value changed or not used
+ // recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_SYNTAX, curbuf->b_p_syn, curbuf->b_fname,
+ value_changed || syn_recursive == 1, curbuf);
+ curbuf->b_flags |= BF_SYN_SET;
+ syn_recursive--;
+ } else if (varp == &(curbuf->b_p_ft)) {
+ // 'filetype' is set, trigger the FileType autocommand
+ // Skip this when called from a modeline and the filetype was
+ // already set to this value.
+ if (!(opt_flags & OPT_MODELINE) || value_changed) {
+ static int ft_recursive = 0;
+ int secure_save = secure;
+
+ // Reset the secure flag, since the value of 'filetype' has
+ // been checked to be safe.
+ secure = 0;
+
+ ft_recursive++;
+ did_filetype = true;
+ // Only pass true for "force" when the value changed or not
+ // used recursively, to avoid endless recurrence.
+ apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname,
+ value_changed || ft_recursive == 1, curbuf);
+ ft_recursive--;
+ // Just in case the old "curbuf" is now invalid
+ if (varp != &(curbuf->b_p_ft)) {
+ varp = NULL;
+ }
+ secure = secure_save;
+ }
+ }
+ if (varp == &(curwin->w_s->b_p_spl)) {
+ char fname[200];
+ char *q = curwin->w_s->b_p_spl;
+
+ // Skip the first name if it is "cjk".
+ if (STRNCMP(q, "cjk,", 4) == 0) {
+ q += 4;
+ }
+
+ // Source the spell/LANG.vim in 'runtimepath'.
+ // They could set 'spellcapcheck' depending on the language.
+ // Use the first name in 'spelllang' up to '_region' or
+ // '.encoding'.
+ for (p = q; *p != NUL; p++) {
+ if (!ASCII_ISALNUM(*p) && *p != '-') {
+ break;
+ }
+ }
+ if (p > q) {
+ vim_snprintf(fname, sizeof(fname), "spell/%.*s.vim", (int)(p - q), q);
+ source_runtime(fname, DIP_ALL);
+ }
+ }
+ }
+
+ if (varp == &p_mouse) {
+ setmouse(); // in case 'mouse' changed
+ }
+
+ if (curwin->w_curswant != MAXCOL
+ && (get_option_flags(opt_idx) & (P_CURSWANT | P_RALL)) != 0) {
+ curwin->w_set_curswant = true;
+ }
+
+ check_redraw(get_option_flags(opt_idx));
+
+ return errmsg;
+}
+
+/// Check an option that can be a range of string values.
+///
+/// @param list when true: accept a list of values
+///
+/// @return OK for correct value, FAIL otherwise. Empty is always OK.
+static int check_opt_strings(char *val, char **values, int list)
+{
+ return opt_strings_flags(val, values, NULL, list);
+}
+
+/// Handle an option that can be a range of string values.
+/// Set a flag in "*flagp" for each string present.
+///
+/// @param val new value
+/// @param values array of valid string values
+/// @param list when true: accept a list of values
+///
+/// @return OK for correct value, FAIL otherwise. Empty is always OK.
+static int opt_strings_flags(char *val, char **values, unsigned *flagp, bool list)
+{
+ unsigned int new_flags = 0;
+
+ while (*val) {
+ for (unsigned int i = 0;; i++) {
+ if (values[i] == NULL) { // val not found in values[]
+ return FAIL;
+ }
+
+ size_t len = STRLEN(values[i]);
+ if (STRNCMP(values[i], val, len) == 0
+ && ((list && val[len] == ',') || val[len] == NUL)) {
+ val += len + (val[len] == ',');
+ assert(i < sizeof(1U) * 8);
+ new_flags |= (1U << i);
+ break; // check next item in val list
+ }
+ }
+ }
+ if (flagp != NULL) {
+ *flagp = new_flags;
+ }
+
+ return OK;
+}
+
+/// @return OK if "p" is a valid fileformat name, FAIL otherwise.
+int check_ff_value(char *p)
+{
+ return check_opt_strings(p, p_ff_values, false);
+}
diff --git a/src/nvim/optionstr.h b/src/nvim/optionstr.h
new file mode 100644
index 0000000000..ac8d90e10e
--- /dev/null
+++ b/src/nvim/optionstr.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_OPTIONSTR_H
+#define NVIM_OPTIONSTR_H
+
+#include "nvim/buffer_defs.h" // for buf_T, win_T
+#include "nvim/option_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "optionstr.h.generated.h"
+#endif
+#endif // NVIM_OPTIONSTR_H
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index eaa56ffe63..c940c86675 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -8,9 +8,8 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
-#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
#include "nvim/macros.h"
#include "nvim/map.h"
#include "nvim/memory.h"
@@ -554,9 +553,9 @@ char_u *expand_env_save_opt(char_u *src, bool one)
/// @param src Input string e.g. "$HOME/vim.hlp"
/// @param dst[out] Where to put the result
/// @param dstlen Maximum length of the result
-void expand_env(char_u *src, char_u *dst, int dstlen)
+void expand_env(char *src, char *dst, int dstlen)
{
- expand_env_esc(src, dst, dstlen, false, false, NULL);
+ expand_env_esc((char_u *)src, (char_u *)dst, dstlen, false, false, NULL);
}
/// Expand environment variable with path name and escaping.
@@ -580,18 +579,18 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
int prefix_len = (prefix == NULL) ? 0 : (int)STRLEN(prefix);
- char_u *src = (char_u *)skipwhite((char *)srcp);
+ char *src = skipwhite((char *)srcp);
dstlen--; // leave one char space for "\,"
while (*src && dstlen > 0) {
// Skip over `=expr`.
if (src[0] == '`' && src[1] == '=') {
- var = src;
+ var = (char_u *)src;
src += 2;
- (void)skip_expr((char **)&src);
+ (void)skip_expr(&src);
if (*src == '`') {
src++;
}
- size_t len = (size_t)(src - var);
+ size_t len = (size_t)(src - (char *)var);
if (len > (size_t)dstlen) {
len = (size_t)dstlen;
}
@@ -608,7 +607,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// The variable name is copied into dst temporarily, because it may
// be a string in read-only memory and a NUL needs to be appended.
if (*src != '~') { // environment var
- tail = src + 1;
+ tail = (char_u *)src + 1;
var = dst;
int c = dstlen - 1;
@@ -646,11 +645,11 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
|| vim_ispathsep(src[1])
|| vim_strchr(" ,\t\n", src[1]) != NULL) {
var = (char_u *)homedir;
- tail = src + 1;
+ tail = (char_u *)src + 1;
} else { // user directory
#if defined(UNIX)
// Copy ~user to dst[], so we can put a NUL after it.
- tail = src;
+ tail = (char_u *)src;
var = dst;
int c = dstlen - 1;
while (c-- > 0
@@ -723,7 +722,7 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
tail++;
}
dst += c;
- src = tail;
+ src = (char *)tail;
copy_char = false;
}
if (mustfree) {
@@ -737,17 +736,17 @@ void expand_env_esc(char_u *restrict srcp, char_u *restrict dst, int dstlen, boo
// ":edit foo ~ foo".
at_start = false;
if (src[0] == '\\' && src[1] != NUL) {
- *dst++ = *src++;
+ *dst++ = (char_u)(*src++);
dstlen--;
} else if ((src[0] == ' ' || src[0] == ',') && !one) {
at_start = true;
}
if (dstlen > 0) {
- *dst++ = *src++;
+ *dst++ = (char_u)(*src++);
dstlen--;
if (prefix != NULL
- && src - prefix_len >= srcp
+ && src - prefix_len >= (char *)srcp
&& STRNCMP(src - prefix_len, prefix, prefix_len) == 0) {
at_start = true;
}
@@ -766,12 +765,12 @@ static char *vim_version_dir(const char *vimdir)
return NULL;
}
char *p = concat_fnames(vimdir, VIM_VERSION_NODOT, true);
- if (os_isdir((char_u *)p)) {
+ if (os_isdir(p)) {
return p;
}
xfree(p);
p = concat_fnames(vimdir, RUNTIME_DIRNAME, true);
- if (os_isdir((char_u *)p)) {
+ if (os_isdir(p)) {
return p;
}
xfree(p);
@@ -938,8 +937,8 @@ char *vim_getenv(const char *name)
// - the directory name from 'helpfile' (unless it contains '$')
// - the executable name from argv[0]
if (vim_path == NULL) {
- if (p_hf != NULL && vim_strchr((char *)p_hf, '$') == NULL) {
- vim_path = (char *)p_hf;
+ if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL) {
+ vim_path = p_hf;
}
char exe_name[MAXPATHL];
@@ -958,7 +957,7 @@ char *vim_getenv(const char *name)
char *vim_path_end = path_tail(vim_path);
// remove "doc/" from 'helpfile', if present
- if (vim_path == (char *)p_hf) {
+ if (vim_path == p_hf) {
vim_path_end = remove_tail(vim_path, vim_path_end, "doc");
}
@@ -977,7 +976,7 @@ char *vim_getenv(const char *name)
assert(vim_path_end >= vim_path);
vim_path = xstrndup(vim_path, (size_t)(vim_path_end - vim_path));
- if (!os_isdir((char_u *)vim_path)) {
+ if (!os_isdir(vim_path)) {
xfree(vim_path);
vim_path = NULL;
}
@@ -1069,9 +1068,8 @@ size_t home_replace(const buf_T *const buf, const char *src, char *const dst, si
must_free = true;
size_t usedlen = 0;
size_t flen = strlen(homedir_env_mod);
- char_u *fbuf = NULL;
- (void)modify_fname(":p", false, &usedlen,
- &homedir_env_mod, (char **)&fbuf, &flen);
+ char *fbuf = NULL;
+ (void)modify_fname(":p", false, &usedlen, &homedir_env_mod, &fbuf, &flen);
flen = strlen(homedir_env_mod);
assert(homedir_env_mod != homedir_env);
if (vim_ispathsep(homedir_env_mod[flen - 1])) {
@@ -1158,15 +1156,12 @@ char *home_replace_save(buf_T *buf, const char *src)
/// Function given to ExpandGeneric() to obtain an environment variable name.
char *get_env_name(expand_T *xp, int idx)
{
-#define ENVNAMELEN 100
- // this static buffer is needed to avoid a memory leak in ExpandGeneric
- static char_u name[ENVNAMELEN];
assert(idx >= 0);
char *envname = os_getenvname_at_index((size_t)idx);
if (envname) {
- STRLCPY(name, envname, ENVNAMELEN);
+ STRLCPY(xp->xp_buf, envname, EXPAND_BUF_LEN);
xfree(envname);
- return (char *)name;
+ return xp->xp_buf;
}
return NULL;
}
@@ -1231,3 +1226,29 @@ bool os_shell_is_cmdexe(const char *sh)
}
return striequal("cmd.exe", path_tail(sh));
}
+
+/// Removes environment variable "name" and take care of side effects.
+void vim_unsetenv_ext(const char *var)
+{
+ os_unsetenv(var);
+
+ // "homedir" is not cleared, keep using the old value until $HOME is set.
+ if (STRICMP(var, "VIM") == 0) {
+ didset_vim = false;
+ } else if (STRICMP(var, "VIMRUNTIME") == 0) {
+ didset_vimruntime = false;
+ }
+}
+
+/// Set environment variable "name" and take care of side effects.
+void vim_setenv_ext(const char *name, const char *val)
+{
+ os_setenv(name, val, 1);
+ if (STRICMP(name, "HOME") == 0) {
+ init_homedir();
+ } else if (didset_vim && STRICMP(name, "VIM") == 0) {
+ didset_vim = false;
+ } else if (didset_vimruntime && STRICMP(name, "VIMRUNTIME") == 0) {
+ didset_vimruntime = false;
+ }
+}
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 901a1bc5a6..c0d5616666 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -129,10 +129,10 @@ bool os_isrealdir(const char *name)
/// Check if the given path exists and is a directory.
///
/// @return `true` if `name` is a directory.
-bool os_isdir(const char_u *name)
+bool os_isdir(const char *name)
FUNC_ATTR_NONNULL_ALL
{
- int32_t mode = os_getperm((const char *)name);
+ int32_t mode = os_getperm(name);
if (mode < 0) {
return false;
}
@@ -144,25 +144,6 @@ bool os_isdir(const char_u *name)
return true;
}
-/// Check if the given path is a directory and is executable.
-/// Gives the same results as `os_isdir()` on Windows.
-///
-/// @return `true` if `name` is a directory and executable.
-bool os_isdir_executable(const char *name)
- FUNC_ATTR_NONNULL_ALL
-{
- int32_t mode = os_getperm(name);
- if (mode < 0) {
- return false;
- }
-
-#ifdef WIN32
- return (S_ISDIR(mode));
-#else
- return (S_ISDIR(mode) && (S_IXUSR & mode));
-#endif
-}
-
/// Check what `name` is:
/// @return NODE_NORMAL: file or directory (or doesn't exist)
/// NODE_WRITABLE: writable device, socket, fifo, etc.
@@ -339,7 +320,7 @@ static bool is_executable_ext(const char *name, char **abspath)
const char *ext_end = ext;
size_t ext_len =
- copy_option_part((char_u **)&ext_end, (char_u *)buf_end,
+ copy_option_part(&ext_end, (char_u *)buf_end,
sizeof(os_buf) - (size_t)(buf_end - os_buf), ENV_SEPSTR);
if (ext_len != 0) {
bool in_pathext = nameext_len == ext_len
@@ -884,7 +865,7 @@ int os_file_is_writable(const char *name)
int r;
RUN_UV_FS_FUNC(r, uv_fs_access, name, W_OK, NULL);
if (r == 0) {
- return os_isdir((char_u *)name) ? 2 : 1;
+ return os_isdir(name) ? 2 : 1;
}
return 0;
}
@@ -930,11 +911,11 @@ int os_mkdir_recurse(const char *const dir, int32_t mode, char **const failed_di
// We're done when it's "/" or "c:/".
const size_t dirlen = strlen(dir);
char *const curdir = xmemdupz(dir, dirlen);
- char *const past_head = (char *)get_past_head((char_u *)curdir);
+ char *const past_head = get_past_head(curdir);
char *e = curdir + dirlen;
const char *const real_end = e;
const char past_head_save = *past_head;
- while (!os_isdir((char_u *)curdir)) {
+ while (!os_isdir(curdir)) {
e = path_tail_with_sep(curdir);
if (e <= past_head) {
*past_head = NUL;
@@ -1051,7 +1032,7 @@ int os_remove(const char *path)
bool os_fileinfo(const char *path, FileInfo *file_info)
FUNC_ATTR_NONNULL_ARG(2)
{
- memset(file_info, 0, sizeof(*file_info));
+ CLEAR_POINTER(file_info);
return os_stat(path, &(file_info->stat)) == kLibuvSuccess;
}
@@ -1063,7 +1044,7 @@ bool os_fileinfo(const char *path, FileInfo *file_info)
bool os_fileinfo_link(const char *path, FileInfo *file_info)
FUNC_ATTR_NONNULL_ARG(2)
{
- memset(file_info, 0, sizeof(*file_info));
+ CLEAR_POINTER(file_info);
if (path == NULL) {
return false;
}
@@ -1087,7 +1068,7 @@ bool os_fileinfo_fd(int file_descriptor, FileInfo *file_info)
FUNC_ATTR_NONNULL_ALL
{
uv_fs_t request;
- memset(file_info, 0, sizeof(*file_info));
+ CLEAR_POINTER(file_info);
fs_loop_lock();
bool ok = uv_fs_fstat(&fs_loop,
&request,
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index c47a891c18..bfe6d59dc6 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -8,10 +8,9 @@
#include "nvim/api/private/defs.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
-#include "nvim/ex_cmds2.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/keycodes.h"
#include "nvim/main.h"
@@ -19,6 +18,7 @@
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/os/input.h"
+#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"
diff --git a/src/nvim/os/pty_conpty_win.h b/src/nvim/os/pty_conpty_win.h
index c243db4fa5..15e7c3da0c 100644
--- a/src/nvim/os/pty_conpty_win.h
+++ b/src/nvim/os/pty_conpty_win.h
@@ -1,6 +1,9 @@
#ifndef NVIM_OS_PTY_CONPTY_WIN_H
#define NVIM_OS_PTY_CONPTY_WIN_H
+#include "nvim/lib/kvec.h"
+#include "nvim/os/input.h"
+
#ifndef HPCON
# define HPCON VOID *
#endif
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 8f2018c1f4..461a79c37b 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -25,6 +25,7 @@
#include "nvim/os/shell.h"
#include "nvim/os/signal.h"
#include "nvim/path.h"
+#include "nvim/profile.h"
#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
@@ -54,7 +55,7 @@ static void save_patterns(int num_pat, char **pat, int *num_file, char ***file)
char_u *s = vim_strsave((char_u *)pat[i]);
// Be compatible with expand_filename(): halve the number of
// backslashes.
- backslash_halve(s);
+ backslash_halve((char *)s);
(*file)[i] = (char *)s;
}
*num_file = num_pat;
@@ -73,7 +74,7 @@ static bool have_wildcard(int num, char **file)
static bool have_dollars(int num, char **file)
{
for (int i = 0; i < num; i++) {
- if (vim_strchr((char *)file[i], '$') != NULL) {
+ if (vim_strchr(file[i], '$') != NULL) {
return true;
}
}
@@ -248,10 +249,16 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
}
STRCAT(command, ">");
} else {
- if (flags & EW_NOTFOUND) {
- STRCPY(command, "set nonomatch; ");
- } else {
- STRCPY(command, "unset nonomatch; ");
+ STRCPY(command, "");
+ if (shell_style == STYLE_GLOB) {
+ // Assume the nonomatch option is valid only for csh like shells,
+ // otherwise, this may set the positional parameters for the shell,
+ // e.g. "$*".
+ if (flags & EW_NOTFOUND) {
+ STRCAT(command, "set nonomatch; ");
+ } else {
+ STRCAT(command, "unset nonomatch; ");
+ }
}
if (shell_style == STYLE_GLOB) {
STRCAT(command, "glob >");
@@ -501,7 +508,7 @@ int os_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, in
}
// check if this entry should be included
- dir = (os_isdir((char_u *)(*file)[i]));
+ dir = (os_isdir((*file)[i]));
if ((dir && !(flags & EW_DIR)) || (!dir && !(flags & EW_FILE))) {
continue;
}
@@ -904,7 +911,7 @@ static int do_os_system(char **argv, const char *input, size_t len, char **outpu
out_data_ring(NULL, SIZE_MAX);
}
if (forward_output) {
- // caller should decide if wait_return is invoked
+ // caller should decide if wait_return() is invoked
no_wait_return++;
msg_end();
no_wait_return--;
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index 581f025a0f..e592570966 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -9,10 +9,10 @@
#endif
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/eval.h"
#include "nvim/event/loop.h"
#include "nvim/event/signal.h"
-#include "nvim/fileio.h"
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/main.h"
diff --git a/src/nvim/path.c b/src/nvim/path.c
index a0b09bcec2..a5cec6772f 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -8,9 +8,9 @@
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
@@ -33,7 +33,7 @@
#include "nvim/vim.h"
#include "nvim/window.h"
-#define URL_SLASH 1 // path_is_url() has found "://"
+#define URL_SLASH 1 // path_is_url() has found ":/"
#define URL_BACKSLASH 2 // path_is_url() has found ":\\"
#ifdef gen_expand_wildcards
@@ -62,7 +62,7 @@ FileComparison path_full_compare(char *const s1, char *const s2, const bool chec
FileID file_id_1, file_id_2;
if (expandenv) {
- expand_env((char_u *)s1, (char_u *)exp1, MAXPATHL);
+ expand_env(s1, exp1, MAXPATHL);
} else {
STRLCPY(exp1, s1, MAXPATHL);
}
@@ -104,7 +104,7 @@ char *path_tail(const char *fname)
return "";
}
- const char *tail = (char *)get_past_head((char_u *)fname);
+ const char *tail = get_past_head(fname);
const char *p = tail;
// Find last part of path.
while (*p != NUL) {
@@ -130,7 +130,7 @@ char *path_tail_with_sep(char *fname)
assert(fname != NULL);
// Don't remove the '/' from "c:/file".
- char *past_head = (char *)get_past_head((char_u *)fname);
+ char *past_head = get_past_head(fname);
char *tail = path_tail(fname);
while (tail > past_head && after_pathsep(fname, tail)) {
tail--;
@@ -150,7 +150,7 @@ char *path_tail_with_sep(char *fname)
const char_u *invocation_path_tail(const char_u *invocation, size_t *len)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1)
{
- const char_u *tail = get_past_head((char_u *)invocation);
+ const char_u *tail = (char_u *)get_past_head((char *)invocation);
const char_u *p = tail;
while (*p != NUL && *p != ' ') {
bool was_sep = vim_ispathsep_nocolon(*p);
@@ -215,28 +215,26 @@ bool is_path_head(const char_u *path)
/// Get a pointer to one character past the head of a path name.
/// Unix: after "/"; Win: after "c:\"
/// If there is no head, path is returned.
-char_u *get_past_head(const char_u *path)
+char *get_past_head(const char *path)
{
- const char_u *retval = path;
+ const char *retval = path;
#ifdef WIN32
// May skip "c:"
- if (is_path_head(path)) {
+ if (is_path_head((char_u *)path)) {
retval = path + 2;
}
#endif
while (vim_ispathsep(*retval)) {
- ++retval;
+ retval++;
}
- return (char_u *)retval;
+ return (char *)retval;
}
-/*
- * Return TRUE if 'c' is a path separator.
- * Note that for MS-Windows this includes the colon.
- */
+/// Return true if 'c' is a path separator.
+/// Note that for MS-Windows this includes the colon.
int vim_ispathsep(int c)
{
#ifdef UNIX
@@ -262,9 +260,7 @@ int vim_ispathsep_nocolon(int c)
;
}
-/*
- * return TRUE if 'c' is a path list separator.
- */
+/// return true if 'c' is a path list separator.
int vim_ispathlistsep(int c)
{
#ifdef UNIX
@@ -314,16 +310,14 @@ void shorten_dir_len(char_u *str, int trim_len)
/// Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname"
/// It's done in-place.
-void shorten_dir(char_u *str)
+void shorten_dir(char *str)
{
- shorten_dir_len(str, 1);
+ shorten_dir_len((char_u *)str, 1);
}
-/*
- * Return TRUE if the directory of "fname" exists, FALSE otherwise.
- * Also returns TRUE if there is no directory name.
- * "fname" must be writable!.
- */
+/// Return true if the directory of "fname" exists, false otherwise.
+/// Also returns true if there is no directory name.
+/// "fname" must be writable!.
bool dir_of_file_exists(char_u *fname)
{
char *p = path_tail_with_sep((char *)fname);
@@ -332,7 +326,7 @@ bool dir_of_file_exists(char_u *fname)
}
char c = *p;
*p = NUL;
- bool retval = os_isdir(fname);
+ bool retval = os_isdir((char *)fname);
*p = c;
return retval;
}
@@ -376,16 +370,16 @@ int path_fnamencmp(const char *const fname1, const char *const fname2, size_t le
const char *p1 = fname1;
const char *p2 = fname2;
while (len > 0) {
- c1 = utf_ptr2char((const char_u *)p1);
- c2 = utf_ptr2char((const char_u *)p2);
+ c1 = utf_ptr2char(p1);
+ c2 = utf_ptr2char(p2);
if ((c1 == NUL || c2 == NUL
|| (!((c1 == '/' || c1 == '\\') && (c2 == '\\' || c2 == '/'))))
&& (p_fic ? (c1 != c2 && CH_FOLD(c1) != CH_FOLD(c2)) : c1 != c2)) {
break;
}
- len -= (size_t)utfc_ptr2len((const char_u *)p1);
- p1 += utfc_ptr2len((const char_u *)p1);
- p2 += utfc_ptr2len((const char_u *)p2);
+ len -= (size_t)utfc_ptr2len(p1);
+ p1 += utfc_ptr2len(p1);
+ p2 += utfc_ptr2len(p2);
}
return p_fic ? CH_FOLD(c1) - CH_FOLD(c2) : c1 - c2;
#else
@@ -637,7 +631,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
while (*path_end != NUL) {
/* May ignore a wildcard that has a backslash before it; it will
* be removed by rem_backslash() or file_pat_to_reg_pat() below. */
- if (path_end >= path + wildoff && rem_backslash(path_end)) {
+ if (path_end >= path + wildoff && rem_backslash((char *)path_end)) {
*p++ = *path_end++;
} else if (vim_ispathsep_nocolon(*path_end)) {
if (e != NULL) {
@@ -663,16 +657,16 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// Now we have one wildcard component between "s" and "e".
/* Remove backslashes between "wildoff" and the start of the wildcard
* component. */
- for (p = buf + wildoff; p < s; ++p) {
- if (rem_backslash(p)) {
+ for (p = buf + wildoff; p < s; p++) {
+ if (rem_backslash((char *)p)) {
STRMOVE(p, p + 1);
- --e;
- --s;
+ e--;
+ s--;
}
}
// Check for "**" between "s" and "e".
- for (p = s; p < e; ++p) {
+ for (p = s; p < e; p++) {
if (p[0] == '*' && p[1] == '*') {
starstar = true;
}
@@ -695,11 +689,11 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
regmatch.rm_ic = true; // Always ignore case on Windows.
#endif
if (flags & (EW_NOERROR | EW_NOTWILD)) {
- ++emsg_silent;
+ emsg_silent++;
}
regmatch.regprog = vim_regcomp(pat, RE_MAGIC);
if (flags & (EW_NOERROR | EW_NOTWILD)) {
- --emsg_silent;
+ emsg_silent--;
}
xfree(pat);
@@ -725,7 +719,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// Find all matching entries.
char_u *name;
scandir_next_with_dots(NULL); // initialize
- while ((name = (char_u *)scandir_next_with_dots(&dir)) != NULL) {
+ while (!got_int && (name = (char_u *)scandir_next_with_dots(&dir)) != NULL) {
if ((name[0] != '.'
|| starts_with_dot
|| ((flags & EW_DODOT)
@@ -742,9 +736,9 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
* find matches. */
STRCPY(buf + len, "/**");
STRCPY(buf + len + 3, path_end);
- ++stardepth;
+ stardepth++;
(void)do_path_expand(gap, buf, len + 1, flags, true);
- --stardepth;
+ stardepth--;
}
STRCPY(buf + len, path_end);
@@ -758,7 +752,7 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
// no more wildcards, check if there is a match
// remove backslashes for the remaining components only
if (*path_end != NUL) {
- backslash_halve(buf + len + 1);
+ backslash_halve((char *)buf + len + 1);
}
// add existing file or symbolic link
if ((flags & EW_ALLLINKS) ? os_fileinfo_link((char *)buf, &file_info)
@@ -774,8 +768,10 @@ static size_t do_path_expand(garray_T *gap, const char_u *path, size_t wildoff,
xfree(buf);
vim_regfree(regmatch.regprog);
+ // When interrupted the matches probably won't be used and sorting can be
+ // slow, thus skip it.
size_t matches = (size_t)(gap->ga_len - start_len);
- if (matches > 0) {
+ if (matches > 0 && !got_int) {
qsort(((char_u **)gap->ga_data) + start_len, matches,
sizeof(char_u *), pstrcmp);
}
@@ -804,10 +800,8 @@ static int find_previous_pathsep(char_u *path, char_u **psep)
return FAIL;
}
-/*
- * Returns TRUE if "maybe_unique" is unique wrt other_paths in "gap".
- * "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
- */
+/// Returns true if "maybe_unique" is unique wrt other_paths in "gap".
+/// "maybe_unique" is the end portion of "((char_u **)gap->ga_data)[i]".
static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
{
char_u **other_paths = (char_u **)gap->ga_data;
@@ -841,7 +835,7 @@ static bool is_unique(char_u *maybe_unique, garray_T *gap, int i)
*/
static void expand_path_option(char_u *curdir, garray_T *gap)
{
- char_u *path_option = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path;
+ char_u *path_option = *curbuf->b_p_path == NUL ? p_path : (char_u *)curbuf->b_p_path;
char_u *buf = xmalloc(MAXPATHL);
while (*path_option != NUL) {
@@ -1141,16 +1135,14 @@ static int expand_in_path(garray_T *const gap, char_u *const pattern, const int
if (flags & EW_ADDSLASH) {
glob_flags |= WILD_ADD_SLASH;
}
- globpath(paths, pattern, gap, glob_flags);
+ globpath((char *)paths, (char *)pattern, gap, glob_flags);
xfree(paths);
return gap->ga_len;
}
-/*
- * Return TRUE if "p" contains what looks like an environment variable.
- * Allowing for escaping.
- */
+/// Return true if "p" contains what looks like an environment variable.
+/// Allowing for escaping.
static bool has_env_var(char_u *p)
{
for (; *p; MB_PTR_ADV(p)) {
@@ -1165,7 +1157,7 @@ static bool has_env_var(char_u *p)
#ifdef SPECIAL_WILDCHAR
-// Return TRUE if "p" contains a special wildcard character, one that Vim
+// Return true if "p" contains a special wildcard character, one that Vim
// cannot expand, requires using a shell.
static bool has_special_wildchar(char_u *p)
{
@@ -1254,7 +1246,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
*/
ga_init(&ga, (int)sizeof(char_u *), 30);
- for (int i = 0; i < num_pat; ++i) {
+ for (int i = 0; i < num_pat && !got_int; i++) {
add_pat = -1;
p = (char_u *)pat[i];
@@ -1320,7 +1312,7 @@ int gen_expand_wildcards(int num_pat, char **pat, int *num_file, char ***file, i
}
if (add_pat == -1 || (add_pat == 0 && (flags & EW_NOTFOUND))) {
- char_u *t = backslash_halve_save(p);
+ char_u *t = (char_u *)backslash_halve_save((char *)p);
/* When EW_NOTFOUND is used, always add files and dirs. Makes
* "vim c:/" work. */
@@ -1363,9 +1355,7 @@ void FreeWild(int count, char **files)
xfree(files);
}
-/*
- * Return TRUE if we can expand this backtick thing here.
- */
+/// Return true if we can expand this backtick thing here.
static int vim_backtick(char_u *p)
{
return *p == '`' && *(p + 1) != NUL && *(p + STRLEN(p) - 1) == '`';
@@ -1401,7 +1391,7 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
cmd = skipwhite(cmd); // skip over white space
p = cmd;
while (*p != NUL && *p != '\r' && *p != '\n') { // skip over entry
- ++p;
+ p++;
}
// add an entry if it is not empty
if (p > cmd) {
@@ -1409,11 +1399,11 @@ static int expand_backtick(garray_T *gap, char_u *pat, int flags)
*p = NUL;
addfile(gap, (char_u *)cmd, flags);
*p = i;
- ++cnt;
+ cnt++;
}
cmd = p;
while (*cmd != NUL && (*cmd == '\r' || *cmd == '\n')) {
- ++cmd;
+ cmd++;
}
}
@@ -1482,7 +1472,7 @@ void addfile(garray_T *gap, char_u *f, int flags)
}
#endif
- isdir = os_isdir(f);
+ isdir = os_isdir((char *)f);
if ((isdir && !(flags & EW_DIR)) || (!isdir && !(flags & EW_FILE))) {
return;
}
@@ -1656,12 +1646,12 @@ void simplify_filename(char_u *filename)
*p = NUL;
} else {
if (p > start && tail[-1] == '.') {
- --p;
+ p--;
}
STRMOVE(p, tail); // strip previous component
}
- --components;
+ components--;
}
} else if (p == start && !relative) { // leading "/.." or "/../"
STRMOVE(p, tail); // strip ".." or "../"
@@ -1682,7 +1672,7 @@ void simplify_filename(char_u *filename)
static char *eval_includeexpr(const char *const ptr, const size_t len)
{
set_vim_var_string(VV_FNAME, ptr, (ptrdiff_t)len);
- char *res = eval_to_string_safe((char *)curbuf->b_p_inex, NULL,
+ char *res = eval_to_string_safe(curbuf->b_p_inex, NULL,
was_set_insecurely(curwin, "includeexpr", OPT_LOCAL));
set_vim_var_string(VV_FNAME, NULL, 0);
return res;
@@ -1724,7 +1714,7 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
ptr = tofree;
len = STRLEN(ptr);
file_name = find_file_in_path(ptr, len, options & ~FNAME_MESS,
- TRUE, rel_fname);
+ true, rel_fname);
}
}
if (file_name == NULL && (options & FNAME_MESS)) {
@@ -1738,7 +1728,7 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
* appears several times in the path. */
while (file_name != NULL && --count > 0) {
xfree(file_name);
- file_name = find_file_in_path(ptr, len, options, FALSE, rel_fname);
+ file_name = find_file_in_path(ptr, len, options, false, rel_fname);
}
} else {
file_name = vim_strnsave(ptr, len);
@@ -1749,12 +1739,26 @@ char_u *find_file_name_in_path(char_u *ptr, size_t len, int options, long count,
return file_name;
}
-// Check if the "://" of a URL is at the pointer, return URL_SLASH.
+/// Checks for a Windows drive letter ("C:/") at the start of the path.
+///
+/// @see https://url.spec.whatwg.org/#start-with-a-windows-drive-letter
+bool path_has_drive_letter(const char *p)
+ FUNC_ATTR_NONNULL_ALL
+{
+ return strlen(p) >= 2
+ && ASCII_ISALPHA(p[0])
+ && (p[1] == ':' || p[1] == '|')
+ && (strlen(p) == 2 || ((p[2] == '/') | (p[2] == '\\') | (p[2] == '?') | (p[2] == '#')));
+}
+
+// Check if the ":/" of a URL is at the pointer, return URL_SLASH.
// Also check for ":\\", which MS Internet Explorer accepts, return
// URL_BACKSLASH.
int path_is_url(const char *p)
{
- if (strncmp(p, "://", 3) == 0) {
+ // In the spec ':' is enough to recognize a scheme
+ // https://url.spec.whatwg.org/#scheme-state
+ if (strncmp(p, ":/", 2) == 0) {
return URL_SLASH;
} else if (strncmp(p, ":\\\\", 3) == 0) {
return URL_BACKSLASH;
@@ -1779,6 +1783,10 @@ int path_with_url(const char *fname)
return 0;
}
+ if (path_has_drive_letter(fname)) {
+ return 0;
+ }
+
// check body: alpha or dash
for (p = fname + 1; (isalpha(*p) || (*p == '-')); p++) {}
@@ -1787,7 +1795,7 @@ int path_with_url(const char *fname)
return 0;
}
- // "://" or ":\\" must follow
+ // ":/" or ":\\" must follow
return path_is_url(p);
}
@@ -1800,9 +1808,7 @@ bool path_with_extension(const char *path, const char *extension)
return strcmp(last_dot + 1, extension) == 0;
}
-/*
- * Return TRUE if "name" is a full (absolute) path name or URL.
- */
+/// Return true if "name" is a full (absolute) path name or URL.
bool vim_isAbsName(char_u *name)
{
return path_with_url((char *)name) != 0 || path_is_absolute(name);
@@ -1839,7 +1845,7 @@ int vim_FullName(const char *fname, char *buf, size_t len, bool force)
return OK;
}
- int rv = path_to_absolute((char_u *)fname, (char_u *)buf, len, force);
+ int rv = path_to_absolute(fname, buf, len, force);
if (rv == FAIL) {
xstrlcpy(buf, fname, len); // something failed; use the filename
}
@@ -1898,7 +1904,7 @@ void path_fix_case(char *name)
}
// Open the directory where the file is located.
- char *slash = (char *)STRRCHR(name, '/');
+ char *slash = strrchr(name, '/');
char *tail;
Directory dir;
bool ok;
@@ -1939,21 +1945,17 @@ void path_fix_case(char *name)
os_closedir(&dir);
}
-/*
- * Return TRUE if "p" points to just after a path separator.
- * Takes care of multi-byte characters.
- * "b" must point to the start of the file name
- */
+/// Return true if "p" points to just after a path separator.
+/// Takes care of multi-byte characters.
+/// "b" must point to the start of the file name
int after_pathsep(const char *b, const char *p)
{
return p > b && vim_ispathsep(p[-1])
&& utf_head_off((char_u *)b, (char_u *)p - 1) == 0;
}
-/*
- * Return TRUE if file names "f1" and "f2" are in the same directory.
- * "f1" may be a short name, "f2" must be a full path.
- */
+/// Return true if file names "f1" and "f2" are in the same directory.
+/// "f1" may be a short name, "f2" must be a full path.
bool same_directory(char_u *f1, char_u *f2)
{
char ffname[MAXPATHL];
@@ -1965,7 +1967,7 @@ bool same_directory(char_u *f1, char_u *f2)
return false;
}
- (void)vim_FullName((char *)f1, (char *)ffname, MAXPATHL, FALSE);
+ (void)vim_FullName((char *)f1, (char *)ffname, MAXPATHL, false);
t1 = path_tail_with_sep(ffname);
t2 = path_tail_with_sep((char *)f2);
return t1 - ffname == (char_u *)t2 - f2
@@ -2133,10 +2135,11 @@ int expand_wildcards_eval(char_u **pat, int *num_file, char ***file, int flags)
if (*exp_pat == '%' || *exp_pat == '#' || *exp_pat == '<') {
emsg_off++;
- eval_pat = eval_vars((char_u *)exp_pat, (char_u *)exp_pat, &usedlen, NULL, &ignored_msg, NULL);
+ eval_pat = eval_vars((char_u *)exp_pat, (char_u *)exp_pat, &usedlen, NULL, &ignored_msg, NULL,
+ true);
emsg_off--;
if (eval_pat != NULL) {
- exp_pat = (char *)concat_str(eval_pat, (char_u *)exp_pat + usedlen);
+ exp_pat = concat_str((char *)eval_pat, exp_pat + usedlen);
}
}
@@ -2192,7 +2195,7 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
ffname = (char_u *)FullName_save((*files)[i], false);
assert((*files)[i] != NULL);
assert(ffname != NULL);
- if (match_file_list(p_wig, (char_u *)(*files)[i], ffname)) {
+ if (match_file_list((char_u *)p_wig, (char_u *)(*files)[i], ffname)) {
// remove this matching file from the list
xfree((*files)[i]);
for (j = i; j + 1 < *num_files; j++) {
@@ -2205,17 +2208,14 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
}
}
- //
// Move the names where 'suffixes' match to the end.
- //
+ // Skip when interrupted, the result probably won't be used.
assert(*num_files == 0 || *files != NULL);
- if (*num_files > 1) {
+ if (*num_files > 1 && !got_int) {
non_suf_match = 0;
for (i = 0; i < *num_files; i++) {
if (!match_suffix((char_u *)(*files)[i])) {
- //
// Move the name without matching suffix to the front of the list.
- //
p = (char_u *)(*files)[i];
for (j = i; j > non_suf_match; j--) {
(*files)[j] = (*files)[j - 1];
@@ -2234,9 +2234,7 @@ int expand_wildcards(int num_pat, char **pat, int *num_files, char ***files, int
return retval;
}
-/*
- * Return TRUE if "fname" matches with an entry in 'suffixes'.
- */
+/// Return true if "fname" matches with an entry in 'suffixes'.
int match_suffix(char_u *fname)
{
#define MAXSUFLEN 30 // maximum length of a file suffix
@@ -2355,20 +2353,20 @@ int append_path(char *path, const char *to_append, size_t max_len)
/// @param force also expand when "fname" is already absolute.
///
/// @return FAIL for failure, OK for success.
-static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int force)
+static int path_to_absolute(const char *fname, char *buf, size_t len, int force)
{
- char_u *p;
+ char *p;
*buf = NUL;
char *relative_directory = xmalloc(len);
char *end_of_path = (char *)fname;
// expand it if forced or not an absolute path
- if (force || !path_is_absolute(fname)) {
- p = STRRCHR(fname, '/');
+ if (force || !path_is_absolute((char_u *)fname)) {
+ p = strrchr(fname, '/');
#ifdef WIN32
if (p == NULL) {
- p = STRRCHR(fname, '\\');
+ p = strrchr(fname, '\\');
}
#endif
if (p != NULL) {
@@ -2382,24 +2380,24 @@ static int path_to_absolute(const char_u *fname, char_u *buf, size_t len, int fo
memcpy(relative_directory, fname, (size_t)(p - fname));
relative_directory[p - fname] = NUL;
}
- end_of_path = (char *)(p + 1);
+ end_of_path = p + 1;
} else {
relative_directory[0] = NUL;
end_of_path = (char *)fname;
}
- if (FAIL == path_full_dir_name(relative_directory, (char *)buf, len)) {
+ if (FAIL == path_full_dir_name(relative_directory, buf, len)) {
xfree(relative_directory);
return FAIL;
}
}
xfree(relative_directory);
- return append_path((char *)buf, end_of_path, len);
+ return append_path(buf, end_of_path, len);
}
/// Check if file `fname` is a full (absolute) path.
///
-/// @return `TRUE` if "fname" is absolute.
+/// @return `true` if "fname" is absolute.
int path_is_absolute(const char_u *fname)
{
#ifdef WIN32
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 70bdbd8b1d..cc730ba307 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -98,15 +98,15 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight)
/// "wp". Does not care about folding, 'wrap' or 'diff'.
int plines_win_nofold(win_T *wp, linenr_T lnum)
{
- char_u *s;
+ char *s;
unsigned int col;
int width;
- s = ml_get_buf(wp->w_buffer, lnum, false);
+ s = (char *)ml_get_buf(wp->w_buffer, lnum, false);
if (*s == NUL) { // empty line
return 1;
}
- col = win_linetabsize(wp, s, MAXCOL);
+ col = win_linetabsize(wp, lnum, (char_u *)s, MAXCOL);
// If list mode is on, then the '$' at the end of the line may take up one
// extra column.
@@ -145,23 +145,27 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
}
char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
- char_u *s = line;
colnr_T col = 0;
- while (*s != NUL && --column >= 0) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL);
- MB_PTR_ADV(s);
+ chartabsize_T cts;
+
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ while (*cts.cts_ptr != NUL && --column >= 0) {
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
+ MB_PTR_ADV(cts.cts_ptr);
}
- // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
- // MODE_INSERT state, then col must be adjusted so that it represents the
+ // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not
+ // in MODE_INSERT state, then col must be adjusted so that it represents the
// last screen position of the TAB. This only fixes an error when the TAB
// wraps from one screen line to the next (when 'columns' is not a multiple
// of 'ts') -- webb.
- if (*s == TAB && (State & MODE_NORMAL)
+ col = cts.cts_vcol;
+ if (*cts.cts_ptr == TAB && (State & MODE_NORMAL)
&& (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
+ col += win_lbr_chartabsize(&cts, NULL) - 1;
}
+ clear_chartabsize_arg(&cts);
// Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
int width = wp->w_width_inner - win_col_off(wp);
@@ -223,13 +227,13 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
/// @param col
///
/// @return Number of characters.
-int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
+int win_chartabsize(win_T *wp, char *p, colnr_T col)
{
buf_T *buf = wp->w_buffer;
if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
return tabstop_padding(col, buf->b_p_ts, buf->b_p_vts_array);
} else {
- return ptr2cells((char *)p);
+ return ptr2cells(p);
}
}
@@ -241,24 +245,24 @@ int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
/// @return Number of characters the string will take on the screen.
int linetabsize(char_u *s)
{
- return linetabsize_col(0, s);
+ return linetabsize_col(0, (char *)s);
}
-/// Like linetabsize(), but starting at column "startcol".
+/// Like linetabsize(), but "s" starts at column "startcol".
///
/// @param startcol
/// @param s
///
/// @return Number of characters the string will take on the screen.
-int linetabsize_col(int startcol, char_u *s)
+int linetabsize_col(int startcol, char *s)
{
- colnr_T col = startcol;
- char_u *line = s; // pointer to start of line, for breakindent
-
- while (*s != NUL) {
- col += lbr_chartabsize_adv(line, &s, col);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, curwin, 0, startcol, (char_u *)s, (char_u *)s);
+ while (*cts.cts_ptr != NUL) {
+ cts.cts_vcol += lbr_chartabsize_adv(&cts);
}
- return (int)col;
+ clear_chartabsize_arg(&cts);
+ return cts.cts_vcol;
}
/// Like linetabsize(), but for a given window instead of the current one.
@@ -268,19 +272,39 @@ int linetabsize_col(int startcol, char_u *s)
/// @param len
///
/// @return Number of characters the string will take on the screen.
-unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
+unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char_u *line, colnr_T len)
{
- colnr_T col = 0;
-
- for (char_u *s = line;
- *s != NUL && (len == MAXCOL || s < line + len);
- MB_PTR_ADV(s)) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL);
+ chartabsize_T cts;
+ init_chartabsize_arg(&cts, wp, lnum, 0, line, line);
+ for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < (char *)line + len);
+ MB_PTR_ADV(cts.cts_ptr)) {
+ cts.cts_vcol += win_lbr_chartabsize(&cts, NULL);
}
+ clear_chartabsize_arg(&cts);
+ return (unsigned int)cts.cts_vcol;
+}
- return (unsigned int)col;
+/// Prepare the structure passed to chartabsize functions.
+///
+/// "line" is the start of the line, "ptr" is the first relevant character.
+/// When "lnum" is zero do not use text properties that insert text.
+void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char_u *line,
+ char_u *ptr)
+{
+ cts->cts_win = wp;
+ cts->cts_lnum = lnum;
+ cts->cts_vcol = col;
+ cts->cts_line = (char *)line;
+ cts->cts_ptr = (char *)ptr;
+ cts->cts_cur_text_width = 0;
+ // TODO(bfredl): actually lookup inline virtual text here
+ cts->cts_has_virt_text = false;
}
+/// Free any allocated item in "cts".
+void clear_chartabsize_arg(chartabsize_T *cts)
+{}
+
/// like win_chartabsize(), but also check for line breaks on the screen
///
/// @param line
@@ -288,16 +312,16 @@ unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
/// @param col
///
/// @return The number of characters taken up on the screen.
-int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
+int lbr_chartabsize(chartabsize_T *cts)
{
if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
- && !curwin->w_p_bri) {
+ && !curwin->w_p_bri && !cts->cts_has_virt_text) {
if (curwin->w_p_wrap) {
- return win_nolbr_chartabsize(curwin, s, col, NULL);
+ return win_nolbr_chartabsize(cts, NULL);
}
- return win_chartabsize(curwin, s, col);
+ return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol);
}
- return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL);
+ return win_lbr_chartabsize(cts, NULL);
}
/// Call lbr_chartabsize() and advance the pointer.
@@ -307,12 +331,12 @@ int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
/// @param col
///
/// @return The number of characters take up on the screen.
-int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
+int lbr_chartabsize_adv(chartabsize_T *cts)
{
int retval;
- retval = lbr_chartabsize(line, *s, col);
- MB_PTR_ADV(*s);
+ retval = lbr_chartabsize(cts);
+ MB_PTR_ADV(cts->cts_ptr);
return retval;
}
@@ -322,17 +346,19 @@ int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
/// string at start of line. Warning: *headp is only set if it's a non-zero
/// value, init to 0 before calling.
///
-/// @param wp
-/// @param line
-/// @param s
-/// @param col
+/// @param cts
/// @param headp
///
/// @return The number of characters taken up on the screen.
-int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp)
+int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
{
+ win_T *wp = cts->cts_win;
+ char *line = cts->cts_line; // start of the line
+ char_u *s = (char_u *)cts->cts_ptr;
+ colnr_T vcol = cts->cts_vcol;
+
colnr_T col2;
- colnr_T col_adj = 0; // col + screen size of tab
+ colnr_T col_adj = 0; // vcol + screen size of tab
colnr_T colmax;
int added;
int mb_added = 0;
@@ -340,16 +366,23 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
char_u *ps;
int n;
+ cts->cts_cur_text_width = 0;
+
// No 'linebreak', 'showbreak' and 'breakindent': return quickly.
- if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL) {
+ if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL
+ && !cts->cts_has_virt_text) {
if (wp->w_p_wrap) {
- return win_nolbr_chartabsize(wp, s, col, headp);
+ return win_nolbr_chartabsize(cts, headp);
}
- return win_chartabsize(wp, s, col);
+ return win_chartabsize(wp, (char *)s, vcol);
+ }
+
+ // First get normal size, without 'linebreak' or virtual text
+ int size = win_chartabsize(wp, (char *)s, vcol);
+ if (cts->cts_has_virt_text) {
+ // TODO(bfredl): inline virtual text
}
- // First get normal size, without 'linebreak'
- int size = win_chartabsize(wp, s, col);
int c = *s;
if (*s == TAB) {
col_adj = size - 1;
@@ -365,15 +398,15 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
numberextra = win_col_off(wp);
- col2 = col;
+ col2 = vcol;
colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
- if (col >= colmax) {
+ if (vcol >= colmax) {
colmax += col_adj;
n = colmax + win_col_off2(wp);
if (n > 0) {
- colmax += (((col - colmax) / n) + 1) * n - col_adj;
+ colmax += (((vcol - colmax) / n) + 1) * n - col_adj;
}
}
@@ -383,21 +416,21 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
c = *s;
if (!(c != NUL
- && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) {
+ && (vim_isbreak(c) || col2 == vcol || !vim_isbreak((int)(*ps))))) {
break;
}
- col2 += win_chartabsize(wp, s, col2);
+ col2 += win_chartabsize(wp, (char *)s, col2);
if (col2 >= colmax) { // doesn't fit
- size = colmax - col + col_adj;
+ size = colmax - vcol + col_adj;
break;
}
}
} else if ((size == 2)
&& (MB_BYTE2LEN(*s) > 1)
&& wp->w_p_wrap
- && in_win_border(wp, col)) {
+ && in_win_border(wp, vcol)) {
// Count the ">" in the last column.
size++;
mb_added = 1;
@@ -409,40 +442,40 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
added = 0;
char *const sbr = (char *)get_showbreak_value(wp);
- if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && col != 0) {
+ if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) {
colnr_T sbrlen = 0;
int numberwidth = win_col_off(wp);
numberextra = numberwidth;
- col += numberextra + mb_added;
+ vcol += numberextra + mb_added;
- if (col >= (colnr_T)wp->w_width_inner) {
- col -= wp->w_width_inner;
+ if (vcol >= (colnr_T)wp->w_width_inner) {
+ vcol -= wp->w_width_inner;
numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
- if (col >= numberextra && numberextra > 0) {
- col %= numberextra;
+ if (vcol >= numberextra && numberextra > 0) {
+ vcol %= numberextra;
}
if (*sbr != NUL) {
sbrlen = (colnr_T)mb_charlen((char_u *)sbr);
- if (col >= sbrlen) {
- col -= sbrlen;
+ if (vcol >= sbrlen) {
+ vcol -= sbrlen;
}
}
- if (col >= numberextra && numberextra > 0) {
- col %= numberextra;
- } else if (col > 0 && numberextra > 0) {
- col += numberwidth - win_col_off2(wp);
+ if (vcol >= numberextra && numberextra > 0) {
+ vcol %= numberextra;
+ } else if (vcol > 0 && numberextra > 0) {
+ vcol += numberwidth - win_col_off2(wp);
}
numberwidth -= win_col_off2(wp);
}
- if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) {
+ if (vcol == 0 || (vcol + size + sbrlen > (colnr_T)wp->w_width_inner)) {
if (*sbr != NUL) {
if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
// Calculate effective window width.
int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
- int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col))
+ int prev_width = vcol ? ((colnr_T)wp->w_width_inner - (sbrlen + vcol))
: 0;
if (width <= 0) {
@@ -459,11 +492,11 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
}
if (wp->w_p_bri) {
- added += get_breakindent_win(wp, line);
+ added += get_breakindent_win(wp, (char_u *)line);
}
size += added;
- if (col != 0) {
+ if (vcol != 0) {
added = 0;
}
}
@@ -485,8 +518,11 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
/// @param headp
///
/// @return The number of characters take up on the screen.
-static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
+static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp)
{
+ win_T *wp = cts->cts_win;
+ char *s = cts->cts_ptr;
+ colnr_T col = cts->cts_vcol;
int n;
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
@@ -494,11 +530,11 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array);
}
- n = ptr2cells((char *)s);
+ n = ptr2cells(s);
// Add one cell for a double-width character in the last column of the
// window, displayed with a ">".
- if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) {
+ if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) {
if (headp != NULL) {
*headp = 1;
}
diff --git a/src/nvim/plines.h b/src/nvim/plines.h
index 32778b69f1..7b228f3e91 100644
--- a/src/nvim/plines.h
+++ b/src/nvim/plines.h
@@ -3,6 +3,20 @@
#include "nvim/vim.h"
+// Argument for lbr_chartabsize().
+typedef struct {
+ win_T *cts_win;
+ linenr_T cts_lnum; // zero when not using text properties
+ char *cts_line; // start of the line
+ char *cts_ptr; // current position in line
+
+ bool cts_has_virt_text; // true if if a property inserts text
+ int cts_cur_text_width; // width of current inserted text
+ // TODO(bfredl): iterator in to the marktree for scanning virt text
+
+ int cts_vcol; // virtual column at current position
+} chartabsize_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "plines.h.generated.h"
#endif
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index 82345f8a46..d64789660e 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -5320,8 +5320,8 @@ msgstr "E424: Te veel verskillende uitlig-eienskappe in gebruik"
msgid "E669: Unprintable character in group name"
msgstr "E669: Onvertoonbare karakter in groepnaam"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ongeldige karakter groepnaam"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ongeldige karakter groepnaam"
#~ msgid "E849: Too many highlight and syntax groups"
#~ msgstr ""
diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po
index 6c4d6ddd22..5869e6567c 100644
--- a/src/nvim/po/ca.po
+++ b/src/nvim/po/ca.po
@@ -5983,9 +5983,9 @@ msgstr "E424: Hi ha massa atributs de ressalt diferents en ús"
msgid "E669: Unprintable character in group name"
msgstr "E669: Caràcter no imprimible en el nom del grup"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Hi ha un caràcter no vàlid en el nom del grup"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Hi ha un caràcter no vàlid en el nom del grup"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/cs.cp1250.po b/src/nvim/po/cs.cp1250.po
index 859039eb87..bce5c0fa76 100644
--- a/src/nvim/po/cs.cp1250.po
+++ b/src/nvim/po/cs.cp1250.po
@@ -6072,10 +6072,10 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
+#: ../highlight_group.c:1756
#, fuzzy
-msgid "W18: Invalid character in group name"
-msgstr "E182: Chybné jméno pøíkazu"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybné jméno pøíkazu"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po
index 4d9ad58836..f4eab3f0d4 100644
--- a/src/nvim/po/cs.po
+++ b/src/nvim/po/cs.po
@@ -6072,10 +6072,10 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
+#: ../highlight_group.c:1756
#, fuzzy
-msgid "W18: Invalid character in group name"
-msgstr "E182: Chybné jméno pøíkazu"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybné jméno pøíkazu"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po
index dfcc052288..1c3284f867 100644
--- a/src/nvim/po/da.po
+++ b/src/nvim/po/da.po
@@ -5599,8 +5599,8 @@ msgstr "E424: For mange forskellige fremhævningsattributter i brug"
msgid "E669: Unprintable character in group name"
msgstr "E669: Tegn som ikke kan udskrives i gruppenavn"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ugyldige tegn i gruppenavn"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ugyldige tegn i gruppenavn"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: For mange fremhævnings- og syntaksgrupper"
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index 2dde77e9f7..ce3fd77ede 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -5408,8 +5408,8 @@ msgid "E669: Unprintable character in group name"
msgstr "E669: Nicht druckbare Zeichen im Namen der Gruppe"
#: ../syntax.c:7304
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ungültiges Zeichen im Namen der Gruppe"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ungültiges Zeichen im Namen der Gruppe"
#: ../syntax.c:7318
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po
index 66cdba6f92..81ee9ed6a0 100644
--- a/src/nvim/po/en_GB.po
+++ b/src/nvim/po/en_GB.po
@@ -5730,8 +5730,8 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
msgstr ""
#: ../syntax.c:7448
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 1c503d0a84..263fb61b18 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -5378,8 +5378,8 @@ msgstr "E424: Tro da malsamaj atributoj de emfazo uzataj"
msgid "E669: Unprintable character in group name"
msgstr "E669: Nepresebla signo en nomo de grupo"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Nevalida signo en nomo de grupo"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Nevalida signo en nomo de grupo"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Tro da emfazaj kaj sintaksaj grupoj"
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index adea651b7c..8a44f6a534 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -6053,9 +6053,9 @@ msgstr "E669: Carácter no imprimible en el nombre del grupo"
# This is an error, but since there previously was no check only
# * give a warning.
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Hay un carácter no válido en el nombre del grupo"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Hay un carácter no válido en el nombre del grupo"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index f10d4ce977..1c0da244ba 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -5369,8 +5369,8 @@ msgstr "E424: Liikaa eri korostusattribuutteja"
msgid "E669: Unprintable character in group name"
msgstr "E669: Tulostuskelvoton merkki ryhmän nimessä"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Virheellinen merkki ryhmän nimessä"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Virheellinen merkki ryhmän nimessä"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Liikaa korostuksia ja syntaksiryhmiä"
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 614ba013e6..be2141cd6d 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -2040,8 +2040,8 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr "E669: Caractère non imprimable dans un nom de groupe"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Caractère invalide dans un nom de groupe"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Caractère invalide dans un nom de groupe"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Trop de groupes de surbrillance et de syntaxe"
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index 1c25ee481c..346f0faa84 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -5668,8 +5668,8 @@ msgstr "E424: An iomarca tréithe aibhsithe in úsáid"
msgid "E669: Unprintable character in group name"
msgstr "E669: Carachtar neamhghrafach in ainm grúpa"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Carachtar neamhbhailí in ainm grúpa"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Carachtar neamhbhailí in ainm grúpa"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: An iomarca grúpaí aibhsithe agus comhréire"
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index 313280c807..152ed2cbe3 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -6046,9 +6046,9 @@ msgstr "E424: Troppi gruppi evidenziazione differenti in uso"
msgid "E669: Unprintable character in group name"
msgstr "E669: Carattere non stampabile in un nome di gruppo"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Carattere non ammesso in un nome di gruppo"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Carattere non ammesso in un nome di gruppo"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po
index b99c22caeb..09be710374 100644
--- a/src/nvim/po/ko.UTF-8.po
+++ b/src/nvim/po/ko.UTF-8.po
@@ -5913,9 +5913,9 @@ msgstr "E424: 너무 ë§Žì€ ë‹¤ë¥¸ 하ì´ë¼ì´íЏ ì†ì„±ì´ 사용ë˜ê³  있습
msgid "E669: Unprintable character in group name"
msgstr "E669: 그룹 ì´ë¦„ì— ì¶œë ¥í•  수 없는 문ìžê°€ 있습니다"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: 그룹 ì´ë¦„ì— ì´ìƒí•œ 문ìž"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: 그룹 ì´ë¦„ì— ì´ìƒí•œ 문ìž"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po
index 2285d755cf..9bc730ae71 100644
--- a/src/nvim/po/nb.po
+++ b/src/nvim/po/nb.po
@@ -5930,9 +5930,9 @@ msgstr "E424: For mange forskjellige uthevingsattributter i bruk"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ikke-skrivbart tegn i gruppenavn"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ugyldig tegn i gruppenavn"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ugyldig tegn i gruppenavn"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po
index 00d113c83c..4d2e55adc6 100644
--- a/src/nvim/po/nl.po
+++ b/src/nvim/po/nl.po
@@ -5913,8 +5913,8 @@ msgstr ""
msgid "E669: Unprintable character in group name"
msgstr ""
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
msgstr ""
#: ../syntax.c:7448
diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po
index 2285d755cf..9bc730ae71 100644
--- a/src/nvim/po/no.po
+++ b/src/nvim/po/no.po
@@ -5930,9 +5930,9 @@ msgstr "E424: For mange forskjellige uthevingsattributter i bruk"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ikke-skrivbart tegn i gruppenavn"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ugyldig tegn i gruppenavn"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ugyldig tegn i gruppenavn"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po
index 5f1779d1bd..7e978113f6 100644
--- a/src/nvim/po/pl.UTF-8.po
+++ b/src/nvim/po/pl.UTF-8.po
@@ -5908,9 +5908,9 @@ msgstr "E424: Zbyt wiele różnych atrybutów podkreślania w użyciu"
msgid "E669: Unprintable character in group name"
msgstr "E669: Niedrukowalny znak w nazwie grupy"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: nieprawidłowy znak w nazwie grupy"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: nieprawidłowy znak w nazwie grupy"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po
index 533d916de1..bfd64b4d28 100644
--- a/src/nvim/po/pt_BR.po
+++ b/src/nvim/po/pt_BR.po
@@ -6070,8 +6070,8 @@ msgid "E669: Unprintable character in group name"
msgstr "E669: Caractere não-imprimível no nome do grupo"
#: ../syntax.c:7320
-msgid "W18: Invalid character in group name"
-msgstr "W18: Caractere inválido no nome do grupo"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Caractere inválido no nome do grupo"
#: ../syntax.c:7334
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index 7566036d3e..da356770d7 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -5977,9 +5977,9 @@ msgstr "E424: ИÑпользуетÑÑ Ñлишком много разных а
msgid "E669: Unprintable character in group name"
msgstr "E669: Ðепечатный Ñимвол в имени группы"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: ÐедопуÑтимый Ñимвол в имени группы"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: ÐедопуÑтимый Ñимвол в имени группы"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po
index ff95c68a12..cb8b8c6abb 100644
--- a/src/nvim/po/sk.cp1250.po
+++ b/src/nvim/po/sk.cp1250.po
@@ -5940,9 +5940,9 @@ msgstr "E424: Používaných príliš ve¾a odlišných zvýrazòovacích vlastností"
msgid "E669: Unprintable character in group name"
msgstr "E669: Netlaèitelný znak v mene skupiny"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Chybný znak v mene skupiny"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybný znak v mene skupiny"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po
index d35622726f..4f9e1fe185 100644
--- a/src/nvim/po/sk.po
+++ b/src/nvim/po/sk.po
@@ -5940,9 +5940,9 @@ msgstr "E424: Pou¾ívaných príli¹ veµa odli¹ných zvýrazòovacích vlastností"
msgid "E669: Unprintable character in group name"
msgstr "E669: Netlaèitelný znak v mene skupiny"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Chybný znak v mene skupiny"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Chybný znak v mene skupiny"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index 3c45e1bf80..cbdf736d0f 100644
--- a/src/nvim/po/sr.po
+++ b/src/nvim/po/sr.po
@@ -6050,8 +6050,8 @@ msgstr "E424: У употреби је превише различитих атÑ
msgid "E669: Unprintable character in group name"
msgstr "E669: У имену групе је карактер који не може да Ñе штампа"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ðеважећи карактер у имену групе"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ðеважећи карактер у имену групе"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Превише ÑинтакÑних и група иÑтицања"
diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po
index d50c9d695d..406900f7b2 100644
--- a/src/nvim/po/sv.po
+++ b/src/nvim/po/sv.po
@@ -2056,8 +2056,8 @@ msgid "E669: Unprintable character in group name"
msgstr "E669: Outskrivbart tecken i gruppnamn"
#: ../syntax.c:7292
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ogiltigt tecken i gruppnamn"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ogiltigt tecken i gruppnamn"
#: ../syntax.c:7306
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po
index fae2fd4967..20b667d198 100644
--- a/src/nvim/po/tr.po
+++ b/src/nvim/po/tr.po
@@ -3054,8 +3054,8 @@ msgstr "E423: İzin verilmeyen argüman: %s"
msgid "E669: Unprintable character in group name"
msgstr "E669: Grup adında yazdırılamayan karakter"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Grup adında geçersiz karakter"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Grup adında geçersiz karakter"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Çok fazla vurgulama ve sözdizim grupları"
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index da87d50683..427abd9b77 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -3058,8 +3058,8 @@ msgstr "E423: Ðеправильний аргумент: %s"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ðедруковний Ñимвол у назві групи"
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ðекоректний Ñимвол у назві групи"
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ðекоректний Ñимвол у назві групи"
msgid "E849: Too many highlight and syntax groups"
msgstr "E849: Забагато груп підÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– ÑинтакÑиÑу"
diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po
index c693f910d8..ad59718a30 100644
--- a/src/nvim/po/vi.po
+++ b/src/nvim/po/vi.po
@@ -5975,9 +5975,9 @@ msgstr "E424: Sá»­ dụng quá nhiá»u thuá»™c tính chiếu sáng cú pháp"
msgid "E669: Unprintable character in group name"
msgstr "E669: Ký tự không thể tin ra trong tên nhóm"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: Ký tự không cho phép trong tên nhóm"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: Ký tự không cho phép trong tên nhóm"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index 70c1389d7f..afa2f29029 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -734,9 +734,9 @@ msgid "E120: Using <SID> not in a script context: %s"
msgstr "E120: <SID> ä¸èƒ½åœ¨ script 上下文外使用: %s"
#: ../eval.c:7391
-#, fuzzy, c-format
+#, c-format
msgid "E725: Calling dict function without Dictionary: %s"
-msgstr "E720: Dictionary 中缺少冒å·: %s"
+msgstr "E725: 调用字典函数但是没有字典:%s"
#: ../eval.c:7453
#, fuzzy
@@ -766,19 +766,16 @@ msgid "E737: Key already exists: %s"
msgstr "E737: 键已存在: %s"
#: ../eval.c:8692
-#, fuzzy
msgid "extend() argument"
-msgstr "--cmd 傿•°"
+msgstr "extend() 傿•°"
#: ../eval.c:8915
-#, fuzzy
msgid "map() argument"
-msgstr "-c 傿•°"
+msgstr "map() 傿•°"
#: ../eval.c:8916
-#, fuzzy
msgid "filter() argument"
-msgstr "-c 傿•°"
+msgstr "filter() 傿•°"
#: ../eval.c:9229
#, c-format
@@ -849,9 +846,8 @@ msgid "E702: Sort compare function failed"
msgstr "E702: Sort 比较函数失败"
#: ../eval.c:13806
-#, fuzzy
msgid "E882: Uniq compare function failed"
-msgstr "E702: Sort 比较函数失败"
+msgstr "E882: Uniq 比较函数失败"
#: ../eval.c:14085
msgid "(Invalid)"
@@ -864,31 +860,31 @@ msgstr "E677: 写临时文件出错"
#: ../eval.c:16159
#, fuzzy
msgid "E805: Using a Float as a Number"
-msgstr "E745: 将 List 作数字使用"
+msgstr "E805: å°†æµ®ç‚¹æ•°å½“åšæ•°å­—使用"
#: ../eval.c:16162
msgid "E703: Using a Funcref as a Number"
-msgstr "E703: 将 Funcref 作数字使用"
+msgstr "E703: å°†å‡½æ•°å½“åšæ•°å­—使用"
#: ../eval.c:16170
msgid "E745: Using a List as a Number"
-msgstr "E745: 将 List 作数字使用"
+msgstr "E745: å°†åˆ—è¡¨å½“åšæ•°å­—使用"
#: ../eval.c:16173
msgid "E728: Using a Dictionary as a Number"
-msgstr "E728: 将 Dictionary 作数字使用"
+msgstr "E728: å°†å­—å…¸å½“åšæ•°å­—使用"
#: ../eval.c:16259
msgid "E729: using Funcref as a String"
-msgstr "E729: 将 Funcref 作 String 使用"
+msgstr "E729: 将函数当åšå­—符串使用"
#: ../eval.c:16262
msgid "E730: using List as a String"
-msgstr "E730: 将 List 作 String 使用"
+msgstr "E730: 将列表当åšå­—符串使用"
#: ../eval.c:16265
msgid "E731: using Dictionary as a String"
-msgstr "E731: 将 Dictionary 作 String 使用"
+msgstr "E731: 将字典当åšå­—符串使用"
#: ../eval.c:16619
#, c-format
@@ -3053,11 +3049,11 @@ msgstr "E673: ä¸å…¼å®¹çš„多字节编ç å’Œå­—符集。"
#: ../hardcopy.c:2238
msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr "E674: printmbcharset 在多字节编ç ä¸‹ä¸èƒ½ä¸ºç©ºã€‚"
+msgstr "E674: printmbcharset 在多字节编ç ä¸‹ä¸èƒ½ä¸ºç©º"
#: ../hardcopy.c:2254
msgid "E675: No default font specified for multi-byte printing."
-msgstr "E675: 没有指定多字节打å°çš„默认字体。"
+msgstr "E675: 没有指定多字节打å°çš„默认字体"
#: ../hardcopy.c:2426
msgid "E324: Can't open PostScript output file"
@@ -4204,9 +4200,8 @@ msgstr "E329: 没有èœå• \"%s\""
#. Only a mnemonic or accelerator is not valid.
#: ../menu.c:329
-#, fuzzy
msgid "E792: Empty menu name"
-msgstr "E749: 空的缓冲区"
+msgstr "E792: 空的èœå•åç§°"
#: ../menu.c:340
msgid "E330: Menu path must not lead to a sub-menu"
@@ -4329,9 +4324,8 @@ msgid "E766: Insufficient arguments for printf()"
msgstr "E766: printf() çš„å‚æ•°ä¸è¶³"
#: ../message.c:3119
-#, fuzzy
msgid "E807: Expected Float argument for printf()"
-msgstr "E766: printf() çš„å‚æ•°ä¸è¶³"
+msgstr "E807: 期盼浮点数作为printf()傿•°"
#: ../message.c:3873
msgid "E767: Too many arguments to printf()"
@@ -5675,9 +5669,9 @@ msgid "E781: .sug file doesn't match .spl file: %s"
msgstr "E781: .sug 文件ä¸èƒ½åŒ¹é… .spl 文件: %s"
#: ../spell.c:9305
-#, fuzzy, c-format
+#, c-format
msgid "E782: error while reading .sug file: %s"
-msgstr "E47: 读å–错误文件失败"
+msgstr "E782: 当读å–.sug 文件时错误"
#. This should have been checked when generating the .spl
#. file.
@@ -5867,6 +5861,7 @@ msgstr "E410: 䏿­£ç¡®çš„ :syntax å­å‘½ä»¤: %s"
msgid ""
" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
msgstr ""
+" 总 计 计 æ•° 匹 é… æœ€ æ…¢ çš„ å¹³ å‡ å å­— 模 å¼"
#: ../syntax.c:6146
msgid "E679: recursive loop loading syncolor.vim"
@@ -5942,9 +5937,9 @@ msgstr "E424: 使用了太多ä¸åŒçš„高亮度属性"
msgid "E669: Unprintable character in group name"
msgstr "E669: 组å中存在ä¸å¯æ˜¾ç¤ºå­—符"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: 组å䏭嫿œ‰æ— æ•ˆå­—符"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: 组å䏭嫿œ‰æ— æ•ˆå­—符"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
@@ -6103,14 +6098,13 @@ msgstr "Vim: 读错误,退出中...\n"
#. This happens when the FileChangedRO autocommand changes the
#. * file in a way it becomes shorter.
#: ../undo.c:379
-#, fuzzy
msgid "E881: Line count changed unexpectedly"
-msgstr "E787: æ„外地改å˜äº†ç¼“冲区"
+msgstr "E881: 行数æ„外地改å˜äº†"
#: ../undo.c:627
-#, fuzzy, c-format
+#, c-format
msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: 无法打开并写入文件"
+msgstr "E828: 无法打开撤销文件去写入"
#: ../undo.c:717
#, c-format
diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po
index e2fb2d39d4..e95b1e2cad 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -59,19 +59,19 @@ msgstr "無法傳é€å›žæ‡‰è¨Šæ¯"
#: ../api/private/helpers.c:204
msgid "internal error: unknown option type"
-msgstr ""
+msgstr "內部錯誤: 未知的é¸é …類型"
#: ../buffer.c:92
msgid "[Location List]"
-msgstr ""
+msgstr "[Location 列表]"
#: ../buffer.c:93
msgid "[Quickfix List]"
-msgstr ""
+msgstr "[Quickfix 列表]"
#: ../buffer.c:94
msgid "E855: Autocommands caused command to abort"
-msgstr ""
+msgstr "E855: è‡ªå‹•å‘½ä»¤å°Žè‡´å‘½ä»¤è¢«åœæ­¢"
#: ../buffer.c:135
msgid "E82: Cannot allocate any buffer, exiting..."
@@ -349,7 +349,7 @@ msgstr "E103: ç·©è¡å€ \"%s\" 䏿˜¯åœ¨ diff 模å¼"
#: ../diff.c:2193
msgid "E787: Buffer changed unexpectedly"
-msgstr ""
+msgstr "E787: æ„外地改變了緩è¡å€"
#: ../digraph.c:1598
msgid "E104: Escape not allowed in digraph"
@@ -365,7 +365,7 @@ msgstr "E105: 使用 :loadkeymap "
#: ../digraph.c:1821
msgid "E791: Empty keymap entry"
-msgstr ""
+msgstr "E791: 空的éµä½æ˜ å°„é …"
#: ../edit.c:82
msgid " Keyword completion (^N^P)"
@@ -434,11 +434,11 @@ msgstr "已到段è½çµå°¾"
#: ../edit.c:101
msgid "E839: Completion function changed window"
-msgstr ""
+msgstr "E839: è£œå…¨å‡½å¼æ›´æ”¹äº†çª—å£"
#: ../edit.c:102
msgid "E840: Completion function deleted text"
-msgstr ""
+msgstr "E840: 補全函å¼åˆªé™¤äº†æ–‡æœ¬"
#: ../edit.c:1847
msgid "'dictionary' option is empty"
@@ -556,7 +556,7 @@ msgstr "E118: å‡½å¼ %s 的引數éŽå¤š"
#: ../eval.c:148
#, c-format
msgid "E716: Key not present in Dictionary: %s"
-msgstr ""
+msgstr "E716: éµåœ¨å­—典中ä¸å­˜åœ¨: %s"
#: ../eval.c:150
#, c-format
@@ -581,7 +581,7 @@ msgstr "E360: ä¸èƒ½ç”¨ -f é¸é …執行 shell"
#: ../eval.c:154
#, c-format
msgid "E734: Wrong variable type for %s="
-msgstr ""
+msgstr "E734: 錯誤的變數類型: %s="
#: ../eval.c:155
#, fuzzy, c-format
@@ -595,19 +595,19 @@ msgstr "E461: ä¸åˆæ³•的變數å稱: %s"
#: ../eval.c:157
msgid "E806: using Float as a String"
-msgstr ""
+msgstr "E806: 使用浮點數作為字串"
#: ../eval.c:1830
msgid "E687: Less targets than List items"
-msgstr ""
+msgstr "E687: 目標比列表項數少"
#: ../eval.c:1834
msgid "E688: More targets than List items"
-msgstr ""
+msgstr "E688: 目標比列表項數多"
#: ../eval.c:1906
msgid "Double ; in list of variables"
-msgstr ""
+msgstr "變數列表出ç¾å…©å€‹ ;"
#: ../eval.c:2078
#, fuzzy, c-format
@@ -616,23 +616,23 @@ msgstr "E138: 無法寫入 viminfo 檔案 %s !"
#: ../eval.c:2391
msgid "E689: Can only index a List or Dictionary"
-msgstr ""
+msgstr "E689: åªèƒ½ç´¢å¼•一個列表或者字典"
#: ../eval.c:2396
msgid "E708: [:] must come last"
-msgstr ""
+msgstr "E708: [:] 必須在最後"
#: ../eval.c:2439
msgid "E709: [:] requires a List value"
-msgstr ""
+msgstr "E709: [:] 需è¦ä¸€å€‹åˆ—表值"
#: ../eval.c:2674
msgid "E710: List value has more items than target"
-msgstr ""
+msgstr "E710: 列表值的項比目標多"
#: ../eval.c:2678
msgid "E711: List value has not enough items"
-msgstr ""
+msgstr "E711: 列表值沒有足夠多的項"
#: ../eval.c:2867
#, fuzzy
@@ -651,7 +651,7 @@ msgstr "E108: 無此變數: \"%s\""
#: ../eval.c:3333
msgid "E743: variable nested too deep for (un)lock"
-msgstr ""
+msgstr "E743: (un)lock çš„è®Šæ•¸åµŒå¥—éŽæ·±"
#: ../eval.c:3630
msgid "E109: Missing ':' after '?'"
@@ -659,7 +659,7 @@ msgstr "E109: '?' 後缺少 ':'"
#: ../eval.c:3893
msgid "E691: Can only compare List with List"
-msgstr ""
+msgstr "E691: åªèƒ½æ¯”較列表和列表"
#: ../eval.c:3895
#, fuzzy
@@ -668,7 +668,7 @@ msgstr "E449: æ”¶åˆ°ä¸æ­£ç¢ºçš„é‹ç®—å¼"
#: ../eval.c:3915
msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr ""
+msgstr "E735: åªèƒ½æ¯”較字典和字典"
#: ../eval.c:3917
#, fuzzy
@@ -677,7 +677,7 @@ msgstr "E116: å‡½å¼ %s çš„å¼•æ•¸ä¸æ­£ç¢º"
#: ../eval.c:3932
msgid "E693: Can only compare Funcref with Funcref"
-msgstr ""
+msgstr "E693: åªèƒ½æ¯”較Funcref å’Œ Funcref"
#: ../eval.c:3934
#, fuzzy
@@ -736,7 +736,7 @@ msgstr "E242: 找ä¸åˆ°é¡è‰²: %s"
#: ../eval.c:6499
#, c-format
msgid "E721: Duplicate key in Dictionary: \"%s\""
-msgstr ""
+msgstr "E721: Dictionary 中出ç¾é‡è¤‡çš„éµ: \"%s\""
#: ../eval.c:6517
#, fuzzy, c-format
@@ -781,7 +781,7 @@ msgstr "E120: <SID> ä¸èƒ½åœ¨ script 本文外使用: %s"
#: ../eval.c:7391
#, c-format
msgid "E725: Calling dict function without Dictionary: %s"
-msgstr ""
+msgstr "E725: 調用字典函å¼ä½†æ˜¯æ²’有字典: %s"
#: ../eval.c:7453
#, fuzzy
@@ -814,16 +814,15 @@ msgstr "E227: %s 的 mapping 已經存在"
#: ../eval.c:8692
msgid "extend() argument"
-msgstr ""
+msgstr "extend() åƒæ•¸"
#: ../eval.c:8915
-#, fuzzy
msgid "map() argument"
-msgstr "vim [åƒæ•¸] "
+msgstr "map() åƒæ•¸"
#: ../eval.c:8916
msgid "filter() argument"
-msgstr ""
+msgstr "filter() åƒæ•¸"
#: ../eval.c:9229
#, c-format
@@ -857,19 +856,19 @@ msgstr "E596: 䏿­£ç¢ºçš„å­—åž‹"
#: ../eval.c:11980
msgid "E726: Stride is zero"
-msgstr ""
+msgstr "E726: 步長為零"
#: ../eval.c:11982
msgid "E727: Start past end"
-msgstr ""
+msgstr "E727: 起始值在終止值後"
#: ../eval.c:12024 ../eval.c:15297
msgid "<empty>"
-msgstr ""
+msgstr "<空>"
#: ../eval.c:12282
msgid "remove() argument"
-msgstr ""
+msgstr "remove() åƒæ•¸"
#: ../eval.c:12466
msgid "E655: Too many symbolic links (cycle?)"
@@ -877,11 +876,11 @@ msgstr "E655: 太多層的符號éˆçµ(symlink) (循環?)"
#: ../eval.c:12593
msgid "reverse() argument"
-msgstr ""
+msgstr "reverse() åƒæ•¸"
#: ../eval.c:13721
msgid "sort() argument"
-msgstr ""
+msgstr "sort() åƒæ•¸"
#: ../eval.c:13721
#, fuzzy
@@ -895,7 +894,7 @@ msgstr "E237: ç„¡æ³•é¸æ“‡æ­¤å°è¡¨æ©Ÿ"
#: ../eval.c:13806
msgid "E882: Uniq compare function failed"
-msgstr ""
+msgstr "E882: Uniq 比較函å¼å¤±æ•—"
#: ../eval.c:14085
msgid "(Invalid)"
@@ -908,32 +907,31 @@ msgstr "E208: 寫入檔案 \"%s\" 錯誤"
#: ../eval.c:16159
msgid "E805: Using a Float as a Number"
-msgstr ""
+msgstr "E805: å°‡æµ®é»žæ•¸ç•¶åšæ•¸å­—使用"
#: ../eval.c:16162
msgid "E703: Using a Funcref as a Number"
-msgstr ""
+msgstr "E703: 將函å¼ç•¶åšæ•¸å­—ä½¿ç”¨"
#: ../eval.c:16170
msgid "E745: Using a List as a Number"
-msgstr ""
+msgstr "E745: å°‡åˆ—è¡¨ç•¶åšæ•¸å­—使用"
#: ../eval.c:16173
msgid "E728: Using a Dictionary as a Number"
-msgstr ""
+msgstr "E728: å°‡å­—å…¸ç•¶åšæ•¸å­—使用"
#: ../eval.c:16259
msgid "E729: using Funcref as a String"
-msgstr ""
+msgstr "E729: 將函å¼ç•¶åšå­—串使用"
#: ../eval.c:16262
-#, fuzzy
msgid "E730: using List as a String"
-msgstr "E374: æ ¼å¼åŒ–字串裡少了 ]"
+msgstr "E730: 將列表當åšå­—串使用"
#: ../eval.c:16265
msgid "E731: using Dictionary as a String"
-msgstr ""
+msgstr "E731: 將字典當åšå­—串使用"
#: ../eval.c:16619
#, fuzzy, c-format
@@ -953,12 +951,12 @@ msgstr "E128: 函å¼å稱第一個字æ¯å¿…須大寫: %s"
#: ../eval.c:16732
#, c-format
msgid "E705: Variable name conflicts with existing function: %s"
-msgstr ""
+msgstr "E705: 變數å與已有函å¼åè¡çª: %s"
#: ../eval.c:16763
#, c-format
msgid "E741: Value is locked: %s"
-msgstr ""
+msgstr "E741: 值已鎖定: %s"
#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
msgid "Unknown"
@@ -971,7 +969,7 @@ msgstr "E284: ä¸èƒ½è¨­å®š IC 數值"
#: ../eval.c:16838
msgid "E698: variable nested too deep for making a copy"
-msgstr ""
+msgstr "E698: è®Šæ•¸åµŒå¥—éŽæ·±ç„¡æ³•複製"
#: ../eval.c:17249
#, c-format
@@ -1216,7 +1214,7 @@ msgstr "è¦è¦†å¯«å·²å­˜åœ¨çš„æª”案 \"%.*s\"?"
#: ../ex_cmds.c:2317
#, c-format
msgid "Swap file \"%s\" exists, overwrite anyway?"
-msgstr ""
+msgstr "äº¤æ›æ–‡ä»¶ \"%s\" 已存在,確實需è¦è¦†è“‹å—Žï¼Ÿ"
#: ../ex_cmds.c:2326
#, fuzzy, c-format
@@ -1456,7 +1454,7 @@ msgstr "%3d %s %s 第 %<PRId64> 行 "
#: ../ex_cmds2.c:942
msgid "E750: First use \":profile start {fname}\""
-msgstr ""
+msgstr "E750: 請先使用 :profile start <fname>"
#: ../ex_cmds2.c:1269
#, fuzzy, c-format
@@ -1555,7 +1553,7 @@ msgstr "vim [åƒæ•¸] "
#: ../ex_cmds2.c:2771
msgid "environment variable"
-msgstr ""
+msgstr "環境變數"
#: ../ex_cmds2.c:2773
#, fuzzy
@@ -2047,7 +2045,7 @@ msgstr "E199: 已刪除掉作用中的視窗或暫存å€"
#: ../file_search.c:203
msgid "E854: path too long for completion"
-msgstr ""
+msgstr "E854: 補全用的路徑太長了"
#: ../file_search.c:446
#, c-format
@@ -2099,11 +2097,11 @@ msgstr "[未命å]"
#: ../fileio.c:511
msgid "[New DIRECTORY]"
-msgstr ""
+msgstr "[新目錄]"
#: ../fileio.c:529 ../fileio.c:532
msgid "[File too big]"
-msgstr ""
+msgstr "[文件太大]"
#: ../fileio.c:534
msgid "[Permission Denied]"
@@ -2265,7 +2263,7 @@ msgstr "E513: 無法寫入 -- 轉æ›å¤±æ•—"
msgid ""
"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
"override)"
-msgstr ""
+msgstr "E513: 寫入錯誤,轉æ›å¤±æ•— (è«‹å°‡ 'fenc' 置空以強制執行)"
#: ../fileio.c:3448
msgid "E514: write error (file system full?)"
@@ -2720,7 +2718,7 @@ msgstr "E49: 錯誤的æ²å‹•大å°"
#: ../globals.h:1021
msgid "E901: Job table is full"
-msgstr ""
+msgstr "E901: 任務表已經滿"
#: ../globals.h:1024
#, c-format
@@ -2877,7 +2875,7 @@ msgstr "E42: 沒有錯誤"
#: ../globals.h:1067
msgid "E776: No location list"
-msgstr ""
+msgstr "E776: 沒有ä½ç½®åˆ—表"
#: ../globals.h:1068
msgid "E43: Damaged match string"
@@ -2992,7 +2990,7 @@ msgstr "E473: 內部錯誤"
#: ../globals.h:1104
msgid "E363: pattern uses more memory than 'maxmempattern'"
-msgstr ""
+msgstr "E363: 表é”å¼çš„內存超出 'maxmempattern'"
#: ../globals.h:1105
#, fuzzy
@@ -3097,15 +3095,15 @@ msgstr "E621: \"%s\" è³‡æºæª”版本錯誤"
#: ../hardcopy.c:2225
msgid "E673: Incompatible multi-byte encoding and character set."
-msgstr ""
+msgstr "E673: ä¸å…¼å®¹çš„多字節編碼和字元集"
#: ../hardcopy.c:2238
msgid "E674: printmbcharset cannot be empty with multi-byte encoding."
-msgstr ""
+msgstr "E674: printmbcharset 在多字節編碼下ä¸èƒ½ç‚ºç©º"
#: ../hardcopy.c:2254
msgid "E675: No default font specified for multi-byte printing."
-msgstr ""
+msgstr "E675: 沒有指定多字節打å°çš„默èªå­—åž‹"
#: ../hardcopy.c:2426
msgid "E324: Can't open PostScript output file"
@@ -3267,6 +3265,7 @@ msgstr "%-5s: %-30s (用法: %s)"
#: ../if_cscope.c:1155
msgid ""
"\n"
+" a: Find assignments to this symbol\n"
" c: Find functions calling this function\n"
" d: Find functions called by this function\n"
" e: Find this egrep pattern\n"
@@ -3276,6 +3275,16 @@ msgid ""
" s: Find this C symbol\n"
" t: Find this text string\n"
msgstr ""
+"\n"
+" a: æœç´¢å°æ­¤ç¬¦è™Ÿçš„賦值\n"
+" c: æœç´¢èª¿ç”¨æ­¤å‡½å¼çš„函å¼\n"
+" d: æœç´¢æ­¤å‡½å¼èª¿ç”¨çš„函å¼\n"
+" e: æœç´¢æ­¤ egrep 模å¼\n"
+" f: æœç´¢æ­¤æ–‡ä»¶\n"
+" g: æœç´¢æ­¤å®šç¾©\n"
+" i: æœç´¢åŒ…嫿­¤æ–‡ä»¶çš„æ–‡ä»¶\n"
+" s: æœç´¢æ­¤ C 符å·\n"
+" t: æœç´¢æ­¤æ–‡æœ¬å­—串\n"
#: ../if_cscope.c:1226
msgid "E568: duplicate cscope database not added"
@@ -3509,7 +3518,7 @@ msgstr "-N\t\t\t'nocompatible' ä¸å®Œå…¨èˆ‡å‚³çµ± Vi 相容,å¯ä½¿ç”¨ Vim 加å
#: ../main.c:2215
msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
-msgstr ""
+msgstr "-V[N][fname]\t\t詳細 [level N] [log messages to fname]"
#: ../main.c:2216
msgid "-D\t\t\tDebugging mode"
@@ -3602,7 +3611,7 @@ msgstr "-W <scriptout>\tå°æª”案 <scriptout> 寫入所有輸入的命令"
#: ../main.c:2240
msgid "--startuptime <file>\tWrite startup timing messages to <file>"
-msgstr ""
+msgstr "--startuptime <file>\t將啟動時間寫入到文件 <file>"
#: ../main.c:2242
msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
@@ -3794,7 +3803,7 @@ msgstr ""
#: ../memline.c:945
msgid " has been damaged (page size is smaller than minimum value).\n"
-msgstr ""
+msgstr "å·²æŸå(é é¢å¤§å°å°æ–¼æœ€å°å€¼ï¼‰ã€‚\n"
#: ../memline.c:974
#, c-format
@@ -4082,7 +4091,7 @@ msgstr "E317: 指標å€å¡Š id 錯 2"
#: ../memline.c:3070
#, c-format
msgid "E773: Symlink loop for \"%s\""
-msgstr ""
+msgstr "E773: \"%s\" ç¬¦è™ŸéˆæŽ¥å‡ºç¾å¾ªç’°"
#: ../memline.c:3221
msgid "E325: ATTENTION"
@@ -4231,7 +4240,7 @@ msgstr "E329: 沒有那樣的é¸å–®"
#. Only a mnemonic or accelerator is not valid.
#: ../menu.c:329
msgid "E792: Empty menu name"
-msgstr ""
+msgstr "E792: 空的èœå–®å稱"
#: ../menu.c:340
msgid "E330: Menu path must not lead to a sub-menu"
@@ -4312,7 +4321,7 @@ msgstr "-- 尚有 --"
#: ../message.c:2398
msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr ""
+msgstr " 空格/d/j: å±å¹•/é /行 下翻,b/u/k: 上翻,q: 退出 "
#: ../message.c:3021 ../message.c:3031
msgid "Question"
@@ -4357,7 +4366,7 @@ msgstr "E116: å‡½å¼ %s çš„å¼•æ•¸ä¸æ­£ç¢º"
#: ../message.c:3119
msgid "E807: Expected Float argument for printf()"
-msgstr ""
+msgstr "E807: 期盼浮點數作為printf()åƒæ•¸"
#: ../message.c:3873
#, fuzzy
@@ -4370,11 +4379,11 @@ msgstr "W10: 注æ„: 你正在修改一個唯讀檔"
#: ../misc1.c:2537
msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr ""
+msgstr "請輸入數字並<Enter>æˆ–é»žæ“Šé¼ æ¨™ï¼ˆç©ºç™½å–æ¶ˆï¼‰: "
#: ../misc1.c:2539
msgid "Type number and <Enter> (empty cancels): "
-msgstr ""
+msgstr "è«‹é¸æ“‡æ•¸å­—並(<Enter> å–æ¶ˆ): "
#: ../misc1.c:2585
msgid "1 more line"
@@ -4400,7 +4409,7 @@ msgstr " (已中斷)"
#: ../misc1.c:2635
msgid "Beep!"
-msgstr ""
+msgstr "Beep!"
#: ../misc2.c:738
#, c-format
@@ -4617,7 +4626,7 @@ msgstr "E520: ä¸èƒ½åœ¨ Modeline 裡出ç¾"
#: ../option.c:2815
msgid "E846: Key code not set"
-msgstr ""
+msgstr "E846: 未設置éµä½ä»£ç¢¼"
#: ../option.c:2924
msgid "E521: Number required after ="
@@ -4642,11 +4651,11 @@ msgstr "E589: 'backupext' 跟 'patchmode' 是一樣的"
#: ../option.c:3964
msgid "E834: Conflicts with value of 'listchars'"
-msgstr ""
+msgstr "E834: 與'listchars'中的值發生è¡çª"
#: ../option.c:3966
msgid "E835: Conflicts with value of 'fillchars'"
-msgstr ""
+msgstr "E835: 與'fillchars'中的值發生è¡çª"
#: ../option.c:4163
msgid "E524: Missing colon"
@@ -4884,7 +4893,7 @@ msgstr "E382: 無法寫入,'buftype' é¸é …已設定"
#: ../quickfix.c:2812
msgid "E683: File name missing or invalid pattern"
-msgstr ""
+msgstr "E683: ç¼ºå°‘æ–‡ä»¶åæˆ–模å¼ç„¡æ•ˆ"
#: ../quickfix.c:2911
#, fuzzy, c-format
@@ -5022,25 +5031,26 @@ msgid ""
"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
"used "
msgstr ""
+"E864: \\%#= 後é¢åªèƒ½æ˜¯0,1,或者2。自動引擎將會被使用"
#: ../regexp_nfa.c:239
msgid "E865: (NFA) Regexp end encountered prematurely"
-msgstr ""
+msgstr "E865: (NFA) éŽæ—©çš„é‡åˆ°äº†æ­£å‰‡è¡¨é”å¼çš„çµå°¾"
#: ../regexp_nfa.c:240
#, c-format
msgid "E866: (NFA regexp) Misplaced %c"
-msgstr ""
+msgstr "E866: (NFA regexp) %c 放錯了ä½ç½®"
#: ../regexp_nfa.c:242
#, c-format
msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr ""
+msgstr "E877: (NFA regexp) ä¸å¯ç”¨çš„字元類: %<PRId64>"
#: ../regexp_nfa.c:1261
#, c-format
msgid "E867: (NFA) Unknown operator '\\z%c'"
-msgstr ""
+msgstr "E867: (NFA) 未知的æ“作符 '\\z%c'"
#: ../regexp_nfa.c:1387
#, c-format
@@ -5050,21 +5060,21 @@ msgstr ""
#: ../regexp_nfa.c:1802
#, c-format
msgid "E869: (NFA) Unknown operator '\\@%c'"
-msgstr ""
+msgstr "E869: (NFA) 未知的æ“作符 '\\%%%c'"
#: ../regexp_nfa.c:1831
msgid "E870: (NFA regexp) Error reading repetition limits"
-msgstr ""
+msgstr "E870: (NFA regexp) 读å–é‡å¤é™åˆ¶æ—¶å‡ºé”™"
#. Can't have a multi follow a multi.
#: ../regexp_nfa.c:1895
msgid "E871: (NFA regexp) Can't have a multi follow a multi !"
-msgstr ""
+msgstr "E871: (NFA regexp) ä¸èƒ½å¤šä¸ªè·Ÿå¤šä¸ªï¼"
#. Too many `('
#: ../regexp_nfa.c:2037
msgid "E872: (NFA regexp) Too many '('"
-msgstr ""
+msgstr "E872: (NFA regexp) 太多 '('"
#: ../regexp_nfa.c:2042
#, fuzzy
@@ -5073,31 +5083,32 @@ msgstr "E50: 太多 \\z("
#: ../regexp_nfa.c:2066
msgid "E873: (NFA regexp) proper termination error"
-msgstr ""
+msgstr "E873: (NFA regexp) 未é©ç•¶çµ‚æ­¢"
#: ../regexp_nfa.c:2599
msgid "E874: (NFA) Could not pop the stack !"
-msgstr ""
+msgstr "E874: (NFA) 無法出棧ï¼"
#: ../regexp_nfa.c:3298
msgid ""
"E875: (NFA regexp) (While converting from postfix to NFA), too many states "
"left on stack"
-msgstr ""
+msgstr "E875: (NFA regexp) (從後綴轉到 NFA 时),棧上éºç•™äº†å¤ªå¤šç‹€æ…‹"
#: ../regexp_nfa.c:3302
msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr ""
+msgstr "E876: (NFA regexp) 沒有足夠的空間存儲NFA "
#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869
msgid ""
"Could not open temporary log file for writing, displaying on stderr ... "
msgstr ""
+"無法打開臨時日志文件進行寫入,顯示在stderr中..."
#: ../regexp_nfa.c:4840
#, c-format
msgid "(NFA) COULD NOT OPEN %s !"
-msgstr ""
+msgstr "(NFA) ä¸èƒ½æ‰“å¼€ %s !"
#: ../regexp_nfa.c:6049
#, fuzzy
@@ -5270,17 +5281,17 @@ msgstr "E297: 暫存檔寫入錯誤"
#: ../spell.c:952
msgid "E758: Truncated spell file"
-msgstr ""
+msgstr "E758: 已截斷的拼寫文件"
#: ../spell.c:953
#, c-format
msgid "Trailing text in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,多餘的後續文本: %s"
#: ../spell.c:954
#, c-format
msgid "Affix name too long in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,附加項å字太長: %s"
#: ../spell.c:955
#, fuzzy
@@ -5289,20 +5300,20 @@ msgstr "E431: Tag 檔 \"%s\" æ ¼å¼éŒ¯èª¤"
#: ../spell.c:957
msgid "E762: Character in FOL, LOW or UPP is out of range"
-msgstr ""
+msgstr "E762: FOLã€LOW 或 UPP 中字元超出範åœ"
#: ../spell.c:958
msgid "Compressing word tree..."
-msgstr ""
+msgstr "壓縮單詞樹……"
#: ../spell.c:1951
msgid "E756: Spell checking is not enabled"
-msgstr ""
+msgstr "E756: 拼寫檢查未啟用"
#: ../spell.c:2249
#, c-format
msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr ""
+msgstr "警告: 找ä¸åˆ°å–®è©žåˆ—表 \"%s.%s.spl\" or \"%s.ascii.spl\""
#: ../spell.c:2473
#, fuzzy, c-format
@@ -5316,11 +5327,11 @@ msgstr "E307: %s 看起來ä¸åƒæ˜¯ Vim 暫存檔"
#: ../spell.c:2501
msgid "E771: Old spell file, needs to be updated"
-msgstr ""
+msgstr "E771: èˆŠçš„æ‹¼å¯«æ–‡ä»¶ï¼Œéœ€è¦æ›´æ–°"
#: ../spell.c:2504
msgid "E772: Spell file is for newer version of Vim"
-msgstr ""
+msgstr "E772: 為更高版本的 Vim 所使用的拼寫文件"
#: ../spell.c:2602
#, fuzzy
@@ -5340,66 +5351,68 @@ msgstr "æœå°‹ tag 檔案 \"%s\""
#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
#, c-format
msgid "Conversion failure for word in %s line %d: %s"
-msgstr ""
+msgstr "單詞 %s 轉æ›å¤±æ•—,第 %d 行: %s"
#: ../spell.c:4630 ../spell.c:6170
#, c-format
msgid "Conversion in %s not supported: from %s to %s"
-msgstr ""
+msgstr "䏿”¯æŒ %s 中的轉æ›: 从 %s 到 %s"
#: ../spell.c:4642
#, c-format
msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,FLAG 的值无效: %s"
#: ../spell.c:4655
#, c-format
msgid "FLAG after using flags in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d è¡Œï¼Œåœ¨ä½¿ç”¨æ¨™å¿—å¾Œå‡ºç¾ FLAG: %s"
#: ../spell.c:4723
#, c-format
msgid ""
"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
"%d"
-msgstr ""
+msgstr "在 PFX 項之後定義 COMPOUNDFORBIDFLAG (%s 第%d行)å¯èƒ½æœƒçµ¦å‡ºçš„éŒ¯èª¤çµæžœ"
+"%d"
#: ../spell.c:4731
#, c-format
msgid ""
"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
"%d"
-msgstr ""
+msgstr "在 PFX 項之後定義 COMPOUNDFORBIDFLAG (%s 第%d行)å¯èƒ½æœƒçµ¦å‡ºçš„éŒ¯èª¤çµæžœ"
+"%d"
#: ../spell.c:4747
#, c-format
msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDMIN 值: %s"
#: ../spell.c:4771
#, c-format
msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDWORDMAX 值: %s"
#: ../spell.c:4777
#, c-format
msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDMIN 值: %s"
#: ../spell.c:4783
#, c-format
msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 COMPOUNDSYLMAX 值: %s"
#: ../spell.c:4795
#, c-format
msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,错误的 CHECKCOMPOUNDPATTERN 值: %s"
#: ../spell.c:4847
#, c-format
msgid "Different combining flag in continued affix block in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,在連續的附加塊種出ç¾ä¸åŒçš„çµ„åˆæ¨™èªŒ: %s"
#: ../spell.c:4850
#, fuzzy, c-format
@@ -5412,45 +5425,47 @@ msgid ""
"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
"line %d: %s"
msgstr ""
+"%s 第 %d 行,附加項被 BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST 使"
+"用: %s"
#: ../spell.c:4893
#, c-format
msgid "Expected Y or N in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d è¡Œï¼Œæ­¤è™•éœ€è¦ Y 或 N: %s"
#: ../spell.c:4968
#, c-format
msgid "Broken condition in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,錯誤的æ¢ä»¶: %s"
#: ../spell.c:5091
#, c-format
msgid "Expected REP(SAL) count in %s line %d"
-msgstr ""
+msgstr "%s 第 %d è¡Œï¼Œæ­¤è™•éœ€è¦ REP(SAL) 計數"
#: ../spell.c:5120
#, c-format
msgid "Expected MAP count in %s line %d"
-msgstr ""
+msgstr "%s 第 %d è¡Œï¼Œæ­¤è™•éœ€è¦ MAP 計數"
#: ../spell.c:5132
#, c-format
msgid "Duplicate character in MAP in %s line %d"
-msgstr ""
+msgstr "%s 第 %d 行,MAP 中存在é‡è¤‡çš„å­—å…ƒ"
#: ../spell.c:5176
#, c-format
msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,無法識別或é‡è¤‡çš„é …: %s"
#: ../spell.c:5197
#, c-format
msgid "Missing FOL/LOW/UPP line in %s"
-msgstr ""
+msgstr "%s 中缺少 FOL/LOW/UPP 行"
#: ../spell.c:5220
msgid "COMPOUNDSYLMAX used without SYLLABLE"
-msgstr ""
+msgstr "在没有 SYLLABLE 的情æ³ä¸‹ä½¿ç”¨äº† COMPOUNDSYLMAX"
#: ../spell.c:5236
#, fuzzy
@@ -5464,32 +5479,32 @@ msgstr "å¤ªå¤šç·¨è¼¯åƒæ•¸"
#: ../spell.c:5240
msgid "Too many postponed prefixes and/or compound flags"
-msgstr ""
+msgstr "太多延é²å‰ç¶´å’Œ/æˆ–çµ„åˆæ¨™èªŒ"
#: ../spell.c:5250
#, c-format
msgid "Missing SOFO%s line in %s"
-msgstr ""
+msgstr "%s 中缺少 SOFO%s 行"
#: ../spell.c:5253
#, c-format
msgid "Both SAL and SOFO lines in %s"
-msgstr ""
+msgstr "%s åŒæ™‚å‡ºç¾ SAL å’Œ SOFO 行"
#: ../spell.c:5331
#, c-format
msgid "Flag is not a number in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d è¡Œï¼Œæ¨™èªŒä¸æ˜¯æ•¸å­—: %s"
#: ../spell.c:5334
#, c-format
msgid "Illegal flag in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,無效的標誌: %s"
#: ../spell.c:5493 ../spell.c:5501
#, c-format
msgid "%s value differs from what is used in another .aff file"
-msgstr ""
+msgstr "%s 的值與å¦ä¸€å€‹ .aff 文件中使用的值ä¸ç›¸åŒ"
#: ../spell.c:5602
#, fuzzy, c-format
@@ -5499,12 +5514,12 @@ msgstr "掃瞄字典: %s"
#: ../spell.c:5611
#, c-format
msgid "E760: No word count in %s"
-msgstr ""
+msgstr "E760: %s 中没有單詞計數"
#: ../spell.c:5669
#, c-format
msgid "line %6d, word %6d - %s"
-msgstr ""
+msgstr "第 %6d 行,第 %6d 个單詞 - %s"
#: ../spell.c:5691
#, fuzzy, c-format
@@ -5514,17 +5529,17 @@ msgstr "æ¯ä¸€è¡Œéƒ½æ‰¾ä¸åˆ°: %s"
#: ../spell.c:5694
#, c-format
msgid "First duplicate word in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,首次出ç¾é‡è¤‡çš„單詞: %s"
#: ../spell.c:5746
#, c-format
msgid "%d duplicate word(s) in %s"
-msgstr ""
+msgstr "存在 %d 个é‡è¤‡çš„單詞,在 %s 中"
#: ../spell.c:5748
#, c-format
msgid "Ignored %d word(s) with non-ASCII characters in %s"
-msgstr ""
+msgstr "å¿½ç•¥äº†å«æœ‰éž ASCII 字元的 %d 个單詞,在 %s 中"
#: ../spell.c:6115
#, fuzzy, c-format
@@ -5534,42 +5549,42 @@ msgstr "從標準輸入讀å–..."
#: ../spell.c:6155
#, c-format
msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %ld 行,é‡å¤çš„ /encoding= 行已被忽略: %s"
#: ../spell.c:6159
#, c-format
msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,å•è¯åŽçš„ /encoding= 行已被忽略: %s"
#: ../spell.c:6180
#, c-format
msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,é‡å¤çš„ /regions= 行已被忽略: %s"
#: ../spell.c:6185
#, c-format
msgid "Too many regions in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,太多å€åŸŸ: %s"
#: ../spell.c:6198
#, c-format
msgid "/ line ignored in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,/ 行已被忽略: %s"
#: ../spell.c:6224
#, fuzzy, c-format
msgid "Invalid region nr in %s line %d: %s"
-msgstr "E573: 䏿­£ç¢ºçš„伺æœå™¨ id : %s"
+msgstr "%s 第 %d 行,無效的å€åŸŸè™Ÿ: %s"
#: ../spell.c:6230
#, c-format
msgid "Unrecognized flags in %s line %d: %s"
-msgstr ""
+msgstr "%s 第 %d 行,ä¸å¯è­˜åˆ¥çš„æ¨™èªŒ: %s"
#: ../spell.c:6257
#, c-format
msgid "Ignored %d words with non-ASCII characters"
-msgstr ""
+msgstr "å¿½ç•¥äº†å«æœ‰éž ASCII 字元的 %d 个單詞"
#: ../spell.c:6656
#, c-format
@@ -5578,23 +5593,23 @@ msgstr ""
#: ../spell.c:7340
msgid "Reading back spell file..."
-msgstr ""
+msgstr "è¯»å–æ‹¼å¯«æ–‡ä»¶â€¦â€¦"
#. Go through the trie of good words, soundfold each word and add it to
#. the soundfold trie.
#: ../spell.c:7357
msgid "Performing soundfolding..."
-msgstr ""
+msgstr "正在 soundfolding……"
#: ../spell.c:7368
#, c-format
msgid "Number of words after soundfolding: %<PRId64>"
-msgstr ""
+msgstr "soundfolding åŽçš„單詞数: %<PRId64>"
#: ../spell.c:7476
#, c-format
msgid "Total number of words: %d"
-msgstr ""
+msgstr "單詞总数: %d"
#: ../spell.c:7655
#, fuzzy, c-format
@@ -5604,11 +5619,11 @@ msgstr "寫入 viminfo 檔案 \"%s\" 中"
#: ../spell.c:7707 ../spell.c:7927
#, c-format
msgid "Estimated runtime memory use: %d bytes"
-msgstr ""
+msgstr "估計é‹è¡Œæ™‚的內存用é‡: %d ä½å…ƒ"
#: ../spell.c:7820
msgid "E751: Output file name must not have region name"
-msgstr ""
+msgstr "E751: 輸出文件ä¸èƒ½å«æœ‰å€åŸŸå"
#: ../spell.c:7822
#, fuzzy
@@ -5622,7 +5637,7 @@ msgstr "E15: 䏿­£ç¢ºçš„é‹ç®—å¼: %s"
#: ../spell.c:7907
msgid "Warning: both compounding and NOBREAK specified"
-msgstr ""
+msgstr "警告: åŒæ™‚指定了 compounding å’Œ NOBREAK"
#: ../spell.c:7920
#, fuzzy, c-format
@@ -5631,30 +5646,30 @@ msgstr "寫入 viminfo 檔案 \"%s\" 中"
#: ../spell.c:7925
msgid "Done!"
-msgstr ""
+msgstr "完æˆï¼"
#: ../spell.c:8034
#, c-format
msgid "E765: 'spellfile' does not have %<PRId64> entries"
-msgstr ""
+msgstr "E765: 'spellfile' 没有 %<PRId64> 項"
#: ../spell.c:8074
#, c-format
msgid "Word '%.*s' removed from %s"
-msgstr ""
+msgstr "从 %s 中删除了單詞"
#: ../spell.c:8117
#, c-format
msgid "Word '%.*s' added to %s"
-msgstr ""
+msgstr "å‘ %s 中添加了單詞"
#: ../spell.c:8381
msgid "E763: Word characters differ between spell files"
-msgstr ""
+msgstr "E763: 拼寫文件之間的字元ä¸ç›¸åŒ"
#: ../spell.c:8684
msgid "Sorry, no suggestions"
-msgstr ""
+msgstr "抱歉,没有建议"
#: ../spell.c:8687
#, fuzzy, c-format
@@ -5671,7 +5686,7 @@ msgstr "將變動存儲至 \"%.*s\"?"
#: ../spell.c:8737
#, c-format
msgid " < \"%.*s\""
-msgstr ""
+msgstr " < \"%.*s\""
#: ../spell.c:8882
#, fuzzy
@@ -5691,28 +5706,28 @@ msgstr "E307: %s 看起來ä¸åƒæ˜¯ Vim 暫存檔"
#: ../spell.c:9282
#, c-format
msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr ""
+msgstr "E779: 舊的.sug æ–‡ä»¶ï¼Œéœ€è¦æ›´æ–°: %s"
#: ../spell.c:9286
#, c-format
msgid "E780: .sug file is for newer version of Vim: %s"
-msgstr ""
+msgstr "E780: .sug 文件é©ç”¨æ–¼è¼ƒæ–°çš„ Vim 版本: %s"
#: ../spell.c:9295
#, c-format
msgid "E781: .sug file doesn't match .spl file: %s"
-msgstr ""
+msgstr "E781: .sug 文件ä¸èƒ½åŒ¹é… .spl 文件: %s"
#: ../spell.c:9305
#, fuzzy, c-format
msgid "E782: error while reading .sug file: %s"
-msgstr "E47: 讀å–錯誤檔案失敗"
+msgstr "E782: 當讀å–.sug 文件時錯誤"
#. This should have been checked when generating the .spl
#. file.
#: ../spell.c:11575
msgid "E783: duplicate char in MAP entry"
-msgstr ""
+msgstr "E783: MAP æ¢ç›®ä¸­æœ‰é‡è¤‡çš„å­—å…ƒ"
#: ../syntax.c:266
msgid "No Syntax items defined for this buffer"
@@ -5894,10 +5909,11 @@ msgstr "E410: 䏿­£ç¢ºçš„ :syntax å­å‘½ä»¤: %s"
msgid ""
" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
msgstr ""
+" 總 計 計 數 匹 é… æœ€ æ…¢ çš„ å¹³ å‡ å å­— 模 å¼"
#: ../syntax.c:6146
msgid "E679: recursive loop loading syncolor.vim"
-msgstr ""
+msgstr "E679: 加載 syncolor.vim 时出ç¾åµŒå¥—循環"
#: ../syntax.c:6256
#, c-format
@@ -5969,13 +5985,13 @@ msgstr "E424: 使用了éŽå¤šç›¸ç•°çš„高亮度屬性"
msgid "E669: Unprintable character in group name"
msgstr "E669: 群組å稱中有無法列å°çš„å­—å…ƒ"
-#: ../syntax.c:7434
-msgid "W18: Invalid character in group name"
-msgstr "W18: 群組åç¨±ä¸­æœ‰ä¸æ­£ç¢ºçš„å­—å…ƒ"
+#: ../highlight_group.c:1756
+msgid "E5248: Invalid character in group name"
+msgstr "E5248: 群組åç¨±ä¸­æœ‰ä¸æ­£ç¢ºçš„å­—å…ƒ"
#: ../syntax.c:7448
msgid "E849: Too many highlight and syntax groups"
-msgstr ""
+msgstr "E849: 高亮和語法組éŽå¤š"
#: ../tag.c:104
msgid "E555: at bottom of tag stack"
@@ -6083,7 +6099,7 @@ msgstr "E435: 找ä¸åˆ° tag, 用猜的!"
#: ../tag.c:2797
#, c-format
msgid "Duplicate field name: %s"
-msgstr ""
+msgstr "é‡è¤‡çš„字段å: %s"
#: ../term.c:1442
msgid "' not known. Available builtin terminals are:"
@@ -6131,35 +6147,35 @@ msgstr "Vim: 讀å–輸入錯誤,離開中...\n"
#. * file in a way it becomes shorter.
#: ../undo.c:379
msgid "E881: Line count changed unexpectedly"
-msgstr ""
+msgstr "E881: 行數æ„外地改變了"
#: ../undo.c:627
-#, fuzzy, c-format
+#, c-format
msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: 無法以寫入模å¼é–‹å•Ÿ"
+msgstr "E828: 無法打開撤銷文件去寫入"
#: ../undo.c:717
#, c-format
msgid "E825: Corrupted undo file (%s): %s"
-msgstr ""
+msgstr "E825: å·²æå£žçš„æ’¤éŠ·æ–‡ä»¶ (%s): %s"
#: ../undo.c:1039
msgid "Cannot write undo file in any directory in 'undodir'"
-msgstr ""
+msgstr "ä¸èƒ½å¯«å…¥æ’¤éŠ·æ–‡ä»¶åˆ° 'undodir' 中的任何文件夾"
#: ../undo.c:1074
#, c-format
msgid "Will not overwrite with undo file, cannot read: %s"
-msgstr ""
+msgstr "ä¸èƒ½å¯«å…¥æ’¤éŠ·æ–‡ä»¶ï¼Œä¸å¯è®€å–: %s"
#: ../undo.c:1092
#, c-format
msgid "Will not overwrite, this is not an undo file: %s"
-msgstr ""
+msgstr "䏿œƒè¦†è“‹ï¼Œé€™ä¸æ˜¯æ’¤éŠ·æ–‡ä»¶: %s"
#: ../undo.c:1108
msgid "Skipping undo file write, nothing to undo"
-msgstr ""
+msgstr "è·³éŽæ’¤æ¶ˆæ–‡ä»¶å¯«å…¥ï¼Œæ²’æœ‰å¯æ’¤æ¶ˆçš„內容"
#: ../undo.c:1121
#, fuzzy, c-format
@@ -6174,7 +6190,7 @@ msgstr "E297: 暫存檔寫入錯誤"
#: ../undo.c:1280
#, c-format
msgid "Not reading undo file, owner differs: %s"
-msgstr ""
+msgstr "ä¸èƒ½è®€å–æ’¤éŠ·æ–‡ä»¶ï¼Œæ“æœ‰è€…ä¸åŒ: %s"
#: ../undo.c:1292
#, fuzzy, c-format
@@ -6198,7 +6214,7 @@ msgstr "E484: 無法開啟檔案 %s"
#: ../undo.c:1328
msgid "File contents changed, cannot use undo info"
-msgstr ""
+msgstr "文件內容已經改變,ä¸èƒ½ä½¿ç”¨æ’¤éŠ·ä¿¡æ¯"
#: ../undo.c:1497
#, fuzzy, c-format
@@ -6207,11 +6223,11 @@ msgstr "çµæŸåŸ·è¡Œ %s"
#: ../undo.c:1586 ../undo.c:1812
msgid "Already at oldest change"
-msgstr ""
+msgstr "已經在最早的改變"
#: ../undo.c:1597 ../undo.c:1814
msgid "Already at newest change"
-msgstr ""
+msgstr "已經在最新的改變"
#: ../undo.c:1806
#, fuzzy, c-format
@@ -6259,11 +6275,11 @@ msgstr "%<PRId64> 行 %s éŽ %d 次"
#: ../undo.c:2228
msgid "before"
-msgstr ""
+msgstr "之å‰"
#: ../undo.c:2228
msgid "after"
-msgstr ""
+msgstr "之後"
#: ../undo.c:2325
#, fuzzy
@@ -6272,7 +6288,7 @@ msgstr "沒有這個 mapping å°æ‡‰"
#: ../undo.c:2330
msgid "number changes when saved"
-msgstr ""
+msgstr " 編號 改變 時間 ä¿å­˜"
#: ../undo.c:2360
#, fuzzy, c-format
diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmenu.c
index 746429e5d5..0d9080ceb7 100644
--- a/src/nvim/popupmnu.c
+++ b/src/nvim/popupmenu.c
@@ -1,7 +1,7 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/// @file popupmnu.c
+/// @file popupmenu.c
///
/// Popup menu (PUM)
@@ -13,17 +13,19 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
#include "nvim/insexpand.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/menu.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -55,7 +57,7 @@ static bool pum_external = false;
static bool pum_invalid = false; // the screen was just cleared
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "popupmnu.c.generated.h"
+# include "popupmenu.c.generated.h"
#endif
#define PUM_DEF_HEIGHT 10
@@ -154,7 +156,6 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
if (pum_external) {
if (array_changed) {
Arena arena = ARENA_EMPTY;
- arena_start(&arena, &ui_ext_fixblk);
Array arr = arena_array(&arena, (size_t)size);
for (int i = 0; i < size; i++) {
Array item = arena_array(&arena, 4);
@@ -166,7 +167,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col,
pum_anchor_grid);
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
} else {
ui_call_popupmenu_select(selected);
return;
@@ -463,7 +464,7 @@ void pum_redraw(void)
/ (pum_size - pum_height);
}
- for (i = 0; i < pum_height; ++i) {
+ for (i = 0; i < pum_height; i++) {
idx = i + pum_first;
attr = (idx == pum_selected) ? attr_select : attr_norm;
@@ -483,7 +484,7 @@ void pum_redraw(void)
grid_col = col_off;
totwidth = 0;
- for (round = 1; round <= 3; ++round) {
+ for (round = 1; round <= 3; round++) {
width = 0;
s = NULL;
@@ -523,7 +524,7 @@ void pum_redraw(void)
}
if (pum_rl) {
- char *rt = (char *)reverse_text(st);
+ char *rt = reverse_text((char *)st);
char *rt_start = rt;
int size = vim_strsize(rt);
@@ -541,14 +542,13 @@ void pum_redraw(void)
size++;
}
}
- grid_puts_len(&pum_grid, (char_u *)rt, (int)STRLEN(rt), row,
- grid_col - size + 1, attr);
+ grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row, grid_col - size + 1, attr);
xfree(rt_start);
xfree(st);
grid_col -= width;
} else {
// use grid_puts_len() to truncate the text
- grid_puts(&pum_grid, st, row, grid_col, attr);
+ grid_puts(&pum_grid, (char *)st, row, grid_col, attr);
xfree(st);
grid_col += width;
}
@@ -559,11 +559,11 @@ void pum_redraw(void)
// Display two spaces for a Tab.
if (pum_rl) {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col - 1,
+ grid_puts_len(&pum_grid, " ", 2, row, grid_col - 1,
attr);
grid_col -= 2;
} else {
- grid_puts_len(&pum_grid, (char_u *)" ", 2, row, grid_col, attr);
+ grid_puts_len(&pum_grid, " ", 2, row, grid_col, attr);
grid_col += 2;
}
totwidth += 2;
@@ -639,11 +639,11 @@ void pum_redraw(void)
/// @param n
/// @param repeat
///
-/// @returns TRUE when the window was resized and the location of the popup
+/// @returns true when the window was resized and the location of the popup
/// menu must be recomputed.
-static int pum_set_selected(int n, int repeat)
+static bool pum_set_selected(int n, int repeat)
{
- int resized = FALSE;
+ int resized = false;
int context = pum_height / 2;
pum_selected = n;
@@ -702,7 +702,7 @@ static int pum_set_selected(int n, int repeat)
if ((pum_array[pum_selected].pum_info != NULL)
&& (Rows > 10)
&& (repeat <= 1)
- && (vim_strchr((char *)p_cot, 'p') != NULL)) {
+ && (vim_strchr(p_cot, 'p') != NULL)) {
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
int res = OK;
@@ -742,11 +742,11 @@ static int pum_set_selected(int n, int repeat)
if (res == OK) {
// Edit a new, empty buffer. Set options for a "wipeout"
// buffer.
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
- set_option_value("bl", 0L, NULL, OPT_LOCAL);
- set_option_value("bt", 0L, "nofile", OPT_LOCAL);
- set_option_value("bh", 0L, "wipe", OPT_LOCAL);
- set_option_value("diff", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bl", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bt", 0L, "nofile", OPT_LOCAL);
+ set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL);
+ set_option_value_give_err("diff", 0L, NULL, OPT_LOCAL);
}
}
@@ -776,12 +776,12 @@ static int pum_set_selected(int n, int repeat)
if (curwin->w_height < lnum) {
win_setheight((int)lnum);
- resized = TRUE;
+ resized = true;
}
}
curbuf->b_changed = false;
- curbuf->b_p_ma = FALSE;
+ curbuf->b_p_ma = false;
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
@@ -795,12 +795,12 @@ static int pum_set_selected(int n, int repeat)
// window is not resized, skip the preview window's
// status line redrawing.
if (ins_compl_active() && !resized) {
- curwin->w_redr_status = FALSE;
+ curwin->w_redr_status = false;
}
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, SOME_VALID);
+ redraw_later(curwin, UPD_SOME_VALID);
// When the preview window was resized we need to
// update the view on the buffer. Only go back to
@@ -904,7 +904,7 @@ void pum_recompose(void)
/// Gets the height of the menu.
///
/// @return the height of the popup menu, the number of entries visible.
-/// Only valid when pum_visible() returns TRUE!
+/// Only valid when pum_visible() returns true!
int pum_get_height(void)
{
if (pum_external) {
@@ -999,7 +999,7 @@ static void pum_execute_menu(vimmenu_T *menu, int mode)
for (vimmenu_T *mp = menu->children; mp != NULL; mp = mp->next) {
if ((mp->modes & mp->enabled & mode) && idx++ == pum_selected) {
- memset(&ea, 0, sizeof(ea));
+ CLEAR_FIELD(ea);
execute_menu(&ea, mp, -1);
break;
}
diff --git a/src/nvim/popupmnu.h b/src/nvim/popupmenu.h
index 7d3f4c6f51..851ad31486 100644
--- a/src/nvim/popupmnu.h
+++ b/src/nvim/popupmenu.h
@@ -1,5 +1,5 @@
-#ifndef NVIM_POPUPMNU_H
-#define NVIM_POPUPMNU_H
+#ifndef NVIM_POPUPMENU_H
+#define NVIM_POPUPMENU_H
#include "nvim/grid_defs.h"
#include "nvim/macros.h"
@@ -17,6 +17,6 @@ typedef struct {
EXTERN ScreenGrid pum_grid INIT(= SCREEN_GRID_INIT);
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "popupmnu.h.generated.h"
+# include "popupmenu.h.generated.h"
#endif
-#endif // NVIM_POPUPMNU_H
+#endif // NVIM_POPUPMENU_H
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index fe7bd2e912..cded231c85 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -6,16 +6,32 @@
#include <stdio.h>
#include "nvim/assert.h"
+#include "nvim/charset.h"
+#include "nvim/debugger.h"
+#include "nvim/eval.h"
+#include "nvim/eval/userfunc.h"
+#include "nvim/fileio.h"
#include "nvim/func_attr.h"
#include "nvim/globals.h" // for the global `time_fd` (startuptime)
-#include "nvim/os/os_defs.h"
+#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/profile.h"
+#include "nvim/runtime.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "profile.c.generated.h"
#endif
+/// Struct used in sn_prl_ga for every line of a script.
+typedef struct sn_prl_S {
+ int snp_count; ///< nr of times line was executed
+ proftime_T sn_prl_total; ///< time spent in a line + children
+ proftime_T sn_prl_self; ///< time spent in a line itself
+} sn_prl_T;
+
+#define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
+
static proftime_T prof_wait_time;
/// Gets the current time.
@@ -195,6 +211,652 @@ int profile_cmp(proftime_T tm1, proftime_T tm2) FUNC_ATTR_CONST
return profile_signed(tm2 - tm1) < 0 ? -1 : 1;
}
+static char *profile_fname = NULL;
+
+/// Reset all profiling information.
+void profile_reset(void)
+{
+ // Reset sourced files.
+ for (int id = 1; id <= script_items.ga_len; id++) {
+ scriptitem_T *si = &SCRIPT_ITEM(id);
+ if (si->sn_prof_on) {
+ si->sn_prof_on = false;
+ si->sn_pr_force = false;
+ si->sn_pr_child = profile_zero();
+ si->sn_pr_nest = 0;
+ si->sn_pr_count = 0;
+ si->sn_pr_total = profile_zero();
+ si->sn_pr_self = profile_zero();
+ si->sn_pr_start = profile_zero();
+ si->sn_pr_children = profile_zero();
+ ga_clear(&si->sn_prl_ga);
+ si->sn_prl_start = profile_zero();
+ si->sn_prl_children = profile_zero();
+ si->sn_prl_wait = profile_zero();
+ si->sn_prl_idx = -1;
+ si->sn_prl_execed = 0;
+ }
+ }
+
+ // Reset functions.
+ hashtab_T *const functbl = func_tbl_get();
+ size_t todo = functbl->ht_used;
+ hashitem_T *hi = functbl->ht_array;
+
+ for (; todo > (size_t)0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ todo--;
+ ufunc_T *uf = HI2UF(hi);
+ if (uf->uf_prof_initialized) {
+ uf->uf_profiling = 0;
+ uf->uf_tm_count = 0;
+ uf->uf_tm_total = profile_zero();
+ uf->uf_tm_self = profile_zero();
+ uf->uf_tm_children = profile_zero();
+
+ for (int i = 0; i < uf->uf_lines.ga_len; i++) {
+ uf->uf_tml_count[i] = 0;
+ uf->uf_tml_total[i] = uf->uf_tml_self[i] = 0;
+ }
+
+ uf->uf_tml_start = profile_zero();
+ uf->uf_tml_children = profile_zero();
+ uf->uf_tml_wait = profile_zero();
+ uf->uf_tml_idx = -1;
+ uf->uf_tml_execed = 0;
+ }
+ }
+ }
+
+ XFREE_CLEAR(profile_fname);
+}
+
+/// ":profile cmd args"
+void ex_profile(exarg_T *eap)
+{
+ static proftime_T pause_time;
+
+ char *e;
+ int len;
+
+ e = skiptowhite(eap->arg);
+ len = (int)(e - eap->arg);
+ e = skipwhite(e);
+
+ if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) {
+ xfree(profile_fname);
+ profile_fname = (char *)expand_env_save_opt((char_u *)e, true);
+ do_profiling = PROF_YES;
+ profile_set_wait(profile_zero());
+ set_vim_var_nr(VV_PROFILING, 1L);
+ } else if (do_profiling == PROF_NONE) {
+ emsg(_("E750: First use \":profile start {fname}\""));
+ } else if (STRCMP(eap->arg, "stop") == 0) {
+ profile_dump();
+ do_profiling = PROF_NONE;
+ set_vim_var_nr(VV_PROFILING, 0L);
+ profile_reset();
+ } else if (STRCMP(eap->arg, "pause") == 0) {
+ if (do_profiling == PROF_YES) {
+ pause_time = profile_start();
+ }
+ do_profiling = PROF_PAUSED;
+ } else if (STRCMP(eap->arg, "continue") == 0) {
+ if (do_profiling == PROF_PAUSED) {
+ pause_time = profile_end(pause_time);
+ profile_set_wait(profile_add(profile_get_wait(), pause_time));
+ }
+ do_profiling = PROF_YES;
+ } else if (STRCMP(eap->arg, "dump") == 0) {
+ profile_dump();
+ } else {
+ // The rest is similar to ":breakadd".
+ ex_breakadd(eap);
+ }
+}
+
+/// Command line expansion for :profile.
+static enum {
+ PEXP_SUBCMD, ///< expand :profile sub-commands
+ PEXP_FUNC, ///< expand :profile func {funcname}
+} pexpand_what;
+
+static char *pexpand_cmds[] = {
+ "continue",
+ "dump",
+ "file",
+ "func",
+ "pause",
+ "start",
+ "stop",
+ NULL
+};
+
+/// Function given to ExpandGeneric() to obtain the profile command
+/// specific expansion.
+char *get_profile_name(expand_T *xp, int idx)
+ FUNC_ATTR_PURE
+{
+ switch (pexpand_what) {
+ case PEXP_SUBCMD:
+ return pexpand_cmds[idx];
+ // case PEXP_FUNC: TODO
+ default:
+ return NULL;
+ }
+}
+
+/// Handle command line completion for :profile command.
+void set_context_in_profile_cmd(expand_T *xp, const char *arg)
+{
+ // Default: expand subcommands.
+ xp->xp_context = EXPAND_PROFILE;
+ pexpand_what = PEXP_SUBCMD;
+ xp->xp_pattern = (char *)arg;
+
+ char_u *const end_subcmd = (char_u *)skiptowhite(arg);
+ if (*end_subcmd == NUL) {
+ return;
+ }
+
+ if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) {
+ xp->xp_context = EXPAND_FILES;
+ xp->xp_pattern = skipwhite((char *)end_subcmd);
+ return;
+ }
+
+ // TODO(tarruda): expand function names after "func"
+ xp->xp_context = EXPAND_NOTHING;
+}
+
+static proftime_T inchar_time;
+
+/// Called when starting to wait for the user to type a character.
+void prof_inchar_enter(void)
+{
+ inchar_time = profile_start();
+}
+
+/// Called when finished waiting for the user to type a character.
+void prof_inchar_exit(void)
+{
+ inchar_time = profile_end(inchar_time);
+ profile_set_wait(profile_add(profile_get_wait(), inchar_time));
+}
+
+/// @return true when a function defined in the current script should be
+/// profiled.
+bool prof_def_func(void)
+ FUNC_ATTR_PURE
+{
+ if (current_sctx.sc_sid > 0) {
+ return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force;
+ }
+ return false;
+}
+
+/// Print the count and times for one function or function line.
+///
+/// @param prefer_self when equal print only self time
+static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *self,
+ bool prefer_self)
+{
+ if (count > 0) {
+ fprintf(fd, "%5d ", count);
+ if (prefer_self && profile_equal(*total, *self)) {
+ fprintf(fd, " ");
+ } else {
+ fprintf(fd, "%s ", profile_msg(*total));
+ }
+ if (!prefer_self && profile_equal(*total, *self)) {
+ fprintf(fd, " ");
+ } else {
+ fprintf(fd, "%s ", profile_msg(*self));
+ }
+ } else {
+ fprintf(fd, " ");
+ }
+}
+
+/// @param prefer_self when equal print only self time
+static void prof_sort_list(FILE *fd, ufunc_T **sorttab, int st_len, char *title, bool prefer_self)
+{
+ int i;
+ ufunc_T *fp;
+
+ fprintf(fd, "FUNCTIONS SORTED ON %s TIME\n", title);
+ fprintf(fd, "count total (s) self (s) function\n");
+ for (i = 0; i < 20 && i < st_len; i++) {
+ fp = sorttab[i];
+ prof_func_line(fd, fp->uf_tm_count, &fp->uf_tm_total, &fp->uf_tm_self,
+ prefer_self);
+ if (fp->uf_name[0] == K_SPECIAL) {
+ fprintf(fd, " <SNR>%s()\n", fp->uf_name + 3);
+ } else {
+ fprintf(fd, " %s()\n", fp->uf_name);
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+/// Compare function for total time sorting.
+static int prof_total_cmp(const void *s1, const void *s2)
+{
+ ufunc_T *p1 = *(ufunc_T **)s1;
+ ufunc_T *p2 = *(ufunc_T **)s2;
+ return profile_cmp(p1->uf_tm_total, p2->uf_tm_total);
+}
+
+/// Compare function for self time sorting.
+static int prof_self_cmp(const void *s1, const void *s2)
+{
+ ufunc_T *p1 = *(ufunc_T **)s1;
+ ufunc_T *p2 = *(ufunc_T **)s2;
+ return profile_cmp(p1->uf_tm_self, p2->uf_tm_self);
+}
+
+/// Start profiling function "fp".
+void func_do_profile(ufunc_T *fp)
+{
+ int len = fp->uf_lines.ga_len;
+
+ if (!fp->uf_prof_initialized) {
+ if (len == 0) {
+ len = 1; // avoid getting error for allocating zero bytes
+ }
+ fp->uf_tm_count = 0;
+ fp->uf_tm_self = profile_zero();
+ fp->uf_tm_total = profile_zero();
+
+ if (fp->uf_tml_count == NULL) {
+ fp->uf_tml_count = xcalloc((size_t)len, sizeof(int));
+ }
+
+ if (fp->uf_tml_total == NULL) {
+ fp->uf_tml_total = xcalloc((size_t)len, sizeof(proftime_T));
+ }
+
+ if (fp->uf_tml_self == NULL) {
+ fp->uf_tml_self = xcalloc((size_t)len, sizeof(proftime_T));
+ }
+
+ fp->uf_tml_idx = -1;
+ fp->uf_prof_initialized = true;
+ }
+
+ fp->uf_profiling = true;
+}
+
+/// Prepare profiling for entering a child or something else that is not
+/// counted for the script/function itself.
+/// Should always be called in pair with prof_child_exit().
+///
+/// @param tm place to store waittime
+void prof_child_enter(proftime_T *tm)
+{
+ funccall_T *fc = get_current_funccal();
+
+ if (fc != NULL && fc->func->uf_profiling) {
+ fc->prof_child = profile_start();
+ }
+
+ script_prof_save(tm);
+}
+
+/// Take care of time spent in a child.
+/// Should always be called after prof_child_enter().
+///
+/// @param tm where waittime was stored
+void prof_child_exit(proftime_T *tm)
+{
+ funccall_T *fc = get_current_funccal();
+
+ if (fc != NULL && fc->func->uf_profiling) {
+ fc->prof_child = profile_end(fc->prof_child);
+ // don't count waiting time
+ fc->prof_child = profile_sub_wait(*tm, fc->prof_child);
+ fc->func->uf_tm_children =
+ profile_add(fc->func->uf_tm_children, fc->prof_child);
+ fc->func->uf_tml_children =
+ profile_add(fc->func->uf_tml_children, fc->prof_child);
+ }
+ script_prof_restore(tm);
+}
+
+/// Called when starting to read a function line.
+/// "sourcing_lnum" must be correct!
+/// When skipping lines it may not actually be executed, but we won't find out
+/// until later and we need to store the time now.
+void func_line_start(void *cookie)
+{
+ funccall_T *fcp = (funccall_T *)cookie;
+ ufunc_T *fp = fcp->func;
+
+ if (fp->uf_profiling && SOURCING_LNUM >= 1 && SOURCING_LNUM <= fp->uf_lines.ga_len) {
+ fp->uf_tml_idx = SOURCING_LNUM - 1;
+ // Skip continuation lines.
+ while (fp->uf_tml_idx > 0 && FUNCLINE(fp, fp->uf_tml_idx) == NULL) {
+ fp->uf_tml_idx--;
+ }
+ fp->uf_tml_execed = false;
+ fp->uf_tml_start = profile_start();
+ fp->uf_tml_children = profile_zero();
+ fp->uf_tml_wait = profile_get_wait();
+ }
+}
+
+/// Called when actually executing a function line.
+void func_line_exec(void *cookie)
+{
+ funccall_T *fcp = (funccall_T *)cookie;
+ ufunc_T *fp = fcp->func;
+
+ if (fp->uf_profiling && fp->uf_tml_idx >= 0) {
+ fp->uf_tml_execed = true;
+ }
+}
+
+/// Called when done with a function line.
+void func_line_end(void *cookie)
+{
+ funccall_T *fcp = (funccall_T *)cookie;
+ ufunc_T *fp = fcp->func;
+
+ if (fp->uf_profiling && fp->uf_tml_idx >= 0) {
+ if (fp->uf_tml_execed) {
+ fp->uf_tml_count[fp->uf_tml_idx]++;
+ fp->uf_tml_start = profile_end(fp->uf_tml_start);
+ fp->uf_tml_start = profile_sub_wait(fp->uf_tml_wait, fp->uf_tml_start);
+ fp->uf_tml_total[fp->uf_tml_idx] =
+ profile_add(fp->uf_tml_total[fp->uf_tml_idx], fp->uf_tml_start);
+ fp->uf_tml_self[fp->uf_tml_idx] =
+ profile_self(fp->uf_tml_self[fp->uf_tml_idx], fp->uf_tml_start,
+ fp->uf_tml_children);
+ }
+ fp->uf_tml_idx = -1;
+ }
+}
+
+/// Dump the profiling results for all functions in file "fd".
+static void func_dump_profile(FILE *fd)
+{
+ hashtab_T *const functbl = func_tbl_get();
+ hashitem_T *hi;
+ int todo;
+ ufunc_T *fp;
+ ufunc_T **sorttab;
+ int st_len = 0;
+
+ todo = (int)functbl->ht_used;
+ if (todo == 0) {
+ return; // nothing to dump
+ }
+
+ sorttab = xmalloc(sizeof(ufunc_T *) * (size_t)todo);
+
+ for (hi = functbl->ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ todo--;
+ fp = HI2UF(hi);
+ if (fp->uf_prof_initialized) {
+ sorttab[st_len++] = fp;
+
+ if (fp->uf_name[0] == K_SPECIAL) {
+ fprintf(fd, "FUNCTION <SNR>%s()\n", fp->uf_name + 3);
+ } else {
+ fprintf(fd, "FUNCTION %s()\n", fp->uf_name);
+ }
+ if (fp->uf_script_ctx.sc_sid != 0) {
+ bool should_free;
+ const LastSet last_set = (LastSet){
+ .script_ctx = fp->uf_script_ctx,
+ .channel_id = 0,
+ };
+ char *p = get_scriptname(last_set, &should_free);
+ fprintf(fd, " Defined: %s:%" PRIdLINENR "\n",
+ p, fp->uf_script_ctx.sc_lnum);
+ if (should_free) {
+ xfree(p);
+ }
+ }
+ if (fp->uf_tm_count == 1) {
+ fprintf(fd, "Called 1 time\n");
+ } else {
+ fprintf(fd, "Called %d times\n", fp->uf_tm_count);
+ }
+ fprintf(fd, "Total time: %s\n", profile_msg(fp->uf_tm_total));
+ fprintf(fd, " Self time: %s\n", profile_msg(fp->uf_tm_self));
+ fprintf(fd, "\n");
+ fprintf(fd, "count total (s) self (s)\n");
+
+ for (int i = 0; i < fp->uf_lines.ga_len; i++) {
+ if (FUNCLINE(fp, i) == NULL) {
+ continue;
+ }
+ prof_func_line(fd, fp->uf_tml_count[i],
+ &fp->uf_tml_total[i], &fp->uf_tml_self[i], true);
+ fprintf(fd, "%s\n", FUNCLINE(fp, i));
+ }
+ fprintf(fd, "\n");
+ }
+ }
+ }
+
+ if (st_len > 0) {
+ qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
+ prof_total_cmp);
+ prof_sort_list(fd, sorttab, st_len, "TOTAL", false);
+ qsort((void *)sorttab, (size_t)st_len, sizeof(ufunc_T *),
+ prof_self_cmp);
+ prof_sort_list(fd, sorttab, st_len, "SELF", true);
+ }
+
+ xfree(sorttab);
+}
+
+/// Start profiling a script.
+void profile_init(scriptitem_T *si)
+{
+ si->sn_pr_count = 0;
+ si->sn_pr_total = profile_zero();
+ si->sn_pr_self = profile_zero();
+
+ ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
+ si->sn_prl_idx = -1;
+ si->sn_prof_on = true;
+ si->sn_pr_nest = 0;
+}
+
+/// Save time when starting to invoke another script or function.
+///
+/// @param tm place to store wait time
+void script_prof_save(proftime_T *tm)
+{
+ scriptitem_T *si;
+
+ if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
+ si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on && si->sn_pr_nest++ == 0) {
+ si->sn_pr_child = profile_start();
+ }
+ }
+ *tm = profile_get_wait();
+}
+
+/// Count time spent in children after invoking another script or function.
+void script_prof_restore(proftime_T *tm)
+{
+ scriptitem_T *si;
+
+ if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) {
+ si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on && --si->sn_pr_nest == 0) {
+ si->sn_pr_child = profile_end(si->sn_pr_child);
+ // don't count wait time
+ si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child);
+ si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child);
+ si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child);
+ }
+ }
+}
+
+/// Dump the profiling results for all scripts in file "fd".
+static void script_dump_profile(FILE *fd)
+{
+ scriptitem_T *si;
+ FILE *sfd;
+ sn_prl_T *pp;
+
+ for (int id = 1; id <= script_items.ga_len; id++) {
+ si = &SCRIPT_ITEM(id);
+ if (si->sn_prof_on) {
+ fprintf(fd, "SCRIPT %s\n", si->sn_name);
+ if (si->sn_pr_count == 1) {
+ fprintf(fd, "Sourced 1 time\n");
+ } else {
+ fprintf(fd, "Sourced %d times\n", si->sn_pr_count);
+ }
+ fprintf(fd, "Total time: %s\n", profile_msg(si->sn_pr_total));
+ fprintf(fd, " Self time: %s\n", profile_msg(si->sn_pr_self));
+ fprintf(fd, "\n");
+ fprintf(fd, "count total (s) self (s)\n");
+
+ sfd = os_fopen(si->sn_name, "r");
+ if (sfd == NULL) {
+ fprintf(fd, "Cannot open file!\n");
+ } else {
+ // Keep going till the end of file, so that trailing
+ // continuation lines are listed.
+ for (int i = 0;; i++) {
+ if (vim_fgets(IObuff, IOSIZE, sfd)) {
+ break;
+ }
+ // When a line has been truncated, append NL, taking care
+ // of multi-byte characters .
+ if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) {
+ int n = IOSIZE - 2;
+
+ // Move to the first byte of this char.
+ // utf_head_off() doesn't work, because it checks
+ // for a truncated character.
+ while (n > 0 && (IObuff[n] & 0xc0) == 0x80) {
+ n--;
+ }
+
+ IObuff[n] = NL;
+ IObuff[n + 1] = NUL;
+ }
+ if (i < si->sn_prl_ga.ga_len
+ && (pp = &PRL_ITEM(si, i))->snp_count > 0) {
+ fprintf(fd, "%5d ", pp->snp_count);
+ if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) {
+ fprintf(fd, " ");
+ } else {
+ fprintf(fd, "%s ", profile_msg(pp->sn_prl_total));
+ }
+ fprintf(fd, "%s ", profile_msg(pp->sn_prl_self));
+ } else {
+ fprintf(fd, " ");
+ }
+ fprintf(fd, "%s", IObuff);
+ }
+ fclose(sfd);
+ }
+ fprintf(fd, "\n");
+ }
+ }
+}
+
+/// Dump the profiling info.
+void profile_dump(void)
+{
+ FILE *fd;
+
+ if (profile_fname != NULL) {
+ fd = os_fopen(profile_fname, "w");
+ if (fd == NULL) {
+ semsg(_(e_notopen), profile_fname);
+ } else {
+ script_dump_profile(fd);
+ func_dump_profile(fd);
+ fclose(fd);
+ }
+ }
+}
+
+/// Called when starting to read a script line.
+/// "sourcing_lnum" must be correct!
+/// When skipping lines it may not actually be executed, but we won't find out
+/// until later and we need to store the time now.
+void script_line_start(void)
+{
+ scriptitem_T *si;
+ sn_prl_T *pp;
+
+ if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
+ return;
+ }
+ si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on && SOURCING_LNUM >= 1) {
+ // Grow the array before starting the timer, so that the time spent
+ // here isn't counted.
+ (void)ga_grow(&si->sn_prl_ga, SOURCING_LNUM - si->sn_prl_ga.ga_len);
+ si->sn_prl_idx = SOURCING_LNUM - 1;
+ while (si->sn_prl_ga.ga_len <= si->sn_prl_idx
+ && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) {
+ // Zero counters for a line that was not used before.
+ pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len);
+ pp->snp_count = 0;
+ pp->sn_prl_total = profile_zero();
+ pp->sn_prl_self = profile_zero();
+ si->sn_prl_ga.ga_len++;
+ }
+ si->sn_prl_execed = false;
+ si->sn_prl_start = profile_start();
+ si->sn_prl_children = profile_zero();
+ si->sn_prl_wait = profile_get_wait();
+ }
+}
+
+/// Called when actually executing a function line.
+void script_line_exec(void)
+{
+ scriptitem_T *si;
+
+ if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
+ return;
+ }
+ si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on && si->sn_prl_idx >= 0) {
+ si->sn_prl_execed = true;
+ }
+}
+
+/// Called when done with a function line.
+void script_line_end(void)
+{
+ scriptitem_T *si;
+ sn_prl_T *pp;
+
+ if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) {
+ return;
+ }
+ si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on && si->sn_prl_idx >= 0
+ && si->sn_prl_idx < si->sn_prl_ga.ga_len) {
+ if (si->sn_prl_execed) {
+ pp = &PRL_ITEM(si, si->sn_prl_idx);
+ pp->snp_count++;
+ si->sn_prl_start = profile_end(si->sn_prl_start);
+ si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start);
+ pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start);
+ pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start,
+ si->sn_prl_children);
+ }
+ si->sn_prl_idx = -1;
+ }
+}
+
/// globals for use in the startuptime related functionality (time_*).
static proftime_T g_start_time;
static proftime_T g_prev_time;
diff --git a/src/nvim/profile.h b/src/nvim/profile.h
index 17c35c5eb7..547d11185f 100644
--- a/src/nvim/profile.h
+++ b/src/nvim/profile.h
@@ -4,7 +4,8 @@
#include <stdint.h>
#include <time.h>
-typedef uint64_t proftime_T;
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/runtime.h"
#define TIME_MSG(s) do { \
if (time_fd != NULL) time_msg(s, NULL); \
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 99129bd15e..f9c4892b91 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -9,10 +9,12 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
@@ -22,6 +24,7 @@
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -31,13 +34,13 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -64,9 +67,9 @@ struct qfline_S {
char *qf_module; ///< module name for this error
char *qf_pattern; ///< search pattern for the error
char *qf_text; ///< description of the error
- char qf_viscol; ///< set to TRUE if qf_col and qf_end_col is
+ char qf_viscol; ///< set to true if qf_col and qf_end_col is
// screen column
- char qf_cleared; ///< set to TRUE if line has been deleted
+ char qf_cleared; ///< set to true if line has been deleted
char qf_type; ///< type of the error (mostly 'E'); 1 for :helpgrep
char qf_valid; ///< valid error message detected
};
@@ -97,7 +100,7 @@ typedef struct qf_list_S {
qfline_T *qf_ptr; ///< pointer to the current error
int qf_count; ///< number of errors (0 means empty list)
int qf_index; ///< current index in the error list
- int qf_nonevalid; ///< TRUE if not a single valid entry found
+ int qf_nonevalid; ///< true if not a single valid entry found
char *qf_title; ///< title derived from the command that created
///< the error list or set by setqflist
typval_T *qf_ctx; ///< context set by setqflist/setloclist
@@ -604,7 +607,7 @@ static efm_T *parse_efm_option(char *efm)
goto parse_efm_error;
}
// Advance to next part
- efm = (char *)skip_to_option_part((char_u *)efm + len); // skip comma and spaces
+ efm = skip_to_option_part(efm + len); // skip comma and spaces
}
if (fmt_first == NULL) { // nothing found
@@ -796,7 +799,7 @@ retry:
// Convert a line if it contains a non-ASCII character
if (state->vc.vc_type != CONV_NONE && has_non_ascii((char_u *)state->linebuf)) {
- char *line = (char *)string_convert(&state->vc, (char_u *)state->linebuf, &state->linelen);
+ char *line = string_convert(&state->vc, state->linebuf, &state->linelen);
if (line != NULL) {
if (state->linelen < IOSIZE) {
STRLCPY(state->linebuf, line, state->linelen + 1);
@@ -997,7 +1000,7 @@ static int qf_setup_state(qfstate_T *pstate, char *restrict enc, const char *res
{
pstate->vc.vc_type = CONV_NONE;
if (enc != NULL && *enc != NUL) {
- convert_setup(&pstate->vc, (char_u *)enc, p_enc);
+ convert_setup(&pstate->vc, enc, p_enc);
}
if (efile != NULL
@@ -1088,7 +1091,7 @@ static int qf_init_ext(qf_info_T *qi, int qf_idx, const char *restrict efile, bu
// Use the local value of 'errorformat' if it's set.
if (errorformat == p_efm && tv == NULL && buf && *buf->b_p_efm != NUL) {
- efm = (char *)buf->b_p_efm;
+ efm = buf->b_p_efm;
} else {
efm = errorformat;
}
@@ -1224,7 +1227,7 @@ static void qf_new_list(qf_info_T *qi, const char *qf_title)
qi->qf_curlist = qi->qf_listcount++;
}
qf_list_T *qfl = qf_get_curlist(qi);
- memset(qfl, 0, sizeof(qf_list_T));
+ CLEAR_POINTER(qfl);
qf_store_title(qfl, qf_title);
qfl->qfl_type = qi->qfl_type;
qfl->qf_id = ++last_qf_id;
@@ -1241,7 +1244,7 @@ static int qf_parse_fmt_f(regmatch_T *rmp, int midx, qffields_T *fields, int pre
// Expand ~/file and $HOME/file to full path.
char c = (char)(*rmp->endp[midx]);
*rmp->endp[midx] = NUL;
- expand_env(rmp->startp[midx], (char_u *)fields->namebuf, CMDBUFFSIZE);
+ expand_env((char *)rmp->startp[midx], fields->namebuf, CMDBUFFSIZE);
*rmp->endp[midx] = (char_u)c;
// For separate filename patterns (%O, %P and %Q), the specified file
@@ -1888,7 +1891,7 @@ static qf_info_T *ll_get_or_alloc_list(win_T *wp)
/// Get the quickfix/location list stack to use for the specified Ex command.
/// For a location list command, returns the stack for the current window. If
/// the location list is not found, then returns NULL and prints an error
-/// message if 'print_emsg' is TRUE.
+/// message if 'print_emsg' is true.
static qf_info_T *qf_cmd_get_stack(exarg_T *eap, int print_emsg)
{
qf_info_T *qi = &ql_info;
@@ -2126,7 +2129,7 @@ static char *qf_push_dir(char *dirbuf, struct dir_stack_T **stackptr, bool is_fi
while (ds_new) {
xfree((*stackptr)->dirname);
(*stackptr)->dirname = concat_fnames(ds_new->dirname, dirbuf, true);
- if (os_isdir((char_u *)(*stackptr)->dirname)) {
+ if (os_isdir((*stackptr)->dirname)) {
break;
}
@@ -2508,7 +2511,7 @@ static win_T *qf_find_win_with_normal_buf(void)
}
// Go to a window in any tabpage containing the specified file. Returns true
-// if successfully jumped to the window. Otherwise returns FALSE.
+// if successfully jumped to the window. Otherwise returns false.
static bool qf_goto_tabwin_with_file(int fnum)
{
FOR_ALL_TAB_WINDOWS(tp, wp) {
@@ -2920,7 +2923,7 @@ void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit)
// If 'newwin' is true, then open the file in a new window.
static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, bool newwin)
{
- char *old_swb = (char *)p_swb;
+ char *old_swb = p_swb;
unsigned old_swb_flags = swb_flags;
const bool old_KeyTyped = KeyTyped; // getting file may reset it
@@ -2929,7 +2932,7 @@ static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, boo
}
if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -2991,10 +2994,10 @@ theend:
qfl->qf_ptr = qf_ptr;
qfl->qf_index = qf_index;
}
- if (p_swb != (char_u *)old_swb && p_swb == empty_option) {
+ if (p_swb != old_swb && p_swb == empty_option) {
// Restore old 'switchbuf' value, but not when an autocommand or
// modeline has changed the value.
- p_swb = (char_u *)old_swb;
+ p_swb = old_swb;
swb_flags = old_swb_flags;
}
decr_quickfix_busy();
@@ -3035,23 +3038,23 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel)
// text of the entry.
bool filter_entry = true;
if (qfp->qf_module != NULL && *qfp->qf_module != NUL) {
- filter_entry &= message_filtered((char_u *)qfp->qf_module);
+ filter_entry &= message_filtered(qfp->qf_module);
}
if (filter_entry && fname != NULL) {
- filter_entry &= message_filtered((char_u *)fname);
+ filter_entry &= message_filtered(fname);
}
if (filter_entry && qfp->qf_pattern != NULL) {
- filter_entry &= message_filtered((char_u *)qfp->qf_pattern);
+ filter_entry &= message_filtered(qfp->qf_pattern);
}
if (filter_entry) {
- filter_entry &= message_filtered((char_u *)qfp->qf_text);
+ filter_entry &= message_filtered(qfp->qf_text);
}
if (filter_entry) {
return;
}
msg_putchar('\n');
- msg_outtrans_attr(IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr);
+ msg_outtrans_attr((char *)IObuff, cursel ? HL_ATTR(HLF_QFL) : qfFileAttr);
if (qfp->qf_lnum != 0) {
msg_puts_attr(":", qfSepAttr);
@@ -3108,7 +3111,7 @@ void qf_list(exarg_T *eap)
}
if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi))) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -3119,7 +3122,7 @@ void qf_list(exarg_T *eap)
}
int idx1 = 1;
int idx2 = -1;
- if (!get_list_range((char_u **)&arg, &idx1, &idx2) || *arg != NUL) {
+ if (!get_list_range(&arg, &idx1, &idx2) || *arg != NUL) {
semsg(_(e_trailing_arg), arg);
return;
}
@@ -3177,7 +3180,7 @@ static void qf_fmt_text(const char *restrict text, char *restrict buf, int bufsi
int i;
const char *p = (char *)text;
- for (i = 0; *p != NUL && i < bufsize - 1; ++i) {
+ for (i = 0; *p != NUL && i < bufsize - 1; i++) {
if (*p == '\n') {
buf[i] = ' ';
while (*++p != NUL) {
@@ -3260,13 +3263,13 @@ void qf_age(exarg_T *eap)
emsg(_("E380: At bottom of quickfix stack"));
break;
}
- --qi->qf_curlist;
+ qi->qf_curlist--;
} else {
if (qi->qf_curlist >= qi->qf_listcount - 1) {
emsg(_("E381: At top of quickfix stack"));
break;
}
- ++qi->qf_curlist;
+ qi->qf_curlist++;
}
}
qf_msg(qi, qi->qf_curlist, "");
@@ -3390,7 +3393,7 @@ bool qf_mark_adjust(win_T *wp, linenr_T line1, linenr_T line2, linenr_T amount,
found_one = true;
if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) {
if (amount == MAXLNUM) {
- qfp->qf_cleared = TRUE;
+ qfp->qf_cleared = true;
} else {
qfp->qf_lnum += amount;
}
@@ -3464,7 +3467,7 @@ void qf_view_result(bool split)
qi = GET_LOC_LIST(curwin);
}
if (qf_list_empty(qf_get_curlist(qi))) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -3555,12 +3558,12 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp
static void qf_set_cwindow_options(void)
{
// switch off 'swapfile'
- set_option_value("swf", 0L, NULL, OPT_LOCAL);
- set_option_value("bt", 0L, "quickfix", OPT_LOCAL);
- set_option_value("bh", 0L, "hide", OPT_LOCAL);
+ set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL);
+ set_option_value_give_err("bt", 0L, "quickfix", OPT_LOCAL);
+ set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL);
RESET_BINDING(curwin);
curwin->w_p_diff = false;
- set_option_value("fdm", 0L, "manual", OPT_LOCAL);
+ set_option_value_give_err("fdm", 0L, "manual", OPT_LOCAL);
}
// Open a new quickfix or location list window, load the quickfix buffer and
@@ -3707,7 +3710,7 @@ static void qf_win_goto(win_T *win, linenr_T lnum)
curwin->w_cursor.coladd = 0;
curwin->w_curswant = 0;
update_topline(curwin); // scroll to show the line
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
curwin->w_redr_status = true; // update ruler
curwin = old_curwin;
curbuf = curwin->w_buffer;
@@ -3744,7 +3747,7 @@ linenr_T qf_current_entry(win_T *wp)
}
/// Update the cursor position in the quickfix window to the current error.
-/// Return TRUE if there is a quickfix window.
+/// Return true if there is a quickfix window.
///
/// @param old_qf_index previous qf_index or zero
static bool qf_win_pos_update(qf_info_T *qi, int old_qf_index)
@@ -3827,38 +3830,11 @@ static buf_T *qf_find_buf(qf_info_T *qi)
return NULL;
}
-// Process the 'quickfixtextfunc' option value.
-bool qf_process_qftf_option(void)
+/// Process the 'quickfixtextfunc' option value.
+/// @return OK or FAIL
+int qf_process_qftf_option(void)
{
- if (p_qftf == NULL || *p_qftf == NUL) {
- callback_free(&qftf_cb);
- return true;
- }
-
- typval_T *tv;
- if (*p_qftf == '{') {
- // Lambda expression
- tv = eval_expr((char *)p_qftf);
- if (tv == NULL) {
- return false;
- }
- } else {
- // treat everything else as a function name string
- tv = xcalloc(1, sizeof(*tv));
- tv->v_type = VAR_STRING;
- tv->vval.v_string = (char *)vim_strsave(p_qftf);
- }
-
- Callback cb;
- if (!callback_from_typval(&cb, tv)) {
- tv_free(tv);
- return false;
- }
-
- callback_free(&qftf_cb);
- qftf_cb = cb;
- tv_free(tv);
- return true;
+ return option_set_callback_func((char_u *)p_qftf, &qftf_cb);
}
/// Update the w:quickfix_title variable in the quickfix/location list window in
@@ -3922,7 +3898,7 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
// Only redraw when added lines are visible. This avoids flickering when
// the added lines are not visible.
if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline) {
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
}
}
}
@@ -4132,7 +4108,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
// resembles reading a file into a buffer, it's more logical when using
// autocommands.
curbuf->b_ro_locked++;
- set_option_value("ft", 0L, "qf", OPT_LOCAL);
+ set_option_value_give_err("ft", 0L, "qf", OPT_LOCAL);
curbuf->b_p_ma = false;
keep_filetype = true; // don't detect 'filetype'
@@ -4142,7 +4118,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
curbuf->b_ro_locked--;
// make sure it will be redrawn
- redraw_curbuf_later(NOT_VALID);
+ redraw_curbuf_later(UPD_NOT_VALID);
}
// Restore KeyTyped, setting 'filetype' may reset it.
@@ -4199,7 +4175,7 @@ static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit)
}
}
-// Return TRUE when using ":vimgrep" for ":grep".
+// Return true when using ":vimgrep" for ":grep".
int grep_internal(cmdidx_T cmdidx)
{
return (cmdidx == CMD_grep
@@ -4207,7 +4183,7 @@ int grep_internal(cmdidx_T cmdidx)
|| cmdidx == CMD_grepadd
|| cmdidx == CMD_lgrepadd)
&& STRCMP("internal",
- *curbuf->b_p_gp == NUL ? p_gp : curbuf->b_p_gp) == 0;
+ *curbuf->b_p_gp == NUL ? p_gp : (char_u *)curbuf->b_p_gp) == 0;
}
// Return the make/grep autocmd name.
@@ -4246,7 +4222,7 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname)
// If 'shellpipe' empty: don't redirect to 'errorfile'.
if (*p_sp != NUL) {
- append_redir(cmd, len, (char *)p_sp, (char *)fname);
+ append_redir(cmd, len, p_sp, (char *)fname);
}
// Display the fully formed command. Output a newline if there's something
@@ -4265,7 +4241,7 @@ static char *make_get_fullcmd(const char *makecmd, const char *fname)
// Used for ":make", ":lmake", ":grep", ":lgrep", ":grepadd", and ":lgrepadd"
void ex_make(exarg_T *eap)
{
- char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc;
+ char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
// Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal".
if (grep_internal(eap->cmdidx)) {
@@ -4352,7 +4328,7 @@ static char *get_mef_name(void)
char *p;
- for (p = p_mef; *p; ++p) {
+ for (p = p_mef; *p; p++) {
if (p[0] == '#' && p[1] == '#') {
break;
}
@@ -4856,7 +4832,7 @@ static void qf_get_nth_below_entry(qfline_T *entry_arg, linenr_T n, bool linewis
}
/// Get the nth quickfix entry above the specified entry. Searches backwards in
-/// the list. If linewise is TRUE, then treat multiple entries on a single line
+/// the list. If linewise is true, then treat multiple entries on a single line
/// as one.
static void qf_get_nth_above_entry(qfline_T *entry, linenr_T n, bool linewise, int *errornr)
FUNC_ATTR_NONNULL_ALL
@@ -4922,7 +4898,7 @@ void ex_cbelow(exarg_T *eap)
|| eap->cmdidx == CMD_cafter) ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
if (!(curbuf->b_has_qf_entry & buf_has_flag)) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -4934,7 +4910,7 @@ void ex_cbelow(exarg_T *eap)
qf_list_T *qfl = qf_get_curlist(qi);
// check if the list has valid errors
if (!qf_list_has_valid_entries(qfl)) {
- emsg(_(e_quickfix));
+ emsg(_(e_no_errors));
return;
}
@@ -5005,7 +4981,7 @@ void ex_cfile(exarg_T *eap)
set_string_option_direct("ef", -1, eap->arg, OPT_FREE, 0);
}
- char *enc = (*curbuf->b_p_menc != NUL) ? (char *)curbuf->b_p_menc : (char *)p_menc;
+ char *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc;
if (is_loclist_cmd(eap->cmdidx)) {
wp = curwin;
@@ -5164,6 +5140,7 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5, 6)
{
bool found_match = false;
+ const size_t pat_len = STRLEN(spat);
for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count && *tomatch > 0; lnum++) {
colnr_T col = 0;
@@ -5205,7 +5182,6 @@ static bool vgr_match_buflines(qf_list_T *qfl, char *fname, buf_T *buf, char *sp
}
}
} else {
- const size_t pat_len = STRLEN(spat);
char *const str = (char *)ml_get_buf(buf, lnum, false);
int score;
uint32_t matches[MAX_FUZZY_MATCHES];
@@ -5299,7 +5275,7 @@ static bool existing_swapfile(const buf_T *buf)
/// :{count}vimgrep /{pattern}/[g][j] {file} ...
static int vgr_process_args(exarg_T *eap, vgr_args_T *args)
{
- memset(args, 0, sizeof(*args));
+ CLEAR_POINTER(args);
args->regmatch.regprog = NULL;
args->qf_title = xstrdup(qf_cmdtitle(*eap->cmdlinep));
@@ -5329,7 +5305,7 @@ static int vgr_process_args(exarg_T *eap, vgr_args_T *args)
}
// Parse the list of arguments, wildcards have already been expanded.
- if (get_arglist_exp((char_u *)p, &args->fcount, &args->fnames, true) == FAIL) {
+ if (get_arglist_exp(p, &args->fcount, &args->fnames, true) == FAIL) {
return FAIL;
}
if (args->fcount == 0) {
@@ -5453,7 +5429,7 @@ static int vgr_process_files(win_T *wp, qf_info_T *qi, vgr_args_T *cmd_args, boo
// options!
aco_save_T aco;
aucmd_prepbuf(&aco, buf);
- apply_autocmds(EVENT_FILETYPE, (char *)buf->b_p_ft, buf->b_fname, true, buf);
+ apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, true, buf);
do_modelines(OPT_NOWIN);
aucmd_restbuf(&aco);
}
@@ -5633,7 +5609,7 @@ static buf_T *load_dummy_buffer(char *fname, char *dirname_start, char *resultin
if (readfile_result == OK
&& !got_int
&& !(curbuf->b_flags & BF_NEW)) {
- failed = FALSE;
+ failed = false;
if (curbuf != newbuf) {
// Bloody autocommands changed the buffer! Can happen when
// using netrw and editing a remote file. Use the current
@@ -5701,7 +5677,7 @@ static void wipe_dummy_buffer(buf_T *buf, char *dirname_start)
cleanup_T cs;
// Reset the error/interrupt/exception state here so that aborting()
- // returns FALSE when wiping out the buffer. Otherwise it doesn't
+ // returns false when wiping out the buffer. Otherwise it doesn't
// work when got_int is set.
enter_cleanup(&cs);
@@ -5779,7 +5755,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list)
/// If qf_idx is -1, use the current list. Otherwise, use the specified list.
/// If eidx is not 0, then return only the specified entry. Otherwise return
/// all the entries.
-int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list)
+static int get_errorlist(qf_info_T *qi_arg, win_T *wp, int qf_idx, int eidx, list_T *list)
{
qf_info_T *qi = qi_arg;
@@ -6149,7 +6125,7 @@ static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
/// Return quickfix/location list details (title) as a dictionary.
/// 'what' contains the details to return. If 'list_idx' is -1,
/// then current list is used. Otherwise the specified list is used.
-int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
+static int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
{
qf_info_T *qi = &ql_info;
dictitem_T *di = NULL;
@@ -7057,7 +7033,7 @@ static void hgr_search_in_rtp(qf_list_T *qfl, regmatch_T *p_regmatch, const char
FUNC_ATTR_NONNULL_ARG(1, 2)
{
// Go through all directories in 'runtimepath'
- char *p = (char *)p_rtp;
+ char *p = p_rtp;
while (*p != NUL && !got_int) {
copy_option_part(&p, (char *)NameBuff, MAXPATHL, ",");
@@ -7086,9 +7062,10 @@ void ex_helpgrep(exarg_T *eap)
}
}
+ bool updated = false;
// Make 'cpoptions' empty, the 'l' flag should not be used here.
char *const save_cpo = p_cpo;
- p_cpo = (char *)empty_option;
+ p_cpo = empty_option;
bool new_qi = false;
if (is_loclist_cmd(eap->cmdidx)) {
@@ -7116,14 +7093,24 @@ void ex_helpgrep(exarg_T *eap)
qfl->qf_ptr = qfl->qf_start;
qfl->qf_index = 1;
qf_list_changed(qfl);
- qf_update_buffer(qi, NULL);
+ updated = true;
}
- if ((char_u *)p_cpo == empty_option) {
+ if (p_cpo == empty_option) {
p_cpo = save_cpo;
} else {
- // Darn, some plugin changed the value.
- free_string_option((char_u *)save_cpo);
+ // Darn, some plugin changed the value. If it's still empty it was
+ // changed and restored, need to restore in the complicated way.
+ if (*p_cpo == NUL) {
+ set_option_value_give_err("cpo", 0L, save_cpo, 0);
+ }
+ free_string_option(save_cpo);
+ }
+
+ if (updated) {
+ // This may open a window and source scripts, do this after 'cpo' was
+ // restored.
+ qf_update_buffer(qi, NULL);
}
if (au_name != NULL) {
@@ -7157,3 +7144,137 @@ void ex_helpgrep(exarg_T *eap)
}
}
}
+
+static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv)
+{
+ if (what_arg->v_type == VAR_UNKNOWN) {
+ tv_list_alloc_ret(rettv, kListLenMayKnow);
+ if (is_qf || wp != NULL) {
+ (void)get_errorlist(NULL, wp, -1, 0, rettv->vval.v_list);
+ }
+ } else {
+ tv_dict_alloc_ret(rettv);
+ if (is_qf || wp != NULL) {
+ if (what_arg->v_type == VAR_DICT) {
+ dict_T *d = what_arg->vval.v_dict;
+
+ if (d != NULL) {
+ qf_get_properties(wp, d, rettv->vval.v_dict);
+ }
+ } else {
+ emsg(_(e_dictreq));
+ }
+ }
+ }
+}
+
+/// "getloclist()" function
+void f_getloclist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ win_T *wp = find_win_by_nr_or_id(&argvars[0]);
+ get_qf_loc_list(false, wp, &argvars[1], rettv);
+}
+
+/// "getqflist()" functions
+void f_getqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ get_qf_loc_list(true, NULL, &argvars[0], rettv);
+}
+
+/// Create quickfix/location list from VimL values
+///
+/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
+/// args argument in which case errors out, including VAR_UNKNOWN parameters.
+///
+/// @param[in,out] wp Window to create location list for. May be NULL in
+/// which case quickfix list will be created.
+/// @param[in] args [list, action, what]
+/// @param[in] args[0] Quickfix list contents.
+/// @param[in] args[1] Optional. Action to perform:
+/// append to an existing list, replace its content,
+/// or create a new one.
+/// @param[in] args[2] Optional. Quickfix list properties or title.
+/// Defaults to caller function name.
+/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
+static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ARG(2, 3)
+{
+ static char *e_invact = N_("E927: Invalid action: '%s'");
+ const char *title = NULL;
+ char action = ' ';
+ static int recursive = 0;
+ rettv->vval.v_number = -1;
+ dict_T *what = NULL;
+
+ typval_T *list_arg = &args[0];
+ if (list_arg->v_type != VAR_LIST) {
+ emsg(_(e_listreq));
+ return;
+ } else if (recursive != 0) {
+ emsg(_(e_au_recursive));
+ return;
+ }
+
+ typval_T *action_arg = &args[1];
+ if (action_arg->v_type == VAR_UNKNOWN) {
+ // Option argument was not given.
+ goto skip_args;
+ } else if (action_arg->v_type != VAR_STRING) {
+ emsg(_(e_stringreq));
+ return;
+ }
+ const char *const act = tv_get_string_chk(action_arg);
+ if ((*act == 'a' || *act == 'r' || *act == ' ' || *act == 'f')
+ && act[1] == NUL) {
+ action = *act;
+ } else {
+ semsg(_(e_invact), act);
+ return;
+ }
+
+ typval_T *const what_arg = &args[2];
+ if (what_arg->v_type == VAR_UNKNOWN) {
+ // Option argument was not given.
+ goto skip_args;
+ } else if (what_arg->v_type == VAR_STRING) {
+ title = tv_get_string_chk(what_arg);
+ if (!title) {
+ // Type error. Error already printed by tv_get_string_chk().
+ return;
+ }
+ } else if (what_arg->v_type == VAR_DICT && what_arg->vval.v_dict != NULL) {
+ what = what_arg->vval.v_dict;
+ } else {
+ emsg(_(e_dictreq));
+ return;
+ }
+
+skip_args:
+ if (!title) {
+ title = (wp ? ":setloclist()" : ":setqflist()");
+ }
+
+ recursive++;
+ list_T *const l = list_arg->vval.v_list;
+ if (set_errorlist(wp, l, action, (char *)title, what) == OK) {
+ rettv->vval.v_number = 0;
+ }
+ recursive--;
+}
+
+/// "setloclist()" function
+void f_setloclist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->vval.v_number = -1;
+
+ win_T *win = find_win_by_nr_or_id(&argvars[0]);
+ if (win != NULL) {
+ set_qf_ll_list(win, &argvars[1], rettv);
+ }
+}
+
+/// "setqflist()" function
+void f_setqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ set_qf_ll_list(NULL, argvars, rettv);
+}
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index fbbf904f8b..a52343e28b 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -20,7 +20,6 @@
#include "nvim/charset.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/garray.h"
#include "nvim/mark.h"
#include "nvim/memline.h"
@@ -28,6 +27,7 @@
#include "nvim/message.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/strings.h"
#include "nvim/vim.h"
@@ -481,19 +481,17 @@ static char_u *skip_anyof(char *p)
return (char_u *)p;
}
-/*
- * Skip past regular expression.
- * Stop at end of "startp" or where "dirc" is found ('/', '?', etc).
- * Take care of characters with a backslash in front of it.
- * Skip strings inside [ and ].
- * When "newp" is not NULL and "dirc" is '?', make an allocated copy of the
- * expression and change "\?" to "?". If "*newp" is not NULL the expression
- * is changed in-place.
- */
-char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
+/// Skip past regular expression.
+/// Stop at end of "startp" or where "dirc" is found ('/', '?', etc).
+/// Take care of characters with a backslash in front of it.
+/// Skip strings inside [ and ].
+/// When "newp" is not NULL and "dirc" is '?', make an allocated copy of the
+/// expression and change "\?" to "?". If "*newp" is not NULL the expression
+/// is changed in-place.
+char *skip_regexp(char *startp, int dirc, int magic, char **newp)
{
int mymagic;
- char_u *p = startp;
+ char *p = startp;
if (magic) {
mymagic = MAGIC_ON;
@@ -508,7 +506,7 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
}
if ((p[0] == '[' && mymagic >= MAGIC_ON)
|| (p[0] == '\\' && p[1] == '[' && mymagic <= MAGIC_OFF)) {
- p = skip_anyof((char *)p + 1);
+ p = (char *)skip_anyof(p + 1);
if (p[0] == NUL) {
break;
}
@@ -516,7 +514,7 @@ char_u *skip_regexp(char_u *startp, int dirc, int magic, char_u **newp)
if (dirc == '?' && newp != NULL && p[1] == '?') {
// change "\?" to "?", make a copy first.
if (*newp == NULL) {
- *newp = vim_strsave(startp);
+ *newp = xstrdup(startp);
p = *newp + (p - startp);
}
STRMOVE(p, p + 1);
@@ -823,7 +821,7 @@ static int64_t gethexchrs(int maxinputlen)
}
nr <<= 4;
nr |= hex2nr(c);
- ++regparse;
+ regparse++;
}
if (i == 0) {
@@ -880,7 +878,7 @@ static int64_t getoctchrs(void)
}
nr <<= 3;
nr |= hex2nr(c);
- ++regparse;
+ regparse++;
}
if (i == 0) {
@@ -1167,7 +1165,7 @@ static bool reg_match_visual(void)
rex.line = reg_getline(rex.lnum);
rex.input = rex.line + col;
- unsigned int cols_u = win_linetabsize(wp, rex.line, col);
+ unsigned int cols_u = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, rex.line, col);
assert(cols_u <= MAXCOL);
colnr_T cols = (colnr_T)cols_u;
if (cols < start || cols > end - (*p_sel == 'e')) {
@@ -2097,8 +2095,8 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest, int des
dst++;
}
- ++s;
- --len;
+ s++;
+ len--;
}
}
}
diff --git a/src/nvim/regexp_bt.c b/src/nvim/regexp_bt.c
index fff5ae9b7f..88f0d781af 100644
--- a/src/nvim/regexp_bt.c
+++ b/src/nvim/regexp_bt.c
@@ -466,7 +466,7 @@ static void regcomp_start(char_u *expr, int re_flags) //
num_complex_braces = 0;
regnpar = 1;
- memset(had_endbrace, 0, sizeof(had_endbrace));
+ CLEAR_FIELD(had_endbrace);
regnzpar = 1;
re_has_z = 0;
regsize = 0L;
@@ -2189,7 +2189,7 @@ collection:
while (*regparse != NUL && *regparse != ']') {
if (*regparse == '-') {
- ++regparse;
+ regparse++;
// The '-' is not used for a range at the end and
// after or before a '\n'.
if (*regparse == ']' || *regparse == NUL
@@ -2619,7 +2619,7 @@ static char_u *regpiece(int *flagp)
regoptail(ret, regnode(BACK));
regoptail(ret, ret);
reginsert_limits(BRACE_LIMITS, minval, maxval, ret);
- ++num_complex_braces;
+ num_complex_braces++;
}
if (minval > 0 && maxval > 0) {
*flagp = (HASWIDTH | (flags & (HASNL | HASLOOKBH)));
@@ -2792,7 +2792,7 @@ static char_u *reg(int paren, int *flagp)
EMSG2_RET_NULL(_("E51: Too many %s("), reg_magic == MAGIC_ALL);
}
parno = regnpar;
- ++regnpar;
+ regnpar++;
ret = regnode(MOPEN + parno);
} else if (paren == REG_NPAREN) {
// Make a NOPEN node.
@@ -3181,7 +3181,7 @@ static int regrepeat(char_u *p, long maxcount)
} else {
break;
}
- ++count;
+ count++;
}
break;
@@ -3299,7 +3299,7 @@ do_class:
} else {
break;
}
- ++count;
+ count++;
}
break;
@@ -3415,7 +3415,7 @@ do_class:
break;
}
scan += len;
- ++count;
+ count++;
}
}
}
@@ -3453,7 +3453,7 @@ do_class:
}
scan++;
}
- ++count;
+ count++;
}
break;
@@ -3764,6 +3764,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case RE_VCOL:
if (!re_num_cmp(win_linetabsize(rex.reg_win == NULL
? curwin : rex.reg_win,
+ rex.reg_firstlnum + rex.lnum,
rex.line,
(colnr_T)(rex.input - rex.line)) + 1,
scan)) {
@@ -4369,7 +4370,7 @@ static bool regmatch(char_u *scan, proftime_T *tm, int *timed_out)
case BRACE_COMPLEX + 8:
case BRACE_COMPLEX + 9:
no = op - BRACE_COMPLEX;
- ++brace_count[no];
+ brace_count[no]++;
// If not matched enough times yet, try one more
if (brace_count[no] <= (brace_min[no] <= brace_max[no]
diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h
index 09f244c2f6..b313dfe877 100644
--- a/src/nvim/regexp_defs.h
+++ b/src/nvim/regexp_defs.h
@@ -15,7 +15,6 @@
#include <stdbool.h>
#include "nvim/pos.h"
-#include "nvim/profile.h"
#include "nvim/types.h"
/*
diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c
index 1a5c250664..d4db710d93 100644
--- a/src/nvim/regexp_nfa.c
+++ b/src/nvim/regexp_nfa.c
@@ -5055,7 +5055,7 @@ skip_add:
// avoid compiler warnings
save_ptr = NULL;
- memset(&save_multipos, 0, sizeof(save_multipos));
+ CLEAR_FIELD(save_multipos);
// Set the position (with "off" added) in the subexpression. Save
// and restore it when it was in use. Otherwise fill any gap.
@@ -5180,7 +5180,7 @@ skip_add:
save_ptr = sub->list.line[subidx].end;
sub->list.line[subidx].end = rex.input + off;
// avoid compiler warnings
- memset(&save_multipos, 0, sizeof(save_multipos));
+ CLEAR_FIELD(save_multipos);
}
subs = addstate(l, state->out, subs, pim, off_arg);
@@ -5488,7 +5488,7 @@ static void nfa_save_listids(nfa_regprog_T *prog, int *list)
for (i = prog->nstate; --i >= 0;) {
list[i] = p->lastlist[1];
p->lastlist[1] = 0;
- ++p;
+ p++;
}
}
@@ -5503,7 +5503,7 @@ static void nfa_restore_listids(nfa_regprog_T *prog, int *list)
p = &prog->state[0];
for (i = prog->nstate; --i >= 0;) {
p->lastlist[1] = list[i];
- ++p;
+ p++;
}
}
@@ -6910,7 +6910,7 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm
result = col > t->state->val * ts;
}
if (!result) {
- uintmax_t lts = win_linetabsize(wp, rex.line, col);
+ uintmax_t lts = win_linetabsize(wp, rex.reg_firstlnum + rex.lnum, rex.line, col);
assert(t->state->val >= 0);
result = nfa_re_num_cmp((uintmax_t)t->state->val, op, lts + 1);
}
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 5d28d624fe..e85167f8fd 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -7,20 +7,176 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
+#include "nvim/debugger.h"
#include "nvim/eval.h"
+#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
+#include "nvim/ex_eval.h"
#include "nvim/lua/executor.h"
+#include "nvim/memline.h"
#include "nvim/option.h"
+#include "nvim/os/input.h"
#include "nvim/os/os.h"
+#include "nvim/profile.h"
#include "nvim/runtime.h"
#include "nvim/vim.h"
+/// Structure used to store info for each sourced file.
+/// It is shared between do_source() and getsourceline().
+/// This is required, because it needs to be handed to do_cmdline() and
+/// sourcing can be done recursively.
+struct source_cookie {
+ FILE *fp; ///< opened file for sourcing
+ char *nextline; ///< if not NULL: line that was read ahead
+ linenr_T sourcing_lnum; ///< line number of the source file
+ int finished; ///< ":finish" used
+#if defined(USE_CRNL)
+ int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
+ bool error; ///< true if LF found after CR-LF
+#endif
+ linenr_T breakpoint; ///< next line with breakpoint or zero
+ char *fname; ///< name of sourced file
+ int dbg_tick; ///< debug_tick when breakpoint was set
+ int level; ///< top nesting level of sourced file
+ vimconv_T conv; ///< type of conversion
+};
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "runtime.c.generated.h"
#endif
+garray_T exestack = { 0, 0, sizeof(estack_T), 50, NULL };
+garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
+
+/// Initialize the execution stack.
+void estack_init(void)
+{
+ ga_grow(&exestack, 10);
+ estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len;
+ entry->es_type = ETYPE_TOP;
+ entry->es_name = NULL;
+ entry->es_lnum = 0;
+ entry->es_info.ufunc = NULL;
+ exestack.ga_len++;
+}
+
+/// Add an item to the execution stack.
+/// @return the new entry
+estack_T *estack_push(etype_T type, char *name, linenr_T lnum)
+{
+ ga_grow(&exestack, 1);
+ estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len;
+ entry->es_type = type;
+ entry->es_name = name;
+ entry->es_lnum = lnum;
+ entry->es_info.ufunc = NULL;
+ exestack.ga_len++;
+ return entry;
+}
+
+/// Add a user function to the execution stack.
+void estack_push_ufunc(ufunc_T *ufunc, linenr_T lnum)
+{
+ estack_T *entry = estack_push(ETYPE_UFUNC,
+ (char *)(ufunc->uf_name_exp != NULL
+ ? ufunc->uf_name_exp : ufunc->uf_name),
+ lnum);
+ if (entry != NULL) {
+ entry->es_info.ufunc = ufunc;
+ }
+}
+
+/// Take an item off of the execution stack.
+void estack_pop(void)
+{
+ if (exestack.ga_len > 1) {
+ exestack.ga_len--;
+ }
+}
+
+/// Get the current value for <sfile> in allocated memory.
+/// @param which ESTACK_SFILE for <sfile>, ESTACK_STACK for <stack> or
+/// ESTACK_SCRIPT for <script>.
+char *estack_sfile(estack_arg_T which)
+{
+ const estack_T *entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1;
+ if (which == ESTACK_SFILE && entry->es_type != ETYPE_UFUNC) {
+ if (entry->es_name == NULL) {
+ return NULL;
+ }
+ return xstrdup(entry->es_name);
+ }
+
+ // If evaluated in a function or autocommand, return the path of the script
+ // where it is defined, at script level the current script path is returned
+ // instead.
+ if (which == ESTACK_SCRIPT) {
+ // Walk the stack backwards, starting from the current frame.
+ for (int idx = exestack.ga_len - 1; idx >= 0; idx--, entry--) {
+ if (entry->es_type == ETYPE_UFUNC || entry->es_type == ETYPE_AUCMD) {
+ const sctx_T *const def_ctx = (entry->es_type == ETYPE_UFUNC
+ ? &entry->es_info.ufunc->uf_script_ctx
+ : &entry->es_info.aucmd->script_ctx);
+ return def_ctx->sc_sid > 0
+ ? xstrdup((SCRIPT_ITEM(def_ctx->sc_sid).sn_name))
+ : NULL;
+ } else if (entry->es_type == ETYPE_SCRIPT) {
+ return xstrdup(entry->es_name);
+ }
+ }
+ return NULL;
+ }
+
+ // Give information about each stack entry up to the root.
+ // For a function we compose the call stack, as it was done in the past:
+ // "function One[123]..Two[456]..Three"
+ garray_T ga;
+ ga_init(&ga, sizeof(char), 100);
+ etype_T last_type = ETYPE_SCRIPT;
+ for (int idx = 0; idx < exestack.ga_len; idx++) {
+ entry = ((estack_T *)exestack.ga_data) + idx;
+ if (entry->es_name != NULL) {
+ size_t len = strlen(entry->es_name) + 15;
+ char *type_name = "";
+ if (entry->es_type != last_type) {
+ switch (entry->es_type) {
+ case ETYPE_SCRIPT:
+ type_name = "script "; break;
+ case ETYPE_UFUNC:
+ type_name = "function "; break;
+ default:
+ type_name = ""; break;
+ }
+ last_type = entry->es_type;
+ }
+ len += strlen(type_name);
+ ga_grow(&ga, (int)len);
+ linenr_T lnum = idx == exestack.ga_len - 1
+ ? which == ESTACK_STACK ? SOURCING_LNUM : 0
+ : entry->es_lnum;
+ char *dots = idx == exestack.ga_len - 1 ? "" : "..";
+ if (lnum == 0) {
+ // For the bottom entry of <sfile>: do not add the line number,
+ // it is used in <slnum>. Also leave it out when the number is
+ // not set.
+ vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s%s",
+ type_name, entry->es_name, dots);
+ } else {
+ vim_snprintf((char *)ga.ga_data + ga.ga_len, len, "%s%s[%" PRIdLINENR "]%s",
+ type_name, entry->es_name, lnum, dots);
+ }
+ ga.ga_len += (int)strlen((char *)ga.ga_data + ga.ga_len);
+ }
+ }
+
+ return (char *)ga.ga_data;
+}
+
static bool runtime_search_path_valid = false;
static int *runtime_search_path_ref = NULL;
static RuntimeSearchPath runtime_search_path;
@@ -36,7 +192,7 @@ void runtime_init(void)
void ex_runtime(exarg_T *eap)
{
char *arg = eap->arg;
- char *p = (char *)skiptowhite((char_u *)arg);
+ char *p = skiptowhite(arg);
ptrdiff_t len = p - arg;
int flags = eap->forceit ? DIP_ALL : 0;
@@ -70,9 +226,9 @@ static void source_callback(char *fname, void *cookie)
/// When "flags" has DIP_ERR: give an error message if there is no match.
///
/// return FAIL when no file could be sourced, OK otherwise.
-int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
+int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char_u *tail;
+ char *tail;
int num_files;
char **files;
int i;
@@ -80,20 +236,20 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
// Make a copy of 'runtimepath'. Invoking the callback may change the
// value.
- char_u *rtp_copy = vim_strsave(path);
+ char *rtp_copy = xstrdup(path);
char *buf = xmallocz(MAXPATHL);
{
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in \"%s\""), name, (char *)path);
+ smsg(_("Searching for \"%s\" in \"%s\""), name, path);
verbose_leave();
}
// Loop over all entries in 'runtimepath'.
- char_u *rtp = rtp_copy;
+ char *rtp = rtp_copy;
while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) {
// Copy the path from 'runtimepath' to buf[].
- copy_option_part((char **)&rtp, buf, MAXPATHL, ",");
+ copy_option_part(&rtp, buf, MAXPATHL, ",");
size_t buflen = STRLEN(buf);
// Skip after or non-after directories.
@@ -111,15 +267,14 @@ int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback,
did_one = true;
} else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
add_pathsep(buf);
- tail = (char_u *)buf + STRLEN(buf);
+ tail = buf + STRLEN(buf);
// Loop over all patterns in "name"
- char_u *np = (char_u *)name;
+ char *np = name;
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
- assert(MAXPATHL >= (tail - (char_u *)buf));
- copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - (char_u *)buf)),
- "\t ");
+ assert(MAXPATHL >= (tail - buf));
+ copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -208,19 +363,19 @@ void runtime_search_path_unref(RuntimeSearchPath path, int *ref)
/// When "flags" has DIP_ERR: give an error message if there is no match.
///
/// return FAIL when no file could be sourced, OK otherwise.
-int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie)
+int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char_u *tail;
+ char *tail;
int num_files;
char **files;
int i;
bool did_one = false;
- char_u buf[MAXPATHL];
+ char buf[MAXPATHL];
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in runtime path"), (char *)name);
+ smsg(_("Searching for \"%s\" in runtime path"), name);
verbose_leave();
}
@@ -244,15 +399,15 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
(*callback)(item.path, cookie);
} else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
STRCPY(buf, item.path);
- add_pathsep((char *)buf);
+ add_pathsep(buf);
tail = buf + STRLEN(buf);
// Loop over all patterns in "name"
- char_u *np = name;
+ char *np = name;
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
- copy_option_part((char **)&np, (char *)tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
+ copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)), "\t ");
if (p_verbose > 10) {
verbose_enter();
@@ -264,7 +419,7 @@ int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void
| (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0;
// Expand wildcards, invoke the callback for each match.
- char *(pat[]) = { (char *)buf };
+ char *(pat[]) = { buf };
if (gen_expand_wildcards(1, pat, &num_files, &files, ew_flags) == OK) {
for (i = 0; i < num_files; i++) {
(*callback)(files[i], cookie);
@@ -344,7 +499,7 @@ ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
if (lua) {
if (item->has_lua == kNone) {
size_t size = (size_t)snprintf(buf, buf_len, "%s/lua/", item->path);
- item->has_lua = (size < buf_len && os_isdir((char_u *)buf));
+ item->has_lua = (size < buf_len && os_isdir(buf));
}
if (item->has_lua == kFalse) {
continue;
@@ -379,13 +534,13 @@ done:
/// If "name" is NULL calls callback for each entry in "path". Cookie is
/// passed by reference in this case, setting it to NULL indicates that callback
/// has done its job.
-int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB callback,
- void *cookie)
+int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
int done = FAIL;
if ((flags & DIP_NORTP) == 0) {
- done |= do_in_path(path, (char *)((name && !*name) ? NULL : name), flags, callback, cookie);
+ done |= do_in_path(path, (name && !*name) ? NULL : name, flags, callback,
+ cookie);
}
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) {
@@ -470,7 +625,7 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_
}
static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used,
- CharVec *after_path, char_u *pack_entry, size_t pack_entry_len)
+ CharVec *after_path, char *pack_entry, size_t pack_entry_len)
{
static char buf[MAXPATHL];
char *(start_pat[]) = { "/pack/*/start/*", "/start/*" }; // NOLINT
@@ -509,10 +664,10 @@ RuntimeSearchPath runtime_search_path_build(void)
RuntimeSearchPath search_path = KV_INITIAL_VALUE;
CharVec after_path = KV_INITIAL_VALUE;
- static char_u buf[MAXPATHL];
- for (char *entry = (char *)p_pp; *entry != NUL;) {
+ static char buf[MAXPATHL];
+ for (char *entry = p_pp; *entry != NUL;) {
char *cur_entry = entry;
- copy_option_part(&entry, (char *)buf, MAXPATHL, ",");
+ copy_option_part(&entry, buf, MAXPATHL, ",");
String the_entry = { .data = cur_entry, .size = STRLEN(buf) };
@@ -521,20 +676,20 @@ RuntimeSearchPath runtime_search_path_build(void)
}
char *rtp_entry;
- for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL;) {
+ for (rtp_entry = p_rtp; *rtp_entry != NUL;) {
char *cur_entry = rtp_entry;
- copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
+ copy_option_part(&rtp_entry, buf, MAXPATHL, ",");
size_t buflen = STRLEN(buf);
- if (path_is_after((char *)buf, buflen)) {
+ if (path_is_after(buf, buflen)) {
rtp_entry = cur_entry;
break;
}
// fact: &rtp entries can contain wild chars
- expand_rtp_entry(&search_path, &rtp_used, (char *)buf, false);
+ expand_rtp_entry(&search_path, &rtp_used, buf, false);
- handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string((char *)buf), false);
+ handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string(buf), false);
if (h) {
(*h)++;
expand_pack_entry(&search_path, &rtp_used, &after_path, buf, buflen);
@@ -545,7 +700,7 @@ RuntimeSearchPath runtime_search_path_build(void)
String item = kv_A(pack_entries, i);
handle_T h = map_get(String, handle_T)(&pack_used, item);
if (h == 0) {
- expand_pack_entry(&search_path, &rtp_used, &after_path, (char_u *)item.data, item.size);
+ expand_pack_entry(&search_path, &rtp_used, &after_path, item.data, item.size);
}
}
@@ -557,8 +712,8 @@ RuntimeSearchPath runtime_search_path_build(void)
// "after" dirs in rtp
for (; *rtp_entry != NUL;) {
- copy_option_part(&rtp_entry, (char *)buf, MAXPATHL, ",");
- expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after((char *)buf, STRLEN(buf)));
+ copy_option_part(&rtp_entry, buf, MAXPATHL, ",");
+ expand_rtp_entry(&search_path, &rtp_used, buf, path_is_after(buf, STRLEN(buf)));
}
// strings are not owned
@@ -612,13 +767,13 @@ int do_in_runtimepath(char *name, int flags, DoInRuntimepathCB callback, void *c
{
int success = FAIL;
if (!(flags & DIP_NORTP)) {
- success |= do_in_cached_path((name && !*name) ? NULL : (char_u *)name, flags, callback, cookie);
+ success |= do_in_cached_path((name && !*name) ? NULL : name, flags, callback, cookie);
flags = (flags & ~DIP_START) | DIP_NORTP;
}
// TODO(bfredl): we could integrate disabled OPT dirs into the cached path
// which would effectivize ":packadd myoptpack" as well
if ((flags & (DIP_START|DIP_OPT)) && (success == FAIL || (flags & DIP_ALL))) {
- success |= do_in_path_and_pp(p_rtp, (char_u *)name, flags, callback, cookie);
+ success |= do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
}
return success;
}
@@ -634,7 +789,7 @@ int source_runtime(char *name, int flags)
}
/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
-int source_in_path(char_u *path, char_u *name, int flags)
+int source_in_path(char *path, char *name, int flags)
{
return do_in_path_and_pp(path, name, flags, source_callback, NULL);
}
@@ -658,17 +813,23 @@ static void source_all_matches(char *pat)
///
/// @param fname the package path
/// @param is_pack whether the added dir is a "pack/*/start/*/" style package
-static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
+static int add_pack_dir_to_rtp(char *fname, bool is_pack)
{
- char_u *p4, *p3, *p2, *p1, *p;
- char_u *buf = NULL;
+ char *p;
+ char *buf = NULL;
char *afterdir = NULL;
int retval = FAIL;
- p4 = p3 = p2 = p1 = get_past_head(fname);
+ char *p1 = get_past_head(fname);
+ char *p2 = p1;
+ char *p3 = p1;
+ char *p4 = p1;
for (p = p1; *p; MB_PTR_ADV(p)) {
if (vim_ispathsep_nocolon(*p)) {
- p4 = p3; p3 = p2; p2 = p1; p1 = p;
+ p4 = p3;
+ p3 = p2;
+ p2 = p1;
+ p1 = p;
}
}
@@ -678,9 +839,9 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
//
// find the part up to "pack" in 'runtimepath'
p4++; // append pathsep in order to expand symlink
- char_u c = *p4;
+ char c = *p4;
*p4 = NUL;
- char *const ffname = fix_fname((char *)fname);
+ char *const ffname = fix_fname(fname);
*p4 = c;
if (ffname == NULL) {
@@ -699,10 +860,10 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
for (const char *entry = (const char *)p_rtp; *entry != NUL;) {
const char *cur_entry = entry;
- copy_option_part((char **)&entry, (char *)buf, MAXPATHL, ",");
+ copy_option_part((char **)&entry, buf, MAXPATHL, ",");
if (insp == NULL) {
- add_pathsep((char *)buf);
- char *const rtp_ffname = fix_fname((char *)buf);
+ add_pathsep(buf);
+ char *const rtp_ffname = fix_fname(buf);
if (rtp_ffname == NULL) {
goto theend;
}
@@ -714,7 +875,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
}
}
- if ((p = (char_u *)strstr((char *)buf, "after")) != NULL
+ if ((p = strstr(buf, "after")) != NULL
&& p > buf
&& vim_ispathsep(p[-1])
&& (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) {
@@ -734,9 +895,9 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
}
// check if rtp/pack/name/start/name/after exists
- afterdir = concat_fnames((char *)fname, "after", true);
+ afterdir = concat_fnames(fname, "after", true);
size_t afterlen = 0;
- if (is_pack ? pack_has_entries((char_u *)afterdir) : os_isdir((char_u *)afterdir)) {
+ if (is_pack ? pack_has_entries(afterdir) : os_isdir(afterdir)) {
afterlen = strlen(afterdir) + 1; // add one for comma
}
@@ -789,7 +950,7 @@ static int add_pack_dir_to_rtp(char_u *fname, bool is_pack)
xstrlcat(new_rtp, afterdir, new_rtp_capacity);
}
- set_option_value("rtp", 0L, new_rtp, 0);
+ set_option_value_give_err("rtp", 0L, new_rtp, 0);
xfree(new_rtp);
retval = OK;
@@ -803,29 +964,29 @@ theend:
/// Load scripts in "plugin" directory of the package.
/// For opt packages, also load scripts in "ftdetect" (start packages already
/// load these from filetype.vim)
-static int load_pack_plugin(bool opt, char_u *fname)
+static int load_pack_plugin(bool opt, char *fname)
{
static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT
- char *const ffname = fix_fname((char *)fname);
+ char *const ffname = fix_fname(fname);
size_t len = strlen(ffname) + STRLEN(ftpat);
- char_u *pat = xmallocz(len);
+ char *pat = xmallocz(len);
- vim_snprintf((char *)pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT
- source_all_matches((char *)pat);
- vim_snprintf((char *)pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT
- source_all_matches((char *)pat);
+ vim_snprintf(pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT
+ source_all_matches(pat);
+ vim_snprintf(pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT
+ source_all_matches(pat);
- char_u *cmd = vim_strsave((char_u *)"g:did_load_filetypes");
+ char *cmd = xstrdup("g:did_load_filetypes");
// If runtime/filetype.vim wasn't loaded yet, the scripts will be
// found when it loads.
- if (opt && eval_to_number((char *)cmd) > 0) {
+ if (opt && eval_to_number(cmd) > 0) {
do_cmdline_cmd("augroup filetypedetect");
- vim_snprintf((char *)pat, len, ftpat, ffname);
- source_all_matches((char *)pat);
+ vim_snprintf(pat, len, ftpat, ffname);
+ source_all_matches(pat);
vim_snprintf((char *)pat, len, "%s/ftdetect/*.lua", ffname); // NOLINT
- source_all_matches((char *)pat);
+ source_all_matches(pat);
do_cmdline_cmd("augroup END");
}
xfree(cmd);
@@ -840,7 +1001,7 @@ static int APP_ADD_DIR;
static int APP_LOAD;
static int APP_BOTH;
-static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
+static void add_pack_plugin(bool opt, char *fname, void *cookie)
{
if (cookie != &APP_LOAD) {
char *buf = xmalloc(MAXPATHL);
@@ -849,7 +1010,7 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
const char *p = (const char *)p_rtp;
while (*p != NUL) {
copy_option_part((char **)&p, buf, MAXPATHL, ",");
- if (path_fnamecmp(buf, (char *)fname) == 0) {
+ if (path_fnamecmp(buf, fname) == 0) {
found = true;
break;
}
@@ -870,12 +1031,12 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie)
static void add_start_pack_plugin(char *fname, void *cookie)
{
- add_pack_plugin(false, (char_u *)fname, cookie);
+ add_pack_plugin(false, fname, cookie);
}
static void add_opt_pack_plugin(char *fname, void *cookie)
{
- add_pack_plugin(true, (char_u *)fname, cookie);
+ add_pack_plugin(true, fname, cookie);
}
/// Add all packages in the "start" directory to 'runtimepath'.
@@ -884,11 +1045,11 @@ void add_pack_start_dirs(void)
do_in_path(p_pp, NULL, DIP_ALL + DIP_DIR, add_pack_start_dir, NULL);
}
-static bool pack_has_entries(char_u *buf)
+static bool pack_has_entries(char *buf)
{
int num_files;
char **files;
- char *(pat[]) = { (char *)buf };
+ char *(pat[]) = { buf };
if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) {
FreeWild(num_files, files);
}
@@ -897,7 +1058,7 @@ static bool pack_has_entries(char_u *buf)
static void add_pack_start_dir(char *fname, void *cookie)
{
- static char_u buf[MAXPATHL];
+ static char buf[MAXPATHL];
char *(start_pat[]) = { "/start/*", "/pack/*/start/*" }; // NOLINT
for (int i = 0; i < 2; i++) {
if (STRLEN(fname) + STRLEN(start_pat[i]) + 1 > MAXPATHL) {
@@ -938,12 +1099,12 @@ void ex_packloadall(exarg_T *eap)
void load_plugins(void)
{
if (p_lpl) {
- char_u *rtp_copy = p_rtp;
- char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT
- char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT
+ char *rtp_copy = p_rtp;
+ char *const plugin_pattern_vim = "plugin/**/*.vim"; // NOLINT
+ char *const plugin_pattern_lua = "plugin/**/*.lua"; // NOLINT
if (!did_source_packages) {
- rtp_copy = vim_strsave(p_rtp);
+ rtp_copy = xstrdup(p_rtp);
add_pack_start_dirs();
}
@@ -960,8 +1121,8 @@ void load_plugins(void)
}
TIME_MSG("loading packages");
- source_runtime((char *)plugin_pattern_vim, DIP_ALL | DIP_AFTER);
- source_runtime((char *)plugin_pattern_lua, DIP_ALL | DIP_AFTER);
+ source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER);
+ source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER);
TIME_MSG("loading after plugins");
}
}
@@ -984,13 +1145,168 @@ void ex_packadd(exarg_T *eap)
vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
// The first round don't give a "not found" error, in the second round
// only when nothing was found in the first round.
- res = do_in_path(p_pp, pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
- round == 1 ? add_start_pack_plugin : add_opt_pack_plugin,
- eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
+ res =
+ do_in_path(p_pp, pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
+ round == 1 ? add_start_pack_plugin : add_opt_pack_plugin,
+ eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
xfree(pat);
}
}
+/// Expand color scheme, compiler or filetype names.
+/// Search from 'runtimepath':
+/// 'runtimepath'/{dirnames}/{pat}.vim
+/// When "flags" has DIP_START: search also from 'start' of 'packpath':
+/// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim
+/// When "flags" has DIP_OPT: search also from 'opt' of 'packpath':
+/// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim
+/// When "flags" has DIP_LUA: search also performed for .lua files
+/// "dirnames" is an array with one or more directory names.
+int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirnames[])
+{
+ *num_file = 0;
+ *file = NULL;
+ size_t pat_len = STRLEN(pat);
+
+ garray_T ga;
+ ga_init(&ga, (int)sizeof(char *), 10);
+
+ // TODO(bfredl): this is bullshit, exandpath should not reinvent path logic.
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 7;
+ char *s = xmalloc(size);
+ snprintf(s, size, "%s/%s*.vim", dirnames[i], pat);
+ globpath(p_rtp, s, &ga, 0);
+ if (flags & DIP_LUA) {
+ snprintf(s, size, "%s/%s*.lua", dirnames[i], pat);
+ globpath(p_rtp, s, &ga, 0);
+ }
+ xfree(s);
+ }
+
+ if (flags & DIP_START) {
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 22;
+ char *s = xmalloc(size);
+ snprintf(s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ if (flags & DIP_LUA) {
+ snprintf(s, size, "pack/*/start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ }
+ xfree(s);
+ }
+
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 22;
+ char *s = xmalloc(size);
+ snprintf(s, size, "start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ if (flags & DIP_LUA) {
+ snprintf(s, size, "start/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ }
+ xfree(s);
+ }
+ }
+
+ if (flags & DIP_OPT) {
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 20;
+ char *s = xmalloc(size);
+ snprintf(s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ if (flags & DIP_LUA) {
+ snprintf(s, size, "pack/*/opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ }
+ xfree(s);
+ }
+
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 20;
+ char *s = xmalloc(size);
+ snprintf(s, size, "opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ if (flags & DIP_LUA) {
+ snprintf(s, size, "opt/*/%s/%s*.lua", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ }
+ xfree(s);
+ }
+ }
+
+ for (int i = 0; i < ga.ga_len; i++) {
+ char *match = ((char **)ga.ga_data)[i];
+ char *s = match;
+ char *e = s + STRLEN(s);
+ if (e - s > 4 && (STRNICMP(e - 4, ".vim", 4) == 0
+ || ((flags & DIP_LUA)
+ && STRNICMP(e - 4, ".lua", 4) == 0))) {
+ e -= 4;
+ for (s = e; s > match; MB_PTR_BACK(match, s)) {
+ if (vim_ispathsep(*s)) {
+ break;
+ }
+ }
+ s++;
+ *e = NUL;
+ assert((e - s) + 1 >= 0);
+ memmove(match, s, (size_t)(e - s) + 1);
+ }
+ }
+
+ if (GA_EMPTY(&ga)) {
+ return FAIL;
+ }
+
+ // Sort and remove duplicates which can happen when specifying multiple
+ // directories in dirnames.
+ ga_remove_duplicate_strings(&ga);
+
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
+/// Expand loadplugin names:
+/// 'packpath'/pack/ * /opt/{pat}
+int ExpandPackAddDir(char *pat, int *num_file, char ***file)
+{
+ garray_T ga;
+
+ *num_file = 0;
+ *file = NULL;
+ size_t pat_len = STRLEN(pat);
+ ga_init(&ga, (int)sizeof(char *), 10);
+
+ size_t buflen = pat_len + 26;
+ char *s = xmalloc(buflen);
+ snprintf(s, buflen, "pack/*/opt/%s*", pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ snprintf(s, buflen, "opt/%s*", pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ xfree(s);
+
+ for (int i = 0; i < ga.ga_len; i++) {
+ char *match = ((char **)ga.ga_data)[i];
+ s = path_tail(match);
+ memmove(match, s, STRLEN(s) + 1);
+ }
+
+ if (GA_EMPTY(&ga)) {
+ return FAIL;
+ }
+
+ // Sort and remove duplicates which can happen when specifying multiple
+ // directories in dirnames.
+ ga_remove_duplicate_strings(&ga);
+
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
/// Append string with escaped commas
static char *strcpy_comma_escaped(char *dest, const char *src, const size_t len)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
@@ -1163,7 +1479,7 @@ char *get_lib_dir(void)
// TODO(bfredl): too fragile? Ideally default_lib_dir would be made empty
// in an appimage build
if (strlen(default_lib_dir) != 0
- && os_isdir((const char_u *)default_lib_dir)) {
+ && os_isdir(default_lib_dir)) {
return xstrdup(default_lib_dir);
}
@@ -1282,3 +1598,900 @@ freeall:
return rtp;
}
#undef NVIM_SIZE
+
+static void cmd_source(char *fname, exarg_T *eap)
+{
+ if (eap != NULL && *fname == NUL) {
+ cmd_source_buffer(eap);
+ } else if (eap != NULL && eap->forceit) {
+ // ":source!": read Normal mode commands
+ // Need to execute the commands directly. This is required at least
+ // for:
+ // - ":g" command busy
+ // - after ":argdo", ":windo" or ":bufdo"
+ // - another command follows
+ // - inside a loop
+ openscript(fname, global_busy || listcmd_busy || eap->nextcmd != NULL
+ || eap->cstack->cs_idx >= 0);
+
+ // ":source" read ex commands
+ } else if (do_source(fname, false, DOSO_NONE) == FAIL) {
+ semsg(_(e_notopen), fname);
+ }
+}
+
+/// ":source [{fname}]"
+void ex_source(exarg_T *eap)
+{
+ cmd_source(eap->arg, eap);
+}
+
+/// ":options"
+void ex_options(exarg_T *eap)
+{
+ char buf[500];
+ bool multi_mods = 0;
+
+ buf[0] = NUL;
+ (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods);
+
+ os_setenv("OPTWIN_CMD", buf, 1);
+ cmd_source(SYS_OPTWIN_FILE, NULL);
+}
+
+/// ":source" and associated commands.
+///
+/// @return address holding the next breakpoint line for a source cookie
+linenr_T *source_breakpoint(void *cookie)
+{
+ return &((struct source_cookie *)cookie)->breakpoint;
+}
+
+/// @return the address holding the debug tick for a source cookie.
+int *source_dbg_tick(void *cookie)
+{
+ return &((struct source_cookie *)cookie)->dbg_tick;
+}
+
+/// @return the nesting level for a source cookie.
+int source_level(void *cookie)
+ FUNC_ATTR_PURE
+{
+ return ((struct source_cookie *)cookie)->level;
+}
+
+/// Special function to open a file without handle inheritance.
+/// If possible the handle is closed on exec().
+static FILE *fopen_noinh_readbin(char *filename)
+{
+#ifdef WIN32
+ int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0);
+#else
+ int fd_tmp = os_open(filename, O_RDONLY, 0);
+#endif
+
+ if (fd_tmp < 0) {
+ return NULL;
+ }
+
+ (void)os_set_cloexec(fd_tmp);
+
+ return fdopen(fd_tmp, READBIN);
+}
+
+/// Concatenate VimL line if it starts with a line continuation into a growarray
+/// (excluding the continuation chars and leading whitespace)
+///
+/// @note Growsize of the growarray may be changed to speed up concatenations!
+///
+/// @param ga the growarray to append to
+/// @param init_growsize the starting growsize value of the growarray
+/// @param p pointer to the beginning of the line to consider
+/// @param len the length of this line
+///
+/// @return true if this line did begin with a continuation (the next line
+/// should also be considered, if it exists); false otherwise
+static bool concat_continued_line(garray_T *const ga, const int init_growsize, const char *const p,
+ size_t len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ const char *const line = skipwhite_len((char *)p, len);
+ len -= (size_t)(line - p);
+ // Skip lines starting with '\" ', concat lines starting with '\'
+ if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) {
+ return true;
+ } else if (len == 0 || line[0] != '\\') {
+ return false;
+ }
+ if (ga->ga_len > init_growsize) {
+ ga_set_growsize(ga, MIN(ga->ga_len, 8000));
+ }
+ ga_concat_len(ga, line + 1, len - 1);
+ return true;
+}
+
+typedef struct {
+ linenr_T curr_lnum;
+ const linenr_T final_lnum;
+} GetBufferLineCookie;
+
+typedef struct {
+ char *buf;
+ size_t offset;
+} GetStrLineCookie;
+
+/// Get one full line from a sourced string (in-memory, no file).
+/// Called by do_cmdline() when it's called from do_source_str().
+///
+/// @return pointer to allocated line, or NULL for end-of-file or
+/// some error.
+static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
+{
+ GetStrLineCookie *p = cookie;
+ if (STRLEN(p->buf) <= p->offset) {
+ return NULL;
+ }
+ const char *line = p->buf + p->offset;
+ const char *eol = skip_to_newline(line);
+ garray_T ga;
+ ga_init(&ga, sizeof(char), 400);
+ ga_concat_len(&ga, line, (size_t)(eol - line));
+ if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
+ while (eol[0] != NUL) {
+ line = eol + 1;
+ const char *const next_eol = skip_to_newline(line);
+ if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
+ break;
+ }
+ eol = (char *)next_eol;
+ }
+ }
+ ga_append(&ga, NUL);
+ p->offset = (size_t)(eol - p->buf) + 1;
+ return ga.ga_data;
+}
+
+/// Create a new script item and allocate script-local vars. @see new_script_vars
+///
+/// @param name File name of the script. NULL for anonymous :source.
+/// @param[out] sid_out SID of the new item.
+///
+/// @return pointer to the created script item.
+scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
+{
+ static scid_T last_current_SID = 0;
+ const scid_T sid = ++last_current_SID;
+ if (sid_out != NULL) {
+ *sid_out = sid;
+ }
+ ga_grow(&script_items, sid - script_items.ga_len);
+ while (script_items.ga_len < sid) {
+ script_items.ga_len++;
+ SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
+ SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
+ }
+ SCRIPT_ITEM(sid).sn_name = name;
+ new_script_vars(sid); // Allocate the local script variables to use for this script.
+ return &SCRIPT_ITEM(sid);
+}
+
+static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
+{
+ char *save_sourcing_name = SOURCING_NAME;
+ linenr_T save_sourcing_lnum = SOURCING_LNUM;
+ char sourcing_name_buf[256];
+ char *sname;
+ if (save_sourcing_name == NULL) {
+ sname = (char *)traceback_name;
+ } else {
+ snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf),
+ "%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name,
+ save_sourcing_lnum);
+ sname = sourcing_name_buf;
+ }
+ estack_push(ETYPE_SCRIPT, sname, 0);
+
+ const sctx_T save_current_sctx = current_sctx;
+ if (current_sctx.sc_sid != SID_LUA) {
+ current_sctx.sc_sid = SID_STR;
+ }
+ current_sctx.sc_seq = 0;
+ current_sctx.sc_lnum = save_sourcing_lnum;
+ funccal_entry_T entry;
+ save_funccal(&entry);
+ int retval = do_cmdline(NULL, fgetline, cookie,
+ DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
+ estack_pop();
+ current_sctx = save_current_sctx;
+ restore_funccal();
+ return retval;
+}
+
+static void cmd_source_buffer(const exarg_T *const eap)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (curbuf == NULL) {
+ return;
+ }
+ garray_T ga;
+ ga_init(&ga, sizeof(char), 400);
+ const linenr_T final_lnum = eap->line2;
+ // Copy the contents to be executed.
+ for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
+ // Adjust growsize to current length to speed up concatenating many lines.
+ if (ga.ga_len > 400) {
+ ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
+ }
+ ga_concat(&ga, (char *)ml_get(curr_lnum));
+ ga_append(&ga, NL);
+ }
+ ((char *)ga.ga_data)[ga.ga_len - 1] = NUL;
+ const GetStrLineCookie cookie = {
+ .buf = ga.ga_data,
+ .offset = 0,
+ };
+ if (curbuf->b_fname
+ && path_with_extension((const char *)curbuf->b_fname, "lua")) {
+ nlua_source_using_linegetter(get_str_line, (void *)&cookie,
+ ":source (no file)");
+ } else {
+ source_using_linegetter((void *)&cookie, get_str_line,
+ ":source (no file)");
+ }
+ ga_clear(&ga);
+}
+
+/// Executes lines in `src` as Ex commands.
+///
+/// @see do_source()
+int do_source_str(const char *cmd, const char *traceback_name)
+{
+ GetStrLineCookie cookie = {
+ .buf = (char *)cmd,
+ .offset = 0,
+ };
+ return source_using_linegetter((void *)&cookie, get_str_line, traceback_name);
+}
+
+/// When fname is a 'lua' file nlua_exec_file() is invoked to source it.
+/// Otherwise reads the file `fname` and executes its lines as Ex commands.
+///
+/// This function may be called recursively!
+///
+/// @see do_source_str
+///
+/// @param fname
+/// @param check_other check for .vimrc and _vimrc
+/// @param is_vimrc DOSO_ value
+///
+/// @return FAIL if file could not be opened, OK otherwise
+int do_source(char *fname, int check_other, int is_vimrc)
+{
+ struct source_cookie cookie;
+ char *p;
+ char *fname_exp;
+ uint8_t *firstline = NULL;
+ int retval = FAIL;
+ int save_debug_break_level = debug_break_level;
+ scriptitem_T *si = NULL;
+ proftime_T wait_start;
+ bool trigger_source_post = false;
+
+ p = expand_env_save(fname);
+ if (p == NULL) {
+ return retval;
+ }
+ fname_exp = fix_fname(p);
+ xfree(p);
+ if (fname_exp == NULL) {
+ return retval;
+ }
+ if (os_isdir(fname_exp)) {
+ smsg(_("Cannot source a directory: \"%s\""), fname);
+ goto theend;
+ }
+
+ // Apply SourceCmd autocommands, they should get the file and source it.
+ if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
+ && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
+ false, curbuf)) {
+ retval = aborting() ? FAIL : OK;
+ if (retval == OK) {
+ // Apply SourcePost autocommands.
+ apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
+ }
+ goto theend;
+ }
+
+ // Apply SourcePre autocommands, they may get the file.
+ apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf);
+
+ cookie.fp = fopen_noinh_readbin(fname_exp);
+ if (cookie.fp == NULL && check_other) {
+ // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
+ // and ".exrc" by "_exrc" or vice versa.
+ p = path_tail(fname_exp);
+ if ((*p == '.' || *p == '_')
+ && (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) {
+ *p = (*p == '_') ? '.' : '_';
+ cookie.fp = fopen_noinh_readbin(fname_exp);
+ }
+ }
+
+ if (cookie.fp == NULL) {
+ if (p_verbose > 1) {
+ verbose_enter();
+ if (SOURCING_NAME == NULL) {
+ smsg(_("could not source \"%s\""), fname);
+ } else {
+ smsg(_("line %" PRId64 ": could not source \"%s\""),
+ (int64_t)SOURCING_LNUM, fname);
+ }
+ verbose_leave();
+ }
+ goto theend;
+ }
+
+ // The file exists.
+ // - In verbose mode, give a message.
+ // - For a vimrc file, may want to call vimrc_found().
+ if (p_verbose > 1) {
+ verbose_enter();
+ if (SOURCING_NAME == NULL) {
+ smsg(_("sourcing \"%s\""), fname);
+ } else {
+ smsg(_("line %" PRId64 ": sourcing \"%s\""), (int64_t)SOURCING_LNUM, fname);
+ }
+ verbose_leave();
+ }
+ if (is_vimrc == DOSO_VIMRC) {
+ vimrc_found(fname_exp, "MYVIMRC");
+ }
+
+#ifdef USE_CRNL
+ // If no automatic file format: Set default to CR-NL.
+ if (*p_ffs == NUL) {
+ cookie.fileformat = EOL_DOS;
+ } else {
+ cookie.fileformat = EOL_UNKNOWN;
+ }
+ cookie.error = false;
+#endif
+
+ cookie.nextline = NULL;
+ cookie.sourcing_lnum = 0;
+ cookie.finished = false;
+
+ // Check if this script has a breakpoint.
+ cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, (linenr_T)0);
+ cookie.fname = fname_exp;
+ cookie.dbg_tick = debug_tick;
+
+ cookie.level = ex_nesting_level;
+
+ // start measuring script load time if --startuptime was passed and
+ // time_fd was successfully opened afterwards.
+ proftime_T rel_time;
+ proftime_T start_time;
+ FILE * const l_time_fd = time_fd;
+ if (l_time_fd != NULL) {
+ time_push(&rel_time, &start_time);
+ }
+
+ const int l_do_profiling = do_profiling;
+ if (l_do_profiling == PROF_YES) {
+ prof_child_enter(&wait_start); // entering a child now
+ }
+
+ // Don't use local function variables, if called from a function.
+ // Also starts profiling timer for nested script.
+ funccal_entry_T funccalp_entry;
+ save_funccal(&funccalp_entry);
+
+ const sctx_T save_current_sctx = current_sctx;
+ si = get_current_script_id(&fname_exp, &current_sctx);
+
+ // Keep the sourcing name/lnum, for recursive calls.
+ estack_push(ETYPE_SCRIPT, si->sn_name, 0);
+
+ if (l_do_profiling == PROF_YES) {
+ bool forceit = false;
+
+ // Check if we do profiling for this script.
+ if (!si->sn_prof_on && has_profiling(true, si->sn_name, &forceit)) {
+ profile_init(si);
+ si->sn_pr_force = forceit;
+ }
+ if (si->sn_prof_on) {
+ si->sn_pr_count++;
+ si->sn_pr_start = profile_start();
+ si->sn_pr_children = profile_zero();
+ }
+ }
+
+ cookie.conv.vc_type = CONV_NONE; // no conversion
+
+ if (path_with_extension((const char *)fname_exp, "lua")) {
+ const sctx_T current_sctx_backup = current_sctx;
+ current_sctx.sc_sid = SID_LUA;
+ current_sctx.sc_lnum = 0;
+ // Source the file as lua
+ nlua_exec_file((const char *)fname_exp);
+ current_sctx = current_sctx_backup;
+ } else {
+ // Read the first line so we can check for a UTF-8 BOM.
+ firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true);
+ if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef
+ && firstline[1] == 0xbb && firstline[2] == 0xbf) {
+ // Found BOM; setup conversion, skip over BOM and recode the line.
+ convert_setup(&cookie.conv, "utf-8", p_enc);
+ p = string_convert(&cookie.conv, (char *)firstline + 3, NULL);
+ if (p == NULL) {
+ p = xstrdup((char *)firstline + 3);
+ }
+ xfree(firstline);
+ firstline = (uint8_t *)p;
+ }
+ // Call do_cmdline, which will call getsourceline() to get the lines.
+ do_cmdline((char *)firstline, getsourceline, (void *)&cookie,
+ DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
+ }
+ retval = OK;
+
+ if (l_do_profiling == PROF_YES) {
+ // Get "si" again, "script_items" may have been reallocated.
+ si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ if (si->sn_prof_on) {
+ si->sn_pr_start = profile_end(si->sn_pr_start);
+ si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start);
+ si->sn_pr_total = profile_add(si->sn_pr_total, si->sn_pr_start);
+ si->sn_pr_self = profile_self(si->sn_pr_self, si->sn_pr_start,
+ si->sn_pr_children);
+ }
+ }
+
+ if (got_int) {
+ emsg(_(e_interr));
+ }
+ estack_pop();
+ if (p_verbose > 1) {
+ verbose_enter();
+ smsg(_("finished sourcing %s"), fname);
+ if (SOURCING_NAME != NULL) {
+ smsg(_("continuing in %s"), SOURCING_NAME);
+ }
+ verbose_leave();
+ }
+
+ if (l_time_fd != NULL) {
+ vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname);
+ time_msg((char *)IObuff, &start_time);
+ time_pop(rel_time);
+ }
+
+ if (!got_int) {
+ trigger_source_post = true;
+ }
+
+ // After a "finish" in debug mode, need to break at first command of next
+ // sourced file.
+ if (save_debug_break_level > ex_nesting_level
+ && debug_break_level == ex_nesting_level) {
+ debug_break_level++;
+ }
+
+ current_sctx = save_current_sctx;
+ restore_funccal();
+ if (l_do_profiling == PROF_YES) {
+ prof_child_exit(&wait_start); // leaving a child now
+ }
+ fclose(cookie.fp);
+ xfree(cookie.nextline);
+ xfree(firstline);
+ convert_setup(&cookie.conv, NULL, NULL);
+
+ if (trigger_source_post) {
+ apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
+ }
+
+theend:
+ xfree(fname_exp);
+ return retval;
+}
+
+/// Check if fname was sourced before to finds its SID.
+/// If it's new, generate a new SID.
+///
+/// @param[in,out] fnamep pointer to file path of script
+/// @param[out] ret_sctx sctx of this script
+scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
+{
+ static int last_current_SID_seq = 0;
+
+ sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq,
+ .sc_lnum = 0,
+ .sc_sid = 0 };
+ scriptitem_T *si = NULL;
+
+ assert(script_items.ga_len >= 0);
+ for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) {
+ // We used to check inode here, but that doesn't work:
+ // - If a script is edited and written, it may get a different
+ // inode number, even though to the user it is the same script.
+ // - If a script is deleted and another script is written, with a
+ // different name, the inode may be re-used.
+ si = &SCRIPT_ITEM(script_sctx.sc_sid);
+ if (si->sn_name != NULL && FNAMECMP(si->sn_name, *fnamep) == 0) {
+ // Found it!
+ break;
+ }
+ }
+ if (script_sctx.sc_sid == 0) {
+ si = new_script_item(*fnamep, &script_sctx.sc_sid);
+ *fnamep = xstrdup(si->sn_name);
+ }
+ if (ret_sctx != NULL) {
+ *ret_sctx = script_sctx;
+ }
+
+ return si;
+}
+
+/// ":scriptnames"
+void ex_scriptnames(exarg_T *eap)
+{
+ if (eap->addr_count > 0) {
+ // :script {scriptId}: edit the script
+ if (eap->line2 < 1 || eap->line2 > script_items.ga_len) {
+ emsg(_(e_invarg));
+ } else {
+ eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
+ do_exedit(eap, NULL);
+ }
+ return;
+ }
+
+ for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
+ if (SCRIPT_ITEM(i).sn_name != NULL) {
+ home_replace(NULL, SCRIPT_ITEM(i).sn_name, (char *)NameBuff, MAXPATHL, true);
+ vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff);
+ if (!message_filtered((char *)IObuff)) {
+ msg_putchar('\n');
+ msg_outtrans((char *)IObuff);
+ line_breakcheck();
+ }
+ }
+ }
+}
+
+#if defined(BACKSLASH_IN_FILENAME)
+/// Fix slashes in the list of script names for 'shellslash'.
+void scriptnames_slash_adjust(void)
+{
+ for (int i = 1; i <= script_items.ga_len; i++) {
+ if (SCRIPT_ITEM(i).sn_name != NULL) {
+ slash_adjust(SCRIPT_ITEM(i).sn_name);
+ }
+ }
+}
+
+#endif
+
+/// Get a pointer to a script name. Used for ":verbose set".
+/// Message appended to "Last set from "
+char *get_scriptname(LastSet last_set, bool *should_free)
+{
+ *should_free = false;
+
+ switch (last_set.script_ctx.sc_sid) {
+ case SID_MODELINE:
+ return _("modeline");
+ case SID_CMDARG:
+ return _("--cmd argument");
+ case SID_CARG:
+ return _("-c argument");
+ case SID_ENV:
+ return _("environment variable");
+ case SID_ERROR:
+ return _("error handler");
+ case SID_WINLAYOUT:
+ return _("changed window size");
+ case SID_LUA:
+ return _("Lua");
+ case SID_API_CLIENT:
+ snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id);
+ return (char *)IObuff;
+ case SID_STR:
+ return _("anonymous :source");
+ default: {
+ char *const sname = SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
+ if (sname == NULL) {
+ snprintf((char *)IObuff, IOSIZE, _("anonymous :source (script id %d)"),
+ last_set.script_ctx.sc_sid);
+ return (char *)IObuff;
+ }
+
+ *should_free = true;
+ return home_replace_save(NULL, sname);
+ }
+ }
+}
+
+#if defined(EXITFREE)
+void free_scriptnames(void)
+{
+ profile_reset();
+
+# define FREE_SCRIPTNAME(item) xfree((item)->sn_name)
+ GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME);
+}
+#endif
+
+linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie)
+ FUNC_ATTR_PURE
+{
+ return fgetline == getsourceline
+ ? ((struct source_cookie *)cookie)->sourcing_lnum
+ : SOURCING_LNUM;
+}
+
+/// Get one full line from a sourced file.
+/// Called by do_cmdline() when it's called from do_source().
+///
+/// @return pointer to the line in allocated memory, or NULL for end-of-file or
+/// some error.
+char *getsourceline(int c, void *cookie, int indent, bool do_concat)
+{
+ struct source_cookie *sp = (struct source_cookie *)cookie;
+ char *line;
+ char *p;
+
+ // If breakpoints have been added/deleted need to check for it.
+ if (sp->dbg_tick < debug_tick) {
+ sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM);
+ sp->dbg_tick = debug_tick;
+ }
+ if (do_profiling == PROF_YES) {
+ script_line_end();
+ }
+ // Set the current sourcing line number.
+ SOURCING_LNUM = sp->sourcing_lnum + 1;
+ // Get current line. If there is a read-ahead line, use it, otherwise get
+ // one now.
+ if (sp->finished) {
+ line = NULL;
+ } else if (sp->nextline == NULL) {
+ line = get_one_sourceline(sp);
+ } else {
+ line = sp->nextline;
+ sp->nextline = NULL;
+ sp->sourcing_lnum++;
+ }
+ if (line != NULL && do_profiling == PROF_YES) {
+ script_line_start();
+ }
+
+ // Only concatenate lines starting with a \ when 'cpoptions' doesn't
+ // contain the 'C' flag.
+ if (line != NULL && do_concat && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) {
+ // compensate for the one line read-ahead
+ sp->sourcing_lnum--;
+
+ // Get the next line and concatenate it when it starts with a
+ // backslash. We always need to read the next line, keep it in
+ // sp->nextline.
+ // Also check for a comment in between continuation lines: "\ .
+ sp->nextline = get_one_sourceline(sp);
+ if (sp->nextline != NULL
+ && (*(p = skipwhite(sp->nextline)) == '\\'
+ || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) {
+ garray_T ga;
+
+ ga_init(&ga, (int)sizeof(char), 400);
+ ga_concat(&ga, line);
+ while (sp->nextline != NULL
+ && concat_continued_line(&ga, 400, sp->nextline, STRLEN(sp->nextline))) {
+ xfree(sp->nextline);
+ sp->nextline = get_one_sourceline(sp);
+ }
+ ga_append(&ga, NUL);
+ xfree(line);
+ line = ga.ga_data;
+ }
+ }
+
+ if (line != NULL && sp->conv.vc_type != CONV_NONE) {
+ char *s;
+
+ // Convert the encoding of the script line.
+ s = string_convert(&sp->conv, line, NULL);
+ if (s != NULL) {
+ xfree(line);
+ line = s;
+ }
+ }
+
+ // Did we encounter a breakpoint?
+ if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) {
+ dbg_breakpoint(sp->fname, SOURCING_LNUM);
+ // Find next breakpoint.
+ sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM);
+ sp->dbg_tick = debug_tick;
+ }
+
+ return line;
+}
+
+static char *get_one_sourceline(struct source_cookie *sp)
+{
+ garray_T ga;
+ int len;
+ int c;
+ char *buf;
+#ifdef USE_CRNL
+ int has_cr; // CR-LF found
+#endif
+ bool have_read = false;
+
+ // use a growarray to store the sourced line
+ ga_init(&ga, 1, 250);
+
+ // Loop until there is a finished line (or end-of-file).
+ sp->sourcing_lnum++;
+ for (;;) {
+ // make room to read at least 120 (more) characters
+ ga_grow(&ga, 120);
+ buf = ga.ga_data;
+
+retry:
+ errno = 0;
+ if (fgets(buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
+ sp->fp) == NULL) {
+ if (errno == EINTR) {
+ goto retry;
+ }
+
+ break;
+ }
+ len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
+#ifdef USE_CRNL
+ // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
+ // CTRL-Z by its own, or after a NL.
+ if ((len == 1 || (len >= 2 && buf[len - 2] == '\n'))
+ && sp->fileformat == EOL_DOS
+ && buf[len - 1] == Ctrl_Z) {
+ buf[len - 1] = NUL;
+ break;
+ }
+#endif
+
+ have_read = true;
+ ga.ga_len = len;
+
+ // If the line was longer than the buffer, read more.
+ if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') {
+ continue;
+ }
+
+ if (len >= 1 && buf[len - 1] == '\n') { // remove trailing NL
+#ifdef USE_CRNL
+ has_cr = (len >= 2 && buf[len - 2] == '\r');
+ if (sp->fileformat == EOL_UNKNOWN) {
+ if (has_cr) {
+ sp->fileformat = EOL_DOS;
+ } else {
+ sp->fileformat = EOL_UNIX;
+ }
+ }
+
+ if (sp->fileformat == EOL_DOS) {
+ if (has_cr) { // replace trailing CR
+ buf[len - 2] = '\n';
+ len--;
+ ga.ga_len--;
+ } else { // lines like ":map xx yy^M" will have failed
+ if (!sp->error) {
+ msg_source(HL_ATTR(HLF_W));
+ emsg(_("W15: Warning: Wrong line separator, ^M may be missing"));
+ }
+ sp->error = true;
+ sp->fileformat = EOL_UNIX;
+ }
+ }
+#endif
+ // The '\n' is escaped if there is an odd number of ^V's just
+ // before it, first set "c" just before the 'V's and then check
+ // len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo
+ for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) {}
+ if ((len & 1) != (c & 1)) { // escaped NL, read more
+ sp->sourcing_lnum++;
+ continue;
+ }
+
+ buf[len - 1] = NUL; // remove the NL
+ }
+
+ // Check for ^C here now and then, so recursive :so can be broken.
+ line_breakcheck();
+ break;
+ }
+
+ if (have_read) {
+ return ga.ga_data;
+ }
+
+ xfree(ga.ga_data);
+ return NULL;
+}
+
+/// ":scriptencoding": Set encoding conversion for a sourced script.
+/// Without the multi-byte feature it's simply ignored.
+void ex_scriptencoding(exarg_T *eap)
+{
+ struct source_cookie *sp;
+ char *name;
+
+ if (!getline_equal(eap->getline, eap->cookie, getsourceline)) {
+ emsg(_("E167: :scriptencoding used outside of a sourced file"));
+ return;
+ }
+
+ if (*eap->arg != NUL) {
+ name = enc_canonize(eap->arg);
+ } else {
+ name = eap->arg;
+ }
+
+ // Setup for conversion from the specified encoding to 'encoding'.
+ sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie);
+ convert_setup(&sp->conv, name, p_enc);
+
+ if (name != eap->arg) {
+ xfree(name);
+ }
+}
+
+/// ":finish": Mark a sourced file as finished.
+void ex_finish(exarg_T *eap)
+{
+ if (getline_equal(eap->getline, eap->cookie, getsourceline)) {
+ do_finish(eap, false);
+ } else {
+ emsg(_("E168: :finish used outside of a sourced file"));
+ }
+}
+
+/// Mark a sourced file as finished. Possibly makes the ":finish" pending.
+/// Also called for a pending finish at the ":endtry" or after returning from
+/// an extra do_cmdline(). "reanimate" is used in the latter case.
+void do_finish(exarg_T *eap, int reanimate)
+{
+ int idx;
+
+ if (reanimate) {
+ ((struct source_cookie *)getline_cookie(eap->getline,
+ eap->cookie))->finished = false;
+ }
+
+ // Cleanup (and deactivate) conditionals, but stop when a try conditional
+ // not in its finally clause (which then is to be executed next) is found.
+ // In this case, make the ":finish" pending for execution at the ":endtry".
+ // Otherwise, finish normally.
+ idx = cleanup_conditionals(eap->cstack, 0, true);
+ if (idx >= 0) {
+ eap->cstack->cs_pending[idx] = CSTP_FINISH;
+ report_make_pending(CSTP_FINISH, NULL);
+ } else {
+ ((struct source_cookie *)getline_cookie(eap->getline,
+ eap->cookie))->finished = true;
+ }
+}
+
+/// @return true when a sourced file had the ":finish" command: Don't give error
+/// message for missing ":endif".
+/// false when not sourcing a file.
+bool source_finished(LineGetter fgetline, void *cookie)
+{
+ return getline_equal(fgetline, cookie, getsourceline)
+ && ((struct source_cookie *)getline_cookie(fgetline, cookie))->finished;
+}
diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h
index d83ec00185..053c71212e 100644
--- a/src/nvim/runtime.h
+++ b/src/nvim/runtime.h
@@ -3,7 +3,77 @@
#include <stdbool.h>
-#include "nvim/ex_docmd.h"
+#include "nvim/autocmd.h"
+#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_eval_defs.h"
+
+typedef enum {
+ ETYPE_TOP, ///< toplevel
+ ETYPE_SCRIPT, ///< sourcing script, use es_info.sctx
+ ETYPE_UFUNC, ///< user function, use es_info.ufunc
+ ETYPE_AUCMD, ///< autocomand, use es_info.aucmd
+ ETYPE_MODELINE, ///< modeline, use es_info.sctx
+ ETYPE_EXCEPT, ///< exception, use es_info.exception
+ ETYPE_ARGS, ///< command line argument
+ ETYPE_ENV, ///< environment variable
+ ETYPE_INTERNAL, ///< internal operation
+ ETYPE_SPELL, ///< loading spell file
+} etype_T;
+
+/// Entry in the execution stack "exestack".
+typedef struct {
+ linenr_T es_lnum; ///< replaces "sourcing_lnum"
+ char *es_name; ///< replaces "sourcing_name"
+ etype_T es_type;
+ union {
+ sctx_T *sctx; ///< script and modeline info
+ ufunc_T *ufunc; ///< function info
+ AutoPatCmd *aucmd; ///< autocommand info
+ except_T *except; ///< exception info
+ } es_info;
+} estack_T;
+
+/// Stack of execution contexts. Each entry is an estack_T.
+/// Current context is at ga_len - 1.
+extern garray_T exestack;
+/// name of error message source
+#define SOURCING_NAME (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_name)
+/// line number in the message source or zero
+#define SOURCING_LNUM (((estack_T *)exestack.ga_data)[exestack.ga_len - 1].es_lnum)
+
+/// Argument for estack_sfile().
+typedef enum {
+ ESTACK_NONE,
+ ESTACK_SFILE,
+ ESTACK_STACK,
+ ESTACK_SCRIPT,
+} estack_arg_T;
+
+typedef struct scriptitem_S {
+ char *sn_name;
+ bool sn_prof_on; ///< true when script is/was profiled
+ bool sn_pr_force; ///< forceit: profile functions in this script
+ proftime_T sn_pr_child; ///< time set when going into first child
+ int sn_pr_nest; ///< nesting for sn_pr_child
+ // profiling the script as a whole
+ int sn_pr_count; ///< nr of times sourced
+ proftime_T sn_pr_total; ///< time spent in script + children
+ proftime_T sn_pr_self; ///< time spent in script itself
+ proftime_T sn_pr_start; ///< time at script start
+ proftime_T sn_pr_children; ///< time in children after script start
+ // profiling the script per line
+ garray_T sn_prl_ga; ///< things stored for every line
+ proftime_T sn_prl_start; ///< start time for current line
+ proftime_T sn_prl_children; ///< time spent in children for this line
+ proftime_T sn_prl_wait; ///< wait start time for current line
+ linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
+ int sn_prl_execed; ///< line being timed was executed
+} scriptitem_T;
+
+/// Growarray to store info about already sourced scripts.
+extern garray_T script_items;
+#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1])
typedef void (*DoInRuntimepathCB)(char *, void *);
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 0d3b936c48..d268dde845 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -1,629 +1,55 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-// screen.c: code for displaying on the screen
-//
+// screen.c: Lower level code for displaying on the screen.
+// grid.c contains some other lower-level code.
+
// Output to the screen (console, terminal emulator or GUI window) is minimized
// by remembering what is already on the screen, and only updating the parts
// that changed.
-//
-// The grid_*() functions write to the screen and handle updating grid->lines[].
-//
-// update_screen() is the function that updates all windows and status lines.
-// It is called from the main loop when must_redraw is non-zero. It may be
-// called from other places when an immediate screen update is needed.
-//
-// The part of the buffer that is displayed in a window is set with:
-// - w_topline (first buffer line in window)
-// - w_topfill (filler lines above the first line)
-// - w_leftcol (leftmost window cell in window),
-// - w_skipcol (skipped window cells of first line)
-//
-// Commands that only move the cursor around in a window, do not need to take
-// action to update the display. The main loop will check if w_topline is
-// valid and update it (scroll the window) when needed.
-//
-// Commands that scroll a window change w_topline and must call
-// check_cursor() to move the cursor into the visible part of the window, and
-// call redraw_later(wp, VALID) to have the window displayed by update_screen()
-// later.
-//
-// Commands that change text in the buffer must call changed_bytes() or
-// changed_lines() to mark the area that changed and will require updating
-// later. The main loop will call update_screen(), which will update each
-// window that shows the changed buffer. This assumes text above the change
-// can remain displayed as it is. Text after the change may need updating for
-// scrolling, folding and syntax highlighting.
-//
-// Commands that change how a window is displayed (e.g., setting 'list') or
-// invalidate the contents of a window in another way (e.g., change fold
-// settings), must call redraw_later(wp, NOT_VALID) to have the whole window
-// redisplayed by update_screen() later.
-//
-// Commands that change how a buffer is displayed (e.g., setting 'tabstop')
-// must call redraw_curbuf_later(NOT_VALID) to have all the windows for the
-// buffer redisplayed by update_screen() later.
-//
-// Commands that change highlighting and possibly cause a scroll too must call
-// redraw_later(wp, SOME_VALID) to update the whole window but still use
-// scrolling to avoid redrawing everything. But the length of displayed lines
-// must not change, use NOT_VALID then.
-//
-// Commands that move the window position must call redraw_later(wp, NOT_VALID).
-// TODO(neovim): should minimize redrawing by scrolling when possible.
-//
-// Commands that change everything (e.g., resizing the screen) must call
-// redraw_all_later(NOT_VALID) or redraw_all_later(CLEAR).
-//
-// Things that are handled indirectly:
-// - When messages scroll the screen up, msg_scrolled will be set and
-// update_screen() called to redraw.
-///
#include <assert.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
-#include "nvim/api/extmark.h"
-#include "nvim/api/private/helpers.h"
-#include "nvim/api/ui.h"
-#include "nvim/api/vim.h"
-#include "nvim/arabic.h"
-#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
-#include "nvim/cursor_shape.h"
-#include "nvim/decoration.h"
-#include "nvim/decoration_provider.h"
-#include "nvim/diff.h"
-#include "nvim/edit.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
-#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
-#include "nvim/grid_defs.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
-#include "nvim/indent.h"
-#include "nvim/insexpand.h"
-#include "nvim/lib/kvec.h"
-#include "nvim/log.h"
-#include "nvim/lua/executor.h"
-#include "nvim/main.h"
-#include "nvim/mark.h"
-#include "nvim/match.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
#include "nvim/menu.h"
-#include "nvim/message.h"
#include "nvim/move.h"
-#include "nvim/normal.h"
#include "nvim/option.h"
-#include "nvim/os/time.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/plines.h"
-#include "nvim/popupmnu.h"
-#include "nvim/quickfix.h"
+#include "nvim/optionstr.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
-#include "nvim/sign.h"
-#include "nvim/spell.h"
#include "nvim/state.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/terminal.h"
-#include "nvim/ui.h"
+#include "nvim/statusline.h"
#include "nvim/ui_compositor.h"
#include "nvim/undo.h"
-#include "nvim/version.h"
-#include "nvim/vim.h"
#include "nvim/window.h"
-#define MB_FILLER_CHAR '<' /* character used when a double-width character
- * doesn't fit. */
-
-static match_T search_hl; // used for 'hlsearch' highlight matching
-
-// for line_putchar. Contains the state that needs to be remembered from
-// putting one character to the next.
-typedef struct {
- const char *p;
- int prev_c; // previous Arabic character
- int prev_c1; // first composing char for prev_c
-} LineState;
-#define LINE_STATE(p) { p, 0, 0 }
-
-/// Whether to call "ui_call_grid_resize" in win_grid_alloc
-static bool send_grid_resize = false;
-
-static bool conceal_cursor_used = false;
-
-static bool redraw_popupmenu = false;
-static bool msg_grid_invalid = false;
-
-static bool resizing = false;
-
-typedef struct {
- NS ns_id;
- uint64_t mark_id;
- int win_row;
- int win_col;
-} WinExtmark;
-static kvec_t(WinExtmark) win_extmark_arr INIT(= KV_INITIAL_VALUE);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.c.generated.h"
#endif
-static char *provider_err = NULL;
-
-/// Redraw a window later, with update_screen(type).
-///
-/// Set must_redraw only if not already set to a higher value.
-/// e.g. if must_redraw is CLEAR, type NOT_VALID will do nothing.
-void redraw_later(win_T *wp, int type)
- FUNC_ATTR_NONNULL_ALL
-{
- if (!exiting && wp->w_redr_type < type) {
- wp->w_redr_type = type;
- if (type >= NOT_VALID) {
- wp->w_lines_valid = 0;
- }
- if (must_redraw < type) { // must_redraw is the maximum of all windows
- must_redraw = type;
- }
- }
-}
-
-/*
- * Mark all windows to be redrawn later.
- */
-void redraw_all_later(int type)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, type);
- }
- // This may be needed when switching tabs.
- if (must_redraw < type) {
- must_redraw = type;
- }
-}
-
-void screen_invalidate_highlights(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- redraw_later(wp, NOT_VALID);
- wp->w_grid_alloc.valid = false;
- }
-}
-
-/*
- * Mark all windows that are editing the current buffer to be updated later.
- */
-void redraw_curbuf_later(int type)
-{
- redraw_buf_later(curbuf, type);
-}
-
-void redraw_buf_later(buf_T *buf, int type)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf) {
- redraw_later(wp, type);
- }
- }
-}
-
-void redraw_buf_line_later(buf_T *buf, linenr_T line)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && line >= wp->w_topline && line < wp->w_botline) {
- redrawWinline(wp, line);
- }
- }
-}
-
-void redraw_buf_range_later(buf_T *buf, linenr_T firstline, linenr_T lastline)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf
- && lastline >= wp->w_topline && firstline < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > firstline) {
- wp->w_redraw_top = firstline;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lastline) {
- wp->w_redraw_bot = lastline;
- }
- redraw_later(wp, VALID);
- }
- }
-}
-
-/*
- * Changed something in the current window, at buffer line "lnum", that
- * requires that line and possibly other lines to be redrawn.
- * Used when entering/leaving Insert mode with the cursor on a folded line.
- * Used to remove the "$" from a change command.
- * Note that when also inserting/deleting lines w_redraw_top and w_redraw_bot
- * may become invalid and the whole window will have to be redrawn.
- */
-void redrawWinline(win_T *wp, linenr_T lnum)
- FUNC_ATTR_NONNULL_ALL
-{
- if (lnum >= wp->w_topline
- && lnum < wp->w_botline) {
- if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) {
- wp->w_redraw_top = lnum;
- }
- if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) {
- wp->w_redraw_bot = lnum;
- }
- redraw_later(wp, VALID);
- }
-}
-
-/// called when the status bars for the buffer 'buf' need to be updated
-void redraw_buf_status_later(buf_T *buf)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf && (wp->w_status_height || (wp == curwin && global_stl_height())
- || wp->w_winbar_height)) {
- wp->w_redr_status = true;
- if (must_redraw < VALID) {
- must_redraw = VALID;
- }
- }
- }
-}
-
-void redraw_win_signcol(win_T *wp)
-{
- // If we can compute a change in the automatic sizing of the sign column
- // under 'signcolumn=auto:X' and signs currently placed in the buffer, better
- // figuring it out here so we can redraw the entire screen for it.
- int scwidth = wp->w_scwidth;
- wp->w_scwidth = win_signcol_count(wp);
- if (wp->w_scwidth != scwidth) {
- changed_line_abv_curs_win(wp);
- }
-}
-
-/// Update all windows that are editing the current buffer.
-void update_curbuf(int type)
-{
- redraw_curbuf_later(type);
- update_screen(type);
-}
-
-/// Redraw the parts of the screen that is marked for redraw.
-///
-/// Most code shouldn't call this directly, rather use redraw_later() and
-/// and redraw_all_later() to mark parts of the screen as needing a redraw.
-///
-/// @param type set to a NOT_VALID to force redraw of entire screen
-int update_screen(int type)
-{
- static bool did_intro = false;
- bool is_stl_global = global_stl_height() > 0;
-
- // Don't do anything if the screen structures are (not yet) valid.
- // A VimResized autocmd can invoke redrawing in the middle of a resize,
- // which would bypass the checks in screen_resize for popupmenu etc.
- if (!default_grid.chars || resizing) {
- return FAIL;
- }
-
- // May have postponed updating diffs.
- if (need_diff_redraw) {
- diff_redraw(true);
- }
-
- if (must_redraw) {
- if (type < must_redraw) { // use maximal type
- type = must_redraw;
- }
-
- // must_redraw is reset here, so that when we run into some weird
- // reason to redraw while busy redrawing (e.g., asynchronous
- // scrolling), or update_topline() in win_update() will cause a
- // scroll, or a decoration provider requires a redraw, the screen
- // will be redrawn later or in win_update().
- must_redraw = 0;
- }
+static char e_conflicts_with_value_of_listchars[] = N_("E834: Conflicts with value of 'listchars'");
+static char e_conflicts_with_value_of_fillchars[] = N_("E835: Conflicts with value of 'fillchars'");
- // Need to update w_lines[].
- if (curwin->w_lines_valid == 0 && type < NOT_VALID) {
- type = NOT_VALID;
- }
-
- /* Postpone the redrawing when it's not needed and when being called
- * recursively. */
- if (!redrawing() || updating_screen) {
- must_redraw = type;
- if (type > INVERTED_ALL) {
- curwin->w_lines_valid = 0; // don't use w_lines[].wl_size now
- }
- return FAIL;
- }
- updating_screen = 1;
-
- display_tick++; // let syntax code know we're in a next round of
- // display updating
-
- // Tricky: vim code can reset msg_scrolled behind our back, so need
- // separate bookkeeping for now.
- if (msg_did_scroll) {
- msg_did_scroll = false;
- msg_scrolled_at_flush = 0;
- }
-
- if (type >= CLEAR || !default_grid.valid) {
- ui_comp_set_screen_valid(false);
- }
-
- // if the screen was scrolled up when displaying a message, scroll it down
- if (msg_scrolled || msg_grid_invalid) {
- clear_cmdline = true;
- int valid = MAX(Rows - msg_scrollsize(), 0);
- if (msg_grid.chars) {
- // non-displayed part of msg_grid is considered invalid.
- for (int i = 0; i < MIN(msg_scrollsize(), msg_grid.rows); i++) {
- grid_clear_line(&msg_grid, msg_grid.line_offset[i],
- msg_grid.cols, false);
- }
- }
- if (msg_use_msgsep()) {
- msg_grid.throttled = false;
- // CLEAR is already handled
- if (type == NOT_VALID && !ui_has(kUIMultigrid) && msg_scrolled) {
- ui_comp_set_screen_valid(false);
- for (int i = valid; i < Rows - p_ch; i++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- Columns, false);
- }
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (W_ENDROW(wp) > valid) {
- wp->w_redr_type = MAX(wp->w_redr_type, NOT_VALID);
- }
- if (!is_stl_global && W_ENDROW(wp) + wp->w_status_height > valid) {
- wp->w_redr_status = true;
- }
- }
- if (is_stl_global && Rows - p_ch - 1 > valid) {
- curwin->w_redr_status = true;
- }
- }
- msg_grid_set_pos(Rows - (int)p_ch, false);
- msg_grid_invalid = false;
- } else if (msg_scrolled > Rows - 5) { // clearing is faster
- type = CLEAR;
- } else if (type != CLEAR) {
- check_for_delay(false);
- grid_ins_lines(&default_grid, 0, msg_scrolled, Rows, 0, Columns);
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- continue;
- }
- if (wp->w_winrow < msg_scrolled) {
- if (W_ENDROW(wp) > msg_scrolled
- && wp->w_redr_type < REDRAW_TOP
- && wp->w_lines_valid > 0
- && wp->w_topline == wp->w_lines[0].wl_lnum) {
- wp->w_upd_rows = msg_scrolled - wp->w_winrow;
- wp->w_redr_type = REDRAW_TOP;
- } else {
- wp->w_redr_type = NOT_VALID;
- if (wp->w_winrow + wp->w_winbar_height <= msg_scrolled) {
- wp->w_redr_status = true;
- }
- }
- }
- }
- if (is_stl_global && Rows - p_ch - 1 <= msg_scrolled) {
- curwin->w_redr_status = true;
- }
- redraw_cmdline = true;
- redraw_tabline = true;
- }
- msg_scrolled = 0;
- msg_scrolled_at_flush = 0;
- need_wait_return = false;
- }
-
- win_ui_flush();
- msg_ext_check_clear();
-
- // reset cmdline_row now (may have been changed temporarily)
- compute_cmdrow();
-
- bool hl_changed = false;
- // Check for changed highlighting
- if (need_highlight_changed) {
- highlight_changed();
- hl_changed = true;
- }
-
- if (type == CLEAR) { // first clear screen
- screenclear(); // will reset clear_cmdline
- cmdline_screen_cleared(); // clear external cmdline state
- type = NOT_VALID;
- // must_redraw may be set indirectly, avoid another redraw later
- must_redraw = 0;
- } else if (!default_grid.valid) {
- grid_invalidate(&default_grid);
- default_grid.valid = true;
- }
-
- // After disabling msgsep the grid might not have been deallocated yet,
- // hence we also need to check msg_grid.chars
- if (type == NOT_VALID && (msg_use_grid() || msg_grid.chars)) {
- grid_fill(&default_grid, Rows - (int)p_ch, Rows, 0, Columns, ' ', ' ', 0);
- }
-
- ui_comp_set_screen_valid(true);
-
- DecorProviders providers;
- decor_providers_start(&providers, type, &provider_err);
-
- // "start" callback could have changed highlights for global elements
- if (win_check_ns_hl(NULL)) {
- redraw_cmdline = true;
- redraw_tabline = true;
- }
-
- if (clear_cmdline) { // going to clear cmdline (done below)
- check_for_delay(false);
- }
-
- /* Force redraw when width of 'number' or 'relativenumber' column
- * changes. */
- if (curwin->w_redr_type < NOT_VALID
- && curwin->w_nrwidth != ((curwin->w_p_nu || curwin->w_p_rnu)
- ? number_width(curwin) : 0)) {
- curwin->w_redr_type = NOT_VALID;
- }
-
- /*
- * Only start redrawing if there is really something to do.
- */
- if (type == INVERTED) {
- update_curswant();
- }
- if (curwin->w_redr_type < type
- && !((type == VALID
- && curwin->w_lines[0].wl_valid
- && curwin->w_topfill == curwin->w_old_topfill
- && curwin->w_botfill == curwin->w_old_botfill
- && curwin->w_topline == curwin->w_lines[0].wl_lnum)
- || (type == INVERTED
- && VIsual_active
- && curwin->w_old_cursor_lnum == curwin->w_cursor.lnum
- && curwin->w_old_visual_mode == VIsual_mode
- && (curwin->w_valid & VALID_VIRTCOL)
- && curwin->w_old_curswant == curwin->w_curswant)
- )) {
- curwin->w_redr_type = type;
- }
-
- // Redraw the tab pages line if needed.
- if (redraw_tabline || type >= NOT_VALID) {
- update_window_hl(curwin, type >= NOT_VALID);
- FOR_ALL_TABS(tp) {
- if (tp != curtab) {
- update_window_hl(tp->tp_curwin, type >= NOT_VALID);
- }
- }
- draw_tabline();
- }
-
- /*
- * Correct stored syntax highlighting info for changes in each displayed
- * buffer. Each buffer must only be done once.
- */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- update_window_hl(wp, type >= NOT_VALID || hl_changed);
-
- buf_T *buf = wp->w_buffer;
- if (buf->b_mod_set) {
- if (buf->b_mod_tick_syn < display_tick
- && syntax_present(wp)) {
- syn_stack_apply_changes(buf);
- buf->b_mod_tick_syn = display_tick;
- }
-
- if (buf->b_mod_tick_decor < display_tick) {
- decor_providers_invoke_buf(buf, &providers, &provider_err);
- buf->b_mod_tick_decor = display_tick;
- }
- }
- }
-
- /*
- * Go from top to bottom through the windows, redrawing the ones that need
- * it.
- */
- bool did_one = false;
- search_hl.rm.regprog = NULL;
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_redr_type == CLEAR && wp->w_floating && wp->w_grid_alloc.chars) {
- grid_invalidate(&wp->w_grid_alloc);
- wp->w_redr_type = NOT_VALID;
- }
-
- // reallocate grid if needed.
- win_grid_alloc(wp);
-
- if (wp->w_redr_border || wp->w_redr_type >= NOT_VALID) {
- win_redr_border(wp);
- }
-
- if (wp->w_redr_type != 0) {
- if (!did_one) {
- did_one = true;
- start_search_hl();
- }
- win_update(wp, &providers);
- }
-
- // redraw status line and window bar after the window to minimize cursor movement
- if (wp->w_redr_status) {
- win_redr_winbar(wp);
- win_redr_status(wp);
- }
- }
-
- end_search_hl();
-
- // May need to redraw the popup menu.
- if (pum_drawn() && must_redraw_pum) {
- pum_redraw();
- }
-
- /* Reset b_mod_set flags. Going through all windows is probably faster
- * than going through all buffers (there could be many buffers). */
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- wp->w_buffer->b_mod_set = false;
- }
-
- updating_screen = 0;
-
- /* Clear or redraw the command line. Done last, because scrolling may
- * mess up the command line. */
- if (clear_cmdline || redraw_cmdline) {
- showmode();
- }
-
- // May put up an introductory message when not editing a file
- if (!did_intro) {
- maybe_intro_message();
- }
- did_intro = true;
-
- decor_providers_invoke_end(&providers, &provider_err);
- kvi_destroy(providers);
-
- // either cmdline is cleared, not drawn or mode is last drawn
- cmdline_was_last_drawn = false;
- return OK;
-}
-
-// Return true if the cursor line in window "wp" may be concealed, according
-// to the 'concealcursor' option.
+/// Return true if the cursor line in window "wp" may be concealed, according
+/// to the 'concealcursor' option.
bool conceal_cursor_line(const win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
@@ -643,21 +69,7 @@ bool conceal_cursor_line(const win_T *wp)
} else {
return false;
}
- return vim_strchr((char *)wp->w_p_cocu, c) != NULL;
-}
-
-// Check if the cursor line needs to be redrawn because of 'concealcursor'.
-//
-// When cursor is moved at the same time, both lines will be redrawn regardless.
-void conceal_check_cursor_line(void)
-{
- bool should_conceal = conceal_cursor_line(curwin);
- if (curwin->w_p_cole > 0 && (conceal_cursor_used != should_conceal)) {
- redrawWinline(curwin, curwin->w_cursor.lnum);
- // Need to recompute cursor column, e.g., when starting Visual mode
- // without concealing.
- curs_columns(curwin, true);
- }
+ return vim_strchr(wp->w_p_cocu, c) != NULL;
}
/// Whether cursorline is drawn in a special way
@@ -669,1081 +81,6 @@ bool win_cursorline_standout(const win_T *wp)
return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
}
-/*
- * Update a single window.
- *
- * This may cause the windows below it also to be redrawn (when clearing the
- * screen or scrolling lines).
- *
- * How the window is redrawn depends on wp->w_redr_type. Each type also
- * implies the one below it.
- * NOT_VALID redraw the whole window
- * SOME_VALID redraw the whole window but do scroll when possible
- * REDRAW_TOP redraw the top w_upd_rows window lines, otherwise like VALID
- * INVERTED redraw the changed part of the Visual area
- * INVERTED_ALL redraw the whole Visual area
- * VALID 1. scroll up/down to adjust for a changed w_topline
- * 2. update lines at the top when scrolled down
- * 3. redraw changed text:
- * - if wp->w_buffer->b_mod_set set, update lines between
- * b_mod_top and b_mod_bot.
- * - if wp->w_redraw_top non-zero, redraw lines between
- * wp->w_redraw_top and wp->w_redr_bot.
- * - continue redrawing when syntax status is invalid.
- * 4. if scrolled up, update lines at the bottom.
- * This results in three areas that may need updating:
- * top: from first row to top_end (when scrolled down)
- * mid: from mid_start to mid_end (update inversion or changed text)
- * bot: from bot_start to last row (when scrolled up)
- */
-static void win_update(win_T *wp, DecorProviders *providers)
-{
- bool called_decor_providers = false;
-win_update_start:
- ;
- buf_T *buf = wp->w_buffer;
- int type;
- int top_end = 0; /* Below last row of the top area that needs
- updating. 0 when no top area updating. */
- int mid_start = 999; /* first row of the mid area that needs
- updating. 999 when no mid area updating. */
- int mid_end = 0; /* Below last row of the mid area that needs
- updating. 0 when no mid area updating. */
- int bot_start = 999; /* first row of the bot area that needs
- updating. 999 when no bot area updating */
- bool scrolled_down = false; // true when scrolled down when w_topline got smaller a bit
- bool top_to_mod = false; // redraw above mod_top
-
- int row; // current window row to display
- linenr_T lnum; // current buffer lnum to display
- int idx; // current index in w_lines[]
- int srow; // starting row of the current line
-
- bool eof = false; // if true, we hit the end of the file
- bool didline = false; // if true, we finished the last line
- int i;
- long j;
- static bool recursive = false; // being called recursively
- const linenr_T old_botline = wp->w_botline;
- // Remember what happened to the previous line.
-#define DID_NONE 1 // didn't update a line
-#define DID_LINE 2 // updated a normal line
-#define DID_FOLD 3 // updated a folded line
- int did_update = DID_NONE;
- linenr_T syntax_last_parsed = 0; // last parsed text line
- linenr_T mod_top = 0;
- linenr_T mod_bot = 0;
- int save_got_int;
-
- type = wp->w_redr_type;
-
- if (type >= NOT_VALID) {
- wp->w_redr_status = true;
- wp->w_lines_valid = 0;
- }
-
- // Window is zero-height: Only need to draw the separator
- if (wp->w_grid.rows == 0) {
- // draw the horizontal separator below this window
- draw_hsep_win(wp);
- draw_sep_connectors_win(wp);
- wp->w_redr_type = 0;
- return;
- }
-
- // Window is zero-width: Only need to draw the separator.
- if (wp->w_grid.cols == 0) {
- // draw the vertical separator right of this window
- draw_vsep_win(wp);
- draw_sep_connectors_win(wp);
- wp->w_redr_type = 0;
- return;
- }
-
- redraw_win_signcol(wp);
-
- init_search_hl(wp, &search_hl);
-
- /* Force redraw when width of 'number' or 'relativenumber' column
- * changes. */
- i = (wp->w_p_nu || wp->w_p_rnu) ? number_width(wp) : 0;
- if (wp->w_nrwidth != i) {
- type = NOT_VALID;
- wp->w_nrwidth = i;
-
- if (buf->terminal) {
- terminal_check_size(buf->terminal);
- }
- } else if (buf->b_mod_set
- && buf->b_mod_xlines != 0
- && wp->w_redraw_top != 0) {
- // When there are both inserted/deleted lines and specific lines to be
- // redrawn, w_redraw_top and w_redraw_bot may be invalid, just redraw
- // everything (only happens when redrawing is off for while).
- type = NOT_VALID;
- } else {
- /*
- * Set mod_top to the first line that needs displaying because of
- * changes. Set mod_bot to the first line after the changes.
- */
- mod_top = wp->w_redraw_top;
- if (wp->w_redraw_bot != 0) {
- mod_bot = wp->w_redraw_bot + 1;
- } else {
- mod_bot = 0;
- }
- if (buf->b_mod_set) {
- if (mod_top == 0 || mod_top > buf->b_mod_top) {
- mod_top = buf->b_mod_top;
- /* Need to redraw lines above the change that may be included
- * in a pattern match. */
- if (syntax_present(wp)) {
- mod_top -= buf->b_s.b_syn_sync_linebreaks;
- if (mod_top < 1) {
- mod_top = 1;
- }
- }
- }
- if (mod_bot == 0 || mod_bot < buf->b_mod_bot) {
- mod_bot = buf->b_mod_bot;
- }
-
- // When 'hlsearch' is on and using a multi-line search pattern, a
- // change in one line may make the Search highlighting in a
- // previous line invalid. Simple solution: redraw all visible
- // lines above the change.
- // Same for a match pattern.
- if (search_hl.rm.regprog != NULL
- && re_multiline(search_hl.rm.regprog)) {
- top_to_mod = true;
- } else {
- const matchitem_T *cur = wp->w_match_head;
- while (cur != NULL) {
- if (cur->match.regprog != NULL
- && re_multiline(cur->match.regprog)) {
- top_to_mod = true;
- break;
- }
- cur = cur->next;
- }
- }
- }
- if (mod_top != 0 && hasAnyFolding(wp)) {
- linenr_T lnumt, lnumb;
-
- /*
- * A change in a line can cause lines above it to become folded or
- * unfolded. Find the top most buffer line that may be affected.
- * If the line was previously folded and displayed, get the first
- * line of that fold. If the line is folded now, get the first
- * folded line. Use the minimum of these two.
- */
-
- /* Find last valid w_lines[] entry above mod_top. Set lnumt to
- * the line below it. If there is no valid entry, use w_topline.
- * Find the first valid w_lines[] entry below mod_bot. Set lnumb
- * to this line. If there is no valid entry, use MAXLNUM. */
- lnumt = wp->w_topline;
- lnumb = MAXLNUM;
- for (i = 0; i < wp->w_lines_valid; ++i) {
- if (wp->w_lines[i].wl_valid) {
- if (wp->w_lines[i].wl_lastlnum < mod_top) {
- lnumt = wp->w_lines[i].wl_lastlnum + 1;
- }
- if (lnumb == MAXLNUM && wp->w_lines[i].wl_lnum >= mod_bot) {
- lnumb = wp->w_lines[i].wl_lnum;
- // When there is a fold column it might need updating
- // in the next line ("J" just above an open fold).
- if (compute_foldcolumn(wp, 0) > 0) {
- lnumb++;
- }
- }
- }
- }
-
- (void)hasFoldingWin(wp, mod_top, &mod_top, NULL, true, NULL);
- if (mod_top > lnumt) {
- mod_top = lnumt;
- }
-
- // Now do the same for the bottom line (one above mod_bot).
- mod_bot--;
- (void)hasFoldingWin(wp, mod_bot, NULL, &mod_bot, true, NULL);
- mod_bot++;
- if (mod_bot < lnumb) {
- mod_bot = lnumb;
- }
- }
-
- /* When a change starts above w_topline and the end is below
- * w_topline, start redrawing at w_topline.
- * If the end of the change is above w_topline: do like no change was
- * made, but redraw the first line to find changes in syntax. */
- if (mod_top != 0 && mod_top < wp->w_topline) {
- if (mod_bot > wp->w_topline) {
- mod_top = wp->w_topline;
- } else if (syntax_present(wp)) {
- top_end = 1;
- }
- }
-
- /* When line numbers are displayed need to redraw all lines below
- * inserted/deleted lines. */
- if (mod_top != 0 && buf->b_mod_xlines != 0 && wp->w_p_nu) {
- mod_bot = MAXLNUM;
- }
- }
- wp->w_redraw_top = 0; // reset for next time
- wp->w_redraw_bot = 0;
-
- /*
- * When only displaying the lines at the top, set top_end. Used when
- * window has scrolled down for msg_scrolled.
- */
- if (type == REDRAW_TOP) {
- j = 0;
- for (i = 0; i < wp->w_lines_valid; ++i) {
- j += wp->w_lines[i].wl_size;
- if (j >= wp->w_upd_rows) {
- top_end = (int)j;
- break;
- }
- }
- if (top_end == 0) {
- // not found (cannot happen?): redraw everything
- type = NOT_VALID;
- } else {
- // top area defined, the rest is VALID
- type = VALID;
- }
- }
-
- /*
- * If there are no changes on the screen that require a complete redraw,
- * handle three cases:
- * 1: we are off the top of the screen by a few lines: scroll down
- * 2: wp->w_topline is below wp->w_lines[0].wl_lnum: may scroll up
- * 3: wp->w_topline is wp->w_lines[0].wl_lnum: find first entry in
- * w_lines[] that needs updating.
- */
- if ((type == VALID || type == SOME_VALID
- || type == INVERTED || type == INVERTED_ALL)
- && !wp->w_botfill && !wp->w_old_botfill) {
- if (mod_top != 0
- && wp->w_topline == mod_top
- && (!wp->w_lines[0].wl_valid
- || wp->w_topline == wp->w_lines[0].wl_lnum)) {
- // w_topline is the first changed line and window is not scrolled,
- // the scrolling from changed lines will be done further down.
- } else if (wp->w_lines[0].wl_valid
- && (wp->w_topline < wp->w_lines[0].wl_lnum
- || (wp->w_topline == wp->w_lines[0].wl_lnum
- && wp->w_topfill > wp->w_old_topfill)
- )) {
- /*
- * New topline is above old topline: May scroll down.
- */
- if (hasAnyFolding(wp)) {
- linenr_T ln;
-
- /* count the number of lines we are off, counting a sequence
- * of folded lines as one */
- j = 0;
- for (ln = wp->w_topline; ln < wp->w_lines[0].wl_lnum; ln++) {
- j++;
- if (j >= wp->w_grid.rows - 2) {
- break;
- }
- (void)hasFoldingWin(wp, ln, NULL, &ln, true, NULL);
- }
- } else {
- j = wp->w_lines[0].wl_lnum - wp->w_topline;
- }
- if (j < wp->w_grid.rows - 2) { // not too far off
- i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1);
- // insert extra lines for previously invisible filler lines
- if (wp->w_lines[0].wl_lnum != wp->w_topline) {
- i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill;
- }
- if (i != 0 && i < wp->w_grid.rows - 2) { // less than a screen off
- // Try to insert the correct number of lines.
- // If not the last window, delete the lines at the bottom.
- // win_ins_lines may fail when the terminal can't do it.
- win_scroll_lines(wp, 0, i);
- if (wp->w_lines_valid != 0) {
- // Need to update rows that are new, stop at the
- // first one that scrolled down.
- top_end = i;
- scrolled_down = true;
-
- // Move the entries that were scrolled, disable
- // the entries for the lines to be redrawn.
- if ((wp->w_lines_valid += (linenr_T)j) > wp->w_grid.rows) {
- wp->w_lines_valid = wp->w_grid.rows;
- }
- for (idx = wp->w_lines_valid; idx - j >= 0; idx--) {
- wp->w_lines[idx] = wp->w_lines[idx - j];
- }
- while (idx >= 0) {
- wp->w_lines[idx--].wl_valid = false;
- }
- }
- } else {
- mid_start = 0; // redraw all lines
- }
- } else {
- mid_start = 0; // redraw all lines
- }
- } else {
- /*
- * New topline is at or below old topline: May scroll up.
- * When topline didn't change, find first entry in w_lines[] that
- * needs updating.
- */
-
- // try to find wp->w_topline in wp->w_lines[].wl_lnum
- j = -1;
- row = 0;
- for (i = 0; i < wp->w_lines_valid; i++) {
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lnum == wp->w_topline) {
- j = i;
- break;
- }
- row += wp->w_lines[i].wl_size;
- }
- if (j == -1) {
- /* if wp->w_topline is not in wp->w_lines[].wl_lnum redraw all
- * lines */
- mid_start = 0;
- } else {
- /*
- * Try to delete the correct number of lines.
- * wp->w_topline is at wp->w_lines[i].wl_lnum.
- */
- /* If the topline didn't change, delete old filler lines,
- * otherwise delete filler lines of the new topline... */
- if (wp->w_lines[0].wl_lnum == wp->w_topline) {
- row += wp->w_old_topfill;
- } else {
- row += win_get_fill(wp, wp->w_topline);
- }
- // ... but don't delete new filler lines.
- row -= wp->w_topfill;
- if (row > 0) {
- win_scroll_lines(wp, 0, -row);
- bot_start = wp->w_grid.rows - row;
- }
- if ((row == 0 || bot_start < 999) && wp->w_lines_valid != 0) {
- /*
- * Skip the lines (below the deleted lines) that are still
- * valid and don't need redrawing. Copy their info
- * upwards, to compensate for the deleted lines. Set
- * bot_start to the first row that needs redrawing.
- */
- bot_start = 0;
- idx = 0;
- for (;;) {
- wp->w_lines[idx] = wp->w_lines[j];
- /* stop at line that didn't fit, unless it is still
- * valid (no lines deleted) */
- if (row > 0 && bot_start + row
- + (int)wp->w_lines[j].wl_size > wp->w_grid.rows) {
- wp->w_lines_valid = idx + 1;
- break;
- }
- bot_start += wp->w_lines[idx++].wl_size;
-
- // stop at the last valid entry in w_lines[].wl_size
- if (++j >= wp->w_lines_valid) {
- wp->w_lines_valid = idx;
- break;
- }
- }
-
- // Correct the first entry for filler lines at the top
- // when it won't get updated below.
- if (win_may_fill(wp) && bot_start > 0) {
- wp->w_lines[0].wl_size = (uint16_t)(plines_win_nofill(wp, wp->w_topline, true)
- + wp->w_topfill);
- }
- }
- }
- }
-
- // When starting redraw in the first line, redraw all lines.
- if (mid_start == 0) {
- mid_end = wp->w_grid.rows;
- }
- } else {
- // Not VALID or INVERTED: redraw all lines.
- mid_start = 0;
- mid_end = wp->w_grid.rows;
- }
-
- if (type == SOME_VALID) {
- // SOME_VALID: redraw all lines.
- mid_start = 0;
- mid_end = wp->w_grid.rows;
- type = NOT_VALID;
- }
-
- // check if we are updating or removing the inverted part
- if ((VIsual_active && buf == curwin->w_buffer)
- || (wp->w_old_cursor_lnum != 0 && type != NOT_VALID)) {
- linenr_T from, to;
-
- if (VIsual_active) {
- if (VIsual_mode != wp->w_old_visual_mode || type == INVERTED_ALL) {
- // If the type of Visual selection changed, redraw the whole
- // selection. Also when the ownership of the X selection is
- // gained or lost.
- if (curwin->w_cursor.lnum < VIsual.lnum) {
- from = curwin->w_cursor.lnum;
- to = VIsual.lnum;
- } else {
- from = VIsual.lnum;
- to = curwin->w_cursor.lnum;
- }
- // redraw more when the cursor moved as well
- if (wp->w_old_cursor_lnum < from) {
- from = wp->w_old_cursor_lnum;
- }
- if (wp->w_old_cursor_lnum > to) {
- to = wp->w_old_cursor_lnum;
- }
- if (wp->w_old_visual_lnum < from) {
- from = wp->w_old_visual_lnum;
- }
- if (wp->w_old_visual_lnum > to) {
- to = wp->w_old_visual_lnum;
- }
- } else {
- /*
- * Find the line numbers that need to be updated: The lines
- * between the old cursor position and the current cursor
- * position. Also check if the Visual position changed.
- */
- if (curwin->w_cursor.lnum < wp->w_old_cursor_lnum) {
- from = curwin->w_cursor.lnum;
- to = wp->w_old_cursor_lnum;
- } else {
- from = wp->w_old_cursor_lnum;
- to = curwin->w_cursor.lnum;
- if (from == 0) { // Visual mode just started
- from = to;
- }
- }
-
- if (VIsual.lnum != wp->w_old_visual_lnum
- || VIsual.col != wp->w_old_visual_col) {
- if (wp->w_old_visual_lnum < from
- && wp->w_old_visual_lnum != 0) {
- from = wp->w_old_visual_lnum;
- }
- if (wp->w_old_visual_lnum > to) {
- to = wp->w_old_visual_lnum;
- }
- if (VIsual.lnum < from) {
- from = VIsual.lnum;
- }
- if (VIsual.lnum > to) {
- to = VIsual.lnum;
- }
- }
- }
-
- /*
- * If in block mode and changed column or curwin->w_curswant:
- * update all lines.
- * First compute the actual start and end column.
- */
- if (VIsual_mode == Ctrl_V) {
- colnr_T fromc, toc;
- unsigned int save_ve_flags = curwin->w_ve_flags;
-
- if (curwin->w_p_lbr) {
- curwin->w_ve_flags = VE_ALL;
- }
-
- getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
- toc++;
- curwin->w_ve_flags = save_ve_flags;
- // Highlight to the end of the line, unless 'virtualedit' has
- // "block".
- if (curwin->w_curswant == MAXCOL) {
- if (get_ve_flags() & VE_BLOCK) {
- pos_T pos;
- int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
-
- // Need to find the longest line.
- toc = 0;
- pos.coladd = 0;
- for (pos.lnum = curwin->w_cursor.lnum;
- cursor_above ? pos.lnum <= VIsual.lnum : pos.lnum >= VIsual.lnum;
- pos.lnum += cursor_above ? 1 : -1) {
- colnr_T t;
-
- pos.col = (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, pos.lnum, false));
- getvvcol(wp, &pos, NULL, NULL, &t);
- if (toc < t) {
- toc = t;
- }
- }
- toc++;
- } else {
- toc = MAXCOL;
- }
- }
-
- if (fromc != wp->w_old_cursor_fcol
- || toc != wp->w_old_cursor_lcol) {
- if (from > VIsual.lnum) {
- from = VIsual.lnum;
- }
- if (to < VIsual.lnum) {
- to = VIsual.lnum;
- }
- }
- wp->w_old_cursor_fcol = fromc;
- wp->w_old_cursor_lcol = toc;
- }
- } else {
- // Use the line numbers of the old Visual area.
- if (wp->w_old_cursor_lnum < wp->w_old_visual_lnum) {
- from = wp->w_old_cursor_lnum;
- to = wp->w_old_visual_lnum;
- } else {
- from = wp->w_old_visual_lnum;
- to = wp->w_old_cursor_lnum;
- }
- }
-
- /*
- * There is no need to update lines above the top of the window.
- */
- if (from < wp->w_topline) {
- from = wp->w_topline;
- }
-
- /*
- * If we know the value of w_botline, use it to restrict the update to
- * the lines that are visible in the window.
- */
- if (wp->w_valid & VALID_BOTLINE) {
- if (from >= wp->w_botline) {
- from = wp->w_botline - 1;
- }
- if (to >= wp->w_botline) {
- to = wp->w_botline - 1;
- }
- }
-
- /*
- * Find the minimal part to be updated.
- * Watch out for scrolling that made entries in w_lines[] invalid.
- * E.g., CTRL-U makes the first half of w_lines[] invalid and sets
- * top_end; need to redraw from top_end to the "to" line.
- * A middle mouse click with a Visual selection may change the text
- * above the Visual area and reset wl_valid, do count these for
- * mid_end (in srow).
- */
- if (mid_start > 0) {
- lnum = wp->w_topline;
- idx = 0;
- srow = 0;
- if (scrolled_down) {
- mid_start = top_end;
- } else {
- mid_start = 0;
- }
- while (lnum < from && idx < wp->w_lines_valid) { // find start
- if (wp->w_lines[idx].wl_valid) {
- mid_start += wp->w_lines[idx].wl_size;
- } else if (!scrolled_down) {
- srow += wp->w_lines[idx].wl_size;
- }
- ++idx;
- if (idx < wp->w_lines_valid && wp->w_lines[idx].wl_valid) {
- lnum = wp->w_lines[idx].wl_lnum;
- } else {
- ++lnum;
- }
- }
- srow += mid_start;
- mid_end = wp->w_grid.rows;
- for (; idx < wp->w_lines_valid; idx++) { // find end
- if (wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum >= to + 1) {
- // Only update until first row of this line
- mid_end = srow;
- break;
- }
- srow += wp->w_lines[idx].wl_size;
- }
- }
- }
-
- if (VIsual_active && buf == curwin->w_buffer) {
- wp->w_old_visual_mode = (char)VIsual_mode;
- wp->w_old_cursor_lnum = curwin->w_cursor.lnum;
- wp->w_old_visual_lnum = VIsual.lnum;
- wp->w_old_visual_col = VIsual.col;
- wp->w_old_curswant = curwin->w_curswant;
- } else {
- wp->w_old_visual_mode = 0;
- wp->w_old_cursor_lnum = 0;
- wp->w_old_visual_lnum = 0;
- wp->w_old_visual_col = 0;
- }
-
- // reset got_int, otherwise regexp won't work
- save_got_int = got_int;
- got_int = 0;
- // Set the time limit to 'redrawtime'.
- proftime_T syntax_tm = profile_setlimit(p_rdt);
- syn_set_timeout(&syntax_tm);
-
- /*
- * Update all the window rows.
- */
- idx = 0; // first entry in w_lines[].wl_size
- row = 0;
- srow = 0;
- lnum = wp->w_topline; // first line shown in window
-
- win_extmark_arr.size = 0;
-
- decor_redraw_reset(buf, &decor_state);
-
- DecorProviders line_providers;
- decor_providers_invoke_win(wp, providers, &line_providers, &provider_err);
- (void)win_signcol_count(wp); // check if provider changed signcol width
- if (must_redraw != 0) {
- must_redraw = 0;
- if (!called_decor_providers) {
- called_decor_providers = true;
- goto win_update_start;
- }
- }
-
- bool cursorline_standout = win_cursorline_standout(wp);
-
- for (;;) {
- /* stop updating when reached the end of the window (check for _past_
- * the end of the window is at the end of the loop) */
- if (row == wp->w_grid.rows) {
- didline = true;
- break;
- }
-
- // stop updating when hit the end of the file
- if (lnum > buf->b_ml.ml_line_count) {
- eof = true;
- break;
- }
-
- /* Remember the starting row of the line that is going to be dealt
- * with. It is used further down when the line doesn't fit. */
- srow = row;
-
- // Update a line when it is in an area that needs updating, when it
- // has changes or w_lines[idx] is invalid.
- // "bot_start" may be halfway a wrapped line after using
- // win_scroll_lines(), check if the current line includes it.
- // When syntax folding is being used, the saved syntax states will
- // already have been updated, we can't see where the syntax state is
- // the same again, just update until the end of the window.
- if (row < top_end
- || (row >= mid_start && row < mid_end)
- || top_to_mod
- || idx >= wp->w_lines_valid
- || (row + wp->w_lines[idx].wl_size > bot_start)
- || (mod_top != 0
- && (lnum == mod_top
- || (lnum >= mod_top
- && (lnum < mod_bot
- || did_update == DID_FOLD
- || (did_update == DID_LINE
- && syntax_present(wp)
- && ((foldmethodIsSyntax(wp)
- && hasAnyFolding(wp))
- || syntax_check_changed(lnum)))
- // match in fixed position might need redraw
- // if lines were inserted or deleted
- || (wp->w_match_head != NULL
- && buf->b_mod_xlines != 0)))))
- || (cursorline_standout && lnum == wp->w_cursor.lnum)
- || lnum == wp->w_last_cursorline) {
- if (lnum == mod_top) {
- top_to_mod = false;
- }
-
- /*
- * When at start of changed lines: May scroll following lines
- * up or down to minimize redrawing.
- * Don't do this when the change continues until the end.
- * Don't scroll when dollar_vcol >= 0, keep the "$".
- * Don't scroll when redrawing the top, scrolled already above.
- */
- if (lnum == mod_top
- && mod_bot != MAXLNUM
- && !(dollar_vcol >= 0 && mod_bot == mod_top + 1)
- && row >= top_end) {
- int old_rows = 0;
- int new_rows = 0;
- int xtra_rows;
- linenr_T l;
-
- /* Count the old number of window rows, using w_lines[], which
- * should still contain the sizes for the lines as they are
- * currently displayed. */
- for (i = idx; i < wp->w_lines_valid; ++i) {
- /* Only valid lines have a meaningful wl_lnum. Invalid
- * lines are part of the changed area. */
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lnum == mod_bot) {
- break;
- }
- old_rows += wp->w_lines[i].wl_size;
- if (wp->w_lines[i].wl_valid
- && wp->w_lines[i].wl_lastlnum + 1 == mod_bot) {
- /* Must have found the last valid entry above mod_bot.
- * Add following invalid entries. */
- ++i;
- while (i < wp->w_lines_valid
- && !wp->w_lines[i].wl_valid) {
- old_rows += wp->w_lines[i++].wl_size;
- }
- break;
- }
- }
-
- if (i >= wp->w_lines_valid) {
- /* We can't find a valid line below the changed lines,
- * need to redraw until the end of the window.
- * Inserting/deleting lines has no use. */
- bot_start = 0;
- } else {
- /* Able to count old number of rows: Count new window
- * rows, and may insert/delete lines */
- j = idx;
- for (l = lnum; l < mod_bot; l++) {
- if (hasFoldingWin(wp, l, NULL, &l, true, NULL)) {
- new_rows++;
- } else if (l == wp->w_topline) {
- new_rows += plines_win_nofill(wp, l, true) + wp->w_topfill;
- } else {
- new_rows += plines_win(wp, l, true);
- }
- j++;
- if (new_rows > wp->w_grid.rows - row - 2) {
- // it's getting too much, must redraw the rest
- new_rows = 9999;
- break;
- }
- }
- xtra_rows = new_rows - old_rows;
- if (xtra_rows < 0) {
- /* May scroll text up. If there is not enough
- * remaining text or scrolling fails, must redraw the
- * rest. If scrolling works, must redraw the text
- * below the scrolled text. */
- if (row - xtra_rows >= wp->w_grid.rows - 2) {
- mod_bot = MAXLNUM;
- } else {
- win_scroll_lines(wp, row, xtra_rows);
- bot_start = wp->w_grid.rows + xtra_rows;
- }
- } else if (xtra_rows > 0) {
- /* May scroll text down. If there is not enough
- * remaining text of scrolling fails, must redraw the
- * rest. */
- if (row + xtra_rows >= wp->w_grid.rows - 2) {
- mod_bot = MAXLNUM;
- } else {
- win_scroll_lines(wp, row + old_rows, xtra_rows);
- if (top_end > row + old_rows) {
- // Scrolled the part at the top that requires
- // updating down.
- top_end += xtra_rows;
- }
- }
- }
-
- /* When not updating the rest, may need to move w_lines[]
- * entries. */
- if (mod_bot != MAXLNUM && i != j) {
- if (j < i) {
- int x = row + new_rows;
-
- // move entries in w_lines[] upwards
- for (;;) {
- // stop at last valid entry in w_lines[]
- if (i >= wp->w_lines_valid) {
- wp->w_lines_valid = (int)j;
- break;
- }
- wp->w_lines[j] = wp->w_lines[i];
- // stop at a line that won't fit
- if (x + (int)wp->w_lines[j].wl_size
- > wp->w_grid.rows) {
- wp->w_lines_valid = (int)j + 1;
- break;
- }
- x += wp->w_lines[j++].wl_size;
- ++i;
- }
- if (bot_start > x) {
- bot_start = x;
- }
- } else { // j > i
- // move entries in w_lines[] downwards
- j -= i;
- wp->w_lines_valid += (linenr_T)j;
- if (wp->w_lines_valid > wp->w_grid.rows) {
- wp->w_lines_valid = wp->w_grid.rows;
- }
- for (i = wp->w_lines_valid; i - j >= idx; i--) {
- wp->w_lines[i] = wp->w_lines[i - j];
- }
-
- /* The w_lines[] entries for inserted lines are
- * now invalid, but wl_size may be used above.
- * Reset to zero. */
- while (i >= idx) {
- wp->w_lines[i].wl_size = 0;
- wp->w_lines[i--].wl_valid = FALSE;
- }
- }
- }
- }
- }
-
- /*
- * When lines are folded, display one line for all of them.
- * Otherwise, display normally (can be several display lines when
- * 'wrap' is on).
- */
- foldinfo_T foldinfo = fold_info(wp, lnum);
-
- if (foldinfo.fi_lines == 0
- && idx < wp->w_lines_valid
- && wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum == lnum
- && lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
- && srow + wp->w_lines[idx].wl_size > wp->w_grid.rows
- && win_get_fill(wp, lnum) == 0) {
- // This line is not going to fit. Don't draw anything here,
- // will draw "@ " lines below.
- row = wp->w_grid.rows + 1;
- } else {
- prepare_search_hl(wp, &search_hl, lnum);
- // Let the syntax stuff know we skipped a few lines.
- if (syntax_last_parsed != 0 && syntax_last_parsed + 1 < lnum
- && syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
- }
-
- // Display one line
- row = win_line(wp, lnum, srow,
- foldinfo.fi_lines ? srow : wp->w_grid.rows,
- mod_top == 0, false, foldinfo, &line_providers);
-
- if (foldinfo.fi_lines == 0) {
- wp->w_lines[idx].wl_folded = false;
- wp->w_lines[idx].wl_lastlnum = lnum;
- did_update = DID_LINE;
- syntax_last_parsed = lnum;
- } else {
- foldinfo.fi_lines--;
- wp->w_lines[idx].wl_folded = true;
- wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
- did_update = DID_FOLD;
- }
- }
-
- wp->w_lines[idx].wl_lnum = lnum;
- wp->w_lines[idx].wl_valid = true;
-
- if (row > wp->w_grid.rows) { // past end of grid
- // we may need the size of that too long line later on
- if (dollar_vcol == -1) {
- wp->w_lines[idx].wl_size = (uint16_t)plines_win(wp, lnum, true);
- }
- idx++;
- break;
- }
- if (dollar_vcol == -1) {
- wp->w_lines[idx].wl_size = (uint16_t)(row - srow);
- }
- idx++;
- lnum += foldinfo.fi_lines + 1;
- } else {
- if (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum) {
- // 'relativenumber' set and cursor moved vertically: The
- // text doesn't need to be drawn, but the number column does.
- foldinfo_T info = fold_info(wp, lnum);
- (void)win_line(wp, lnum, srow, wp->w_grid.rows, true, true,
- info, &line_providers);
- }
-
- // This line does not need to be drawn, advance to the next one.
- row += wp->w_lines[idx++].wl_size;
- if (row > wp->w_grid.rows) { // past end of screen
- break;
- }
- lnum = wp->w_lines[idx - 1].wl_lastlnum + 1;
- did_update = DID_NONE;
- }
-
- if (lnum > buf->b_ml.ml_line_count) {
- eof = true;
- break;
- }
- }
- /*
- * End of loop over all window lines.
- */
-
- // Now that the window has been redrawn with the old and new cursor line,
- // update w_last_cursorline.
- wp->w_last_cursorline = cursorline_standout ? wp->w_cursor.lnum : 0;
-
- wp->w_last_cursor_lnum_rnu = wp->w_p_rnu ? wp->w_cursor.lnum : 0;
-
- if (idx > wp->w_lines_valid) {
- wp->w_lines_valid = idx;
- }
-
- /*
- * Let the syntax stuff know we stop parsing here.
- */
- if (syntax_last_parsed != 0 && syntax_present(wp)) {
- syntax_end_parsing(syntax_last_parsed + 1);
- }
-
- /*
- * If we didn't hit the end of the file, and we didn't finish the last
- * line we were working on, then the line didn't fit.
- */
- wp->w_empty_rows = 0;
- wp->w_filler_rows = 0;
- if (!eof && !didline) {
- int at_attr = hl_combine_attr(wp->w_hl_attr_normal,
- win_hl_attr(wp, HLF_AT));
- if (lnum == wp->w_topline) {
- /*
- * Single line that does not fit!
- * Don't overwrite it, it can be edited.
- */
- wp->w_botline = lnum + 1;
- } else if (win_get_fill(wp, lnum) >= wp->w_grid.rows - srow) {
- // Window ends in filler lines.
- wp->w_botline = lnum;
- wp->w_filler_rows = wp->w_grid.rows - srow;
- } else if (dy_flags & DY_TRUNCATE) { // 'display' has "truncate"
- int scr_row = wp->w_grid.rows - 1;
-
- // Last line isn't finished: Display "@@@" in the last screen line.
- grid_puts_len(&wp->w_grid, (char_u *)"@@", MIN(wp->w_grid.cols, 2), scr_row, 0, at_attr);
-
- grid_fill(&wp->w_grid, scr_row, scr_row + 1, 2, wp->w_grid.cols,
- '@', ' ', at_attr);
- set_empty_rows(wp, srow);
- wp->w_botline = lnum;
- } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
- int start_col = wp->w_grid.cols - 3;
-
- // Last line isn't finished: Display "@@@" at the end.
- grid_fill(&wp->w_grid, wp->w_grid.rows - 1, wp->w_grid.rows,
- MAX(start_col, 0), wp->w_grid.cols, '@', '@', at_attr);
- set_empty_rows(wp, srow);
- wp->w_botline = lnum;
- } else {
- win_draw_end(wp, '@', ' ', true, srow, wp->w_grid.rows, HLF_AT);
- wp->w_botline = lnum;
- }
- } else {
- if (eof) { // we hit the end of the file
- wp->w_botline = buf->b_ml.ml_line_count + 1;
- j = win_get_fill(wp, wp->w_botline);
- if (j > 0 && !wp->w_botfill && row < wp->w_grid.rows) {
- // Display filler text below last line. win_line() will check
- // for ml_line_count+1 and only draw filler lines
- foldinfo_T info = FOLDINFO_INIT;
- row = win_line(wp, wp->w_botline, row, wp->w_grid.rows,
- false, false, info, &line_providers);
- }
- } else if (dollar_vcol == -1) {
- wp->w_botline = lnum;
- }
-
- // make sure the rest of the screen is blank
- // write the 'eob' character to rows that aren't part of the file.
- win_draw_end(wp, wp->w_p_fcs_chars.eob, ' ', false, row, wp->w_grid.rows,
- HLF_EOB);
- }
-
- kvi_destroy(line_providers);
-
- if (wp->w_redr_type >= REDRAW_TOP) {
- draw_vsep_win(wp);
- draw_hsep_win(wp);
- draw_sep_connectors_win(wp);
- }
- syn_set_timeout(NULL);
-
- // Reset the type of redrawing required, the window has been updated.
- wp->w_redr_type = 0;
- wp->w_old_topfill = wp->w_topfill;
- wp->w_old_botfill = wp->w_botfill;
-
- // Send win_extmarks if needed
- for (size_t n = 0; n < kv_size(win_extmark_arr); n++) {
- ui_call_win_extmark(wp->w_grid_alloc.handle, wp->handle,
- kv_A(win_extmark_arr, n).ns_id, (Integer)kv_A(win_extmark_arr, n).mark_id,
- kv_A(win_extmark_arr, n).win_row, kv_A(win_extmark_arr, n).win_col);
- }
-
- if (dollar_vcol == -1) {
- /*
- * There is a trick with w_botline. If we invalidate it on each
- * change that might modify it, this will cause a lot of expensive
- * calls to plines_win() in update_topline() each time. Therefore the
- * value of w_botline is often approximated, and this value is used to
- * compute the value of w_topline. If the value of w_botline was
- * wrong, check that the value of w_topline is correct (cursor is on
- * the visible part of the text). If it's not, we need to redraw
- * again. Mostly this just means scrolling up a few lines, so it
- * doesn't look too bad. Only do this for the current window (where
- * changes are relevant).
- */
- wp->w_valid |= VALID_BOTLINE;
- wp->w_viewport_invalid = true;
- if (wp == curwin && wp->w_botline != old_botline && !recursive) {
- recursive = true;
- curwin->w_valid &= ~VALID_TOPLINE;
- update_topline(curwin); // may invalidate w_botline again
- if (must_redraw != 0) {
- // Don't update for changes in buffer again.
- i = curbuf->b_mod_set;
- curbuf->b_mod_set = false;
- win_update(curwin, providers);
- must_redraw = 0;
- curbuf->b_mod_set = i;
- }
- recursive = false;
- }
- }
-
- // restore got_int, unless CTRL-C was hit while redrawing
- if (!got_int) {
- got_int = save_got_int;
- }
-}
-
/// Returns width of the signcolumn that should be used for the whole window
///
/// @param wp window we want signcolumn width from
@@ -1783,7 +120,7 @@ static int win_fill_end(win_T *wp, int c1, int c2, int off, int width, int row,
/// Clear lines near the end of the window and mark the unused lines with "c1".
/// Use "c2" as filler character.
/// When "draw_margin" is true, then draw the sign/fold/number columns.
-static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl)
+void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, int endrow, hlf_T hl)
{
assert(hl >= 0 && hl < HLF_COUNT);
int n = 0;
@@ -1808,7 +145,7 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i
}
}
- int attr = hl_combine_attr(wp->w_hl_attr_normal, win_hl_attr(wp, (int)hl));
+ int attr = hl_combine_attr(win_bg_attr(wp), win_hl_attr(wp, (int)hl));
if (wp->w_p_rl) {
grid_fill(&wp->w_grid, row, endrow, wp->w_wincol, W_ENDCOL(wp) - 1 - n,
@@ -1822,20 +159,9 @@ static void win_draw_end(win_T *wp, int c1, int c2, bool draw_margin, int row, i
set_empty_rows(wp, row);
}
-/// Advance **color_cols
-///
-/// @return true when there are columns to draw.
-static bool advance_color_col(int vcol, int **color_cols)
-{
- while (**color_cols >= 0 && vcol > **color_cols) {
- ++*color_cols;
- }
- return **color_cols >= 0;
-}
-
-// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
-// space is available for window "wp", minus "col".
-static int compute_foldcolumn(win_T *wp, int col)
+/// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
+/// space is available for window "wp", minus "col".
+int compute_foldcolumn(win_T *wp, int col)
{
int fdc = win_fdccol_count(wp);
int wmw = wp == curwin && p_wmw == 0 ? 1 : (int)p_wmw;
@@ -1847,63 +173,6 @@ static int compute_foldcolumn(win_T *wp, int col)
return fdc;
}
-/// Put a single char from an UTF-8 buffer into a line buffer.
-///
-/// Handles composing chars and arabic shaping state.
-static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol)
-{
- const char_u *p = (char_u *)s->p;
- int cells = utf_ptr2cells((char *)p);
- int c_len = utfc_ptr2len((char *)p);
- int u8c, u8cc[MAX_MCO];
- if (cells > maxcells) {
- return -1;
- }
- u8c = utfc_ptr2char(p, u8cc);
- if (*p == TAB) {
- cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells);
- for (int c = 0; c < cells; c++) {
- schar_from_ascii(dest[c], ' ');
- }
- goto done;
- } else if (*p < 0x80 && u8cc[0] == 0) {
- schar_from_ascii(dest[0], (char)(*p));
- s->prev_c = u8c;
- } else {
- if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) {
- // Do Arabic shaping.
- int pc, pc1, nc;
- int pcc[MAX_MCO];
- int firstbyte = *p;
-
- // The idea of what is the previous and next
- // character depends on 'rightleft'.
- if (rl) {
- pc = s->prev_c;
- pc1 = s->prev_c1;
- nc = utf_ptr2char((char *)p + c_len);
- s->prev_c1 = u8cc[0];
- } else {
- pc = utfc_ptr2char(p + c_len, pcc);
- nc = s->prev_c;
- pc1 = pcc[0];
- }
- s->prev_c = u8c;
-
- u8c = arabic_shape(u8c, &firstbyte, &u8cc[0], pc, pc1, nc);
- } else {
- s->prev_c = u8c;
- }
- schar_from_cc(dest[0], u8c, u8cc);
- }
- if (cells > 1) {
- dest[1][0] = 0;
- }
-done:
- s->p += c_len;
- return cells;
-}
-
/// Fills the foldcolumn at "p" for window "wp".
/// Only to be called when 'foldcolumn' > 0.
///
@@ -1913,7 +182,7 @@ done:
///
/// Assume monocell characters
/// @return number of chars added to \param p
-static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
+size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_T lnum)
{
int i = 0;
int level;
@@ -1968,2751 +237,22 @@ static size_t fill_foldcolumn(char_u *p, win_T *wp, foldinfo_T foldinfo, linenr_
return MAX(char_counter + (size_t)(fdc - i), (size_t)fdc);
}
-static inline void provider_err_virt_text(linenr_T lnum, char *err)
-{
- Decoration err_decor = DECORATION_INIT;
- int hl_err = syn_check_group(S_LEN("ErrorMsg"));
- kv_push(err_decor.virt_text,
- ((VirtTextChunk){ .text = provider_err,
- .hl_id = hl_err }));
- err_decor.virt_text_width = (int)mb_string2cells(err);
- decor_add_ephemeral(lnum - 1, 0, lnum - 1, 0, &err_decor, 0, 0);
-}
-
-static inline void get_line_number_str(win_T *wp, linenr_T lnum, char_u *buf, size_t buf_len)
-{
- long num;
- char *fmt = "%*ld ";
-
- if (wp->w_p_nu && !wp->w_p_rnu) {
- // 'number' + 'norelativenumber'
- num = (long)lnum;
- } else {
- // 'relativenumber', don't use negative numbers
- num = labs((long)get_cursor_rel_lnum(wp, lnum));
- if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
- // 'number' + 'relativenumber'
- num = lnum;
- fmt = "%-*ld ";
- }
- }
-
- snprintf((char *)buf, buf_len, fmt, number_width(wp), num);
-}
-
-/// Display line "lnum" of window 'wp' on the screen.
-/// wp->w_virtcol needs to be valid.
-///
-/// @param lnum line to display
-/// @param startrow first row relative to window grid
-/// @param endrow last grid row to be redrawn
-/// @param nochange not updating for changed text
-/// @param number_only only update the number column
-/// @param foldinfo fold info for this line
-/// @param[in, out] providers decoration providers active this line
-/// items will be disables if they cause errors
-/// or explicitly return `false`.
-///
-/// @return the number of last row the line occupies.
-static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool nochange,
- bool number_only, foldinfo_T foldinfo, DecorProviders *providers)
-{
- int c = 0; // init for GCC
- long vcol = 0; // virtual column (for tabs)
- long vcol_sbr = -1; // virtual column after showbreak
- long vcol_prev = -1; // "vcol" of previous character
- char_u *line; // current line
- char_u *ptr; // current position in "line"
- int row; // row in the window, excl w_winrow
- ScreenGrid *grid = &wp->w_grid; // grid specific to the window
-
- char_u extra[57]; // sign, line number and 'fdc' must
- // fit in here
- int n_extra = 0; // number of extra chars
- char_u *p_extra = NULL; // string of extra chars, plus NUL
- char_u *p_extra_free = NULL; // p_extra needs to be freed
- int c_extra = NUL; // extra chars, all the same
- int c_final = NUL; // final char, mandatory if set
- int extra_attr = 0; // attributes when n_extra != 0
- static char_u *at_end_str = (char_u *)""; // used for p_extra when displaying
- // curwin->w_p_lcs_chars.eol at
- // end-of-line
- int lcs_eol_one = wp->w_p_lcs_chars.eol; // 'eol' until it's been used
- int lcs_prec_todo = wp->w_p_lcs_chars.prec; // 'prec' until it's been used
- bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
-
- // saved "extra" items for when draw_state becomes WL_LINE (again)
- int saved_n_extra = 0;
- char_u *saved_p_extra = NULL;
- int saved_c_extra = 0;
- int saved_c_final = 0;
- int saved_char_attr = 0;
-
- int n_attr = 0; // chars with special attr
- int saved_attr2 = 0; // char_attr saved for n_attr
- int n_attr3 = 0; // chars with overruling special attr
- int saved_attr3 = 0; // char_attr saved for n_attr3
-
- int n_skip = 0; // nr of chars to skip for 'nowrap'
-
- int fromcol = -10; // start of inverting
- int tocol = MAXCOL; // end of inverting
- int fromcol_prev = -2; // start of inverting after cursor
- bool noinvcur = false; // don't invert the cursor
- bool lnum_in_visual_area = false;
- pos_T pos;
- long v;
-
- int char_attr = 0; // attributes for next character
- bool attr_pri = false; // char_attr has priority
- bool area_highlighting = false; // Visual or incsearch highlighting in this line
- int attr = 0; // attributes for area highlighting
- int area_attr = 0; // attributes desired by highlighting
- int search_attr = 0; // attributes desired by 'hlsearch'
- int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
- int syntax_attr = 0; // attributes desired by syntax
- int has_syntax = FALSE; // this buffer has syntax highl.
- int save_did_emsg;
- int eol_hl_off = 0; // 1 if highlighted char after EOL
- bool draw_color_col = false; // highlight colorcolumn
- int *color_cols = NULL; // pointer to according columns array
- bool has_spell = false; // this buffer has spell checking
-#define SPWORDLEN 150
- char_u nextline[SPWORDLEN * 2]; // text with start of the next line
- int nextlinecol = 0; // column where nextline[] starts
- int nextline_idx = 0; /* index in nextline[] where next line
- starts */
- int spell_attr = 0; // attributes desired by spelling
- int word_end = 0; // last byte with same spell_attr
- static linenr_T checked_lnum = 0; // line number for "checked_col"
- static int checked_col = 0; /* column in "checked_lnum" up to which
- * there are no spell errors */
- static int cap_col = -1; // column to check for Cap word
- static linenr_T capcol_lnum = 0; // line number where "cap_col"
- int cur_checked_col = 0; // checked column for current line
- int extra_check = 0; // has syntax or linebreak
- int multi_attr = 0; // attributes desired by multibyte
- int mb_l = 1; // multi-byte byte length
- int mb_c = 0; // decoded multi-byte character
- bool mb_utf8 = false; // screen char is UTF-8 char
- int u8cc[MAX_MCO]; // composing UTF-8 chars
- int filler_lines; // nr of filler lines to be drawn
- int filler_todo; // nr of filler lines still to do + 1
- hlf_T diff_hlf = (hlf_T)0; // type of diff highlighting
- int change_start = MAXCOL; // first col of changed area
- int change_end = -1; // last col of changed area
- colnr_T trailcol = MAXCOL; // start of trailing spaces
- colnr_T leadcol = 0; // start of leading spaces
- bool in_multispace = false; // in multiple consecutive spaces
- int multispace_pos = 0; // position in lcs-multispace string
- bool need_showbreak = false; // overlong line, skip first x chars
- sign_attrs_T sattrs[SIGN_SHOW_MAX]; // attributes for signs
- int num_signs; // number of signs for line
- int line_attr = 0; // attribute for the whole line
- int line_attr_save;
- int line_attr_lowprio = 0; // low-priority attribute for the line
- int line_attr_lowprio_save;
- int prev_c = 0; // previous Arabic character
- int prev_c1 = 0; // first composing char for prev_c
-
- bool search_attr_from_match = false; // if search_attr is from :match
- bool has_decor = false; // this buffer has decoration
- int win_col_offset = 0; // offset for window columns
-
- char_u buf_fold[FOLD_TEXT_LEN]; // Hold value returned by get_foldtext
-
- bool area_active = false;
-
- int cul_attr = 0; // set when 'cursorline' active
- // 'cursorlineopt' has "screenline" and cursor is in this line
- bool cul_screenline = false;
- // margin columns for the screen line, needed for when 'cursorlineopt'
- // contains "screenline"
- int left_curline_col = 0;
- int right_curline_col = 0;
-
- // draw_state: items that are drawn in sequence:
-#define WL_START 0 // nothing done yet
-#define WL_CMDLINE (WL_START + 1) // cmdline window column
-#define WL_FOLD (WL_CMDLINE + 1) // 'foldcolumn'
-#define WL_SIGN (WL_FOLD + 1) // column for signs
-#define WL_NR (WL_SIGN + 1) // line number
-#define WL_BRI (WL_NR + 1) // 'breakindent'
-#define WL_SBR (WL_BRI + 1) // 'showbreak' or 'diff'
-#define WL_LINE (WL_SBR + 1) // text in the line
- int draw_state = WL_START; // what to draw next
-
- int syntax_flags = 0;
- int syntax_seqnr = 0;
- int prev_syntax_id = 0;
- int conceal_attr = win_hl_attr(wp, HLF_CONCEAL);
- bool is_concealing = false;
- int boguscols = 0; ///< nonexistent columns added to
- ///< force wrapping
- int vcol_off = 0; ///< offset for concealed characters
- int did_wcol = false;
- int match_conc = 0; ///< cchar for match functions
- int old_boguscols = 0;
-#define VCOL_HLC (vcol - vcol_off)
-#define FIX_FOR_BOGUSCOLS \
- { \
- n_extra += vcol_off; \
- vcol -= vcol_off; \
- vcol_off = 0; \
- col -= boguscols; \
- old_boguscols = boguscols; \
- boguscols = 0; \
- }
-
- if (startrow > endrow) { // past the end already!
- return startrow;
- }
-
- row = startrow;
-
- buf_T *buf = wp->w_buffer;
- bool end_fill = (lnum == buf->b_ml.ml_line_count + 1);
-
- if (!number_only) {
- // To speed up the loop below, set extra_check when there is linebreak,
- // trailing white space and/or syntax processing to be done.
- extra_check = wp->w_p_lbr;
- if (syntax_present(wp) && !wp->w_s->b_syn_error && !wp->w_s->b_syn_slow
- && !has_fold && !end_fill) {
- // Prepare for syntax highlighting in this line. When there is an
- // error, stop syntax highlighting.
- save_did_emsg = did_emsg;
- did_emsg = false;
- syntax_start(wp, lnum);
- if (did_emsg) {
- wp->w_s->b_syn_error = true;
- } else {
- did_emsg = save_did_emsg;
- if (!wp->w_s->b_syn_slow) {
- has_syntax = true;
- extra_check = true;
- }
- }
- }
-
- has_decor = decor_redraw_line(buf, lnum - 1, &decor_state);
-
- providers_invoke_line(wp, providers, lnum - 1, &has_decor, &provider_err);
-
- if (provider_err) {
- provider_err_virt_text(lnum, provider_err);
- has_decor = true;
- provider_err = NULL;
- }
-
- if (has_decor) {
- extra_check = true;
- }
-
- // Check for columns to display for 'colorcolumn'.
- color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
- if (color_cols != NULL) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- if (wp->w_p_spell
- && !has_fold
- && !end_fill
- && *wp->w_s->b_p_spl != NUL
- && !GA_EMPTY(&wp->w_s->b_langp)
- && *(char **)(wp->w_s->b_langp.ga_data) != NULL) {
- // Prepare for spell checking.
- has_spell = true;
- extra_check = true;
-
- // Get the start of the next line, so that words that wrap to the next
- // line are found too: "et<line-break>al.".
- // Trick: skip a few chars for C/shell/Vim comments
- nextline[SPWORDLEN] = NUL;
- if (lnum < wp->w_buffer->b_ml.ml_line_count) {
- line = ml_get_buf(wp->w_buffer, lnum + 1, false);
- spell_cat_line(nextline + SPWORDLEN, line, SPWORDLEN);
- }
-
- // When a word wrapped from the previous line the start of the current
- // line is valid.
- if (lnum == checked_lnum) {
- cur_checked_col = checked_col;
- }
- checked_lnum = 0;
-
- // When there was a sentence end in the previous line may require a
- // word starting with capital in this line. In line 1 always check
- // the first word.
- if (lnum != capcol_lnum) {
- cap_col = -1;
- }
- if (lnum == 1) {
- cap_col = 0;
- }
- capcol_lnum = 0;
- }
-
- // handle Visual active in this window
- if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
- pos_T *top, *bot;
-
- if (ltoreq(curwin->w_cursor, VIsual)) {
- // Visual is after curwin->w_cursor
- top = &curwin->w_cursor;
- bot = &VIsual;
- } else {
- // Visual is before curwin->w_cursor
- top = &VIsual;
- bot = &curwin->w_cursor;
- }
- lnum_in_visual_area = (lnum >= top->lnum && lnum <= bot->lnum);
- if (VIsual_mode == Ctrl_V) {
- // block mode
- if (lnum_in_visual_area) {
- fromcol = wp->w_old_cursor_fcol;
- tocol = wp->w_old_cursor_lcol;
- }
- } else {
- // non-block mode
- if (lnum > top->lnum && lnum <= bot->lnum) {
- fromcol = 0;
- } else if (lnum == top->lnum) {
- if (VIsual_mode == 'V') { // linewise
- fromcol = 0;
- } else {
- getvvcol(wp, top, (colnr_T *)&fromcol, NULL, NULL);
- if (gchar_pos(top) == NUL) {
- tocol = fromcol + 1;
- }
- }
- }
- if (VIsual_mode != 'V' && lnum == bot->lnum) {
- if (*p_sel == 'e' && bot->col == 0
- && bot->coladd == 0) {
- fromcol = -10;
- tocol = MAXCOL;
- } else if (bot->col == MAXCOL) {
- tocol = MAXCOL;
- } else {
- pos = *bot;
- if (*p_sel == 'e') {
- getvvcol(wp, &pos, (colnr_T *)&tocol, NULL, NULL);
- } else {
- getvvcol(wp, &pos, NULL, NULL, (colnr_T *)&tocol);
- tocol++;
- }
- }
- }
- }
-
- // Check if the char under the cursor should be inverted (highlighted).
- if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
- && cursor_is_block_during_visual(*p_sel == 'e')) {
- noinvcur = true;
- }
-
- // if inverting in this line set area_highlighting
- if (fromcol >= 0) {
- area_highlighting = true;
- attr = win_hl_attr(wp, HLF_V);
- }
- // handle 'incsearch' and ":s///c" highlighting
- } else if (highlight_match
- && wp == curwin
- && !has_fold
- && lnum >= curwin->w_cursor.lnum
- && lnum <= curwin->w_cursor.lnum + search_match_lines) {
- if (lnum == curwin->w_cursor.lnum) {
- getvcol(curwin, &(curwin->w_cursor),
- (colnr_T *)&fromcol, NULL, NULL);
- } else {
- fromcol = 0;
- }
- if (lnum == curwin->w_cursor.lnum + search_match_lines) {
- pos.lnum = lnum;
- pos.col = search_match_endcol;
- getvcol(curwin, &pos, (colnr_T *)&tocol, NULL, NULL);
- }
- // do at least one character; happens when past end of line
- if (fromcol == tocol && search_match_endcol) {
- tocol = fromcol + 1;
- }
- area_highlighting = true;
- attr = win_hl_attr(wp, HLF_I);
- }
- }
-
- filler_lines = diff_check(wp, lnum);
- if (filler_lines < 0) {
- if (filler_lines == -1) {
- if (diff_find_change(wp, lnum, &change_start, &change_end)) {
- diff_hlf = HLF_ADD; // added line
- } else if (change_start == 0) {
- diff_hlf = HLF_TXD; // changed text
- } else {
- diff_hlf = HLF_CHD; // changed line
- }
- } else {
- diff_hlf = HLF_ADD; // added line
- }
- filler_lines = 0;
- area_highlighting = true;
- }
- VirtLines virt_lines = KV_INITIAL_VALUE;
- int n_virt_lines = decor_virt_lines(wp, lnum, &virt_lines);
- filler_lines += n_virt_lines;
- if (lnum == wp->w_topline) {
- filler_lines = wp->w_topfill;
- n_virt_lines = MIN(n_virt_lines, filler_lines);
- }
- filler_todo = filler_lines;
-
- // Cursor line highlighting for 'cursorline' in the current window.
- if (lnum == wp->w_cursor.lnum) {
- // Do not show the cursor line in the text when Visual mode is active,
- // because it's not clear what is selected then.
- if (wp->w_p_cul && !(wp == curwin && VIsual_active)
- && wp->w_p_culopt_flags != CULOPT_NBR) {
- cul_screenline = (wp->w_p_wrap
- && (wp->w_p_culopt_flags & CULOPT_SCRLINE));
- if (!cul_screenline) {
- cul_attr = win_hl_attr(wp, HLF_CUL);
- HlAttrs ae = syn_attr2entry(cul_attr);
- // We make a compromise here (#7383):
- // * low-priority CursorLine if fg is not set
- // * high-priority ("same as Vim" priority) CursorLine if fg is set
- if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
- line_attr_lowprio = cul_attr;
- } else {
- if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
- && qf_current_entry(wp) == lnum) {
- line_attr = hl_combine_attr(cul_attr, line_attr);
- } else {
- line_attr = cul_attr;
- }
- }
- } else {
- margin_columns_win(wp, &left_curline_col, &right_curline_col);
- }
- area_highlighting = true;
- }
- }
-
- memset(sattrs, 0, sizeof(sattrs));
- num_signs = buf_get_signattrs(wp->w_buffer, lnum, sattrs);
- decor_redraw_signs(buf, lnum - 1, &num_signs, sattrs);
-
- // If this line has a sign with line highlighting set line_attr.
- // TODO(bfredl, vigoux): this should not take priority over decoration!
- sign_attrs_T *sattr = sign_get_attr(SIGN_LINEHL, sattrs, 0, 1);
- if (sattr != NULL) {
- line_attr = sattr->sat_linehl;
- }
-
- // Highlight the current line in the quickfix window.
- if (bt_quickfix(wp->w_buffer) && qf_current_entry(wp) == lnum) {
- line_attr = win_hl_attr(wp, HLF_QFL);
- }
-
- if (line_attr_lowprio || line_attr) {
- area_highlighting = true;
- }
-
- if (cul_screenline) {
- line_attr_save = line_attr;
- line_attr_lowprio_save = line_attr_lowprio;
- }
-
- line = end_fill ? (char_u *)"" : ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line;
-
- if (has_spell && !number_only) {
- // For checking first word with a capital skip white space.
- if (cap_col == 0) {
- cap_col = (int)getwhitecols(line);
- }
-
- /* To be able to spell-check over line boundaries copy the end of the
- * current line into nextline[]. Above the start of the next line was
- * copied to nextline[SPWORDLEN]. */
- if (nextline[SPWORDLEN] == NUL) {
- // No next line or it is empty.
- nextlinecol = MAXCOL;
- nextline_idx = 0;
- } else {
- v = (long)STRLEN(line);
- if (v < SPWORDLEN) {
- /* Short line, use it completely and append the start of the
- * next line. */
- nextlinecol = 0;
- memmove(nextline, line, (size_t)v);
- STRMOVE(nextline + v, nextline + SPWORDLEN);
- nextline_idx = (int)v + 1;
- } else {
- // Long line, use only the last SPWORDLEN bytes.
- nextlinecol = (int)v - SPWORDLEN;
- memmove(nextline, line + nextlinecol, SPWORDLEN); // -V512
- nextline_idx = SPWORDLEN + 1;
- }
- }
- }
-
- if (wp->w_p_list && !has_fold && !end_fill) {
- if (wp->w_p_lcs_chars.space
- || wp->w_p_lcs_chars.multispace != NULL
- || wp->w_p_lcs_chars.leadmultispace != NULL
- || wp->w_p_lcs_chars.trail
- || wp->w_p_lcs_chars.lead
- || wp->w_p_lcs_chars.nbsp) {
- extra_check = true;
- }
- // find start of trailing whitespace
- if (wp->w_p_lcs_chars.trail) {
- trailcol = (colnr_T)STRLEN(ptr);
- while (trailcol > (colnr_T)0 && ascii_iswhite(ptr[trailcol - 1])) {
- trailcol--;
- }
- trailcol += (colnr_T)(ptr - line);
- }
- // find end of leading whitespace
- if (wp->w_p_lcs_chars.lead || wp->w_p_lcs_chars.leadmultispace != NULL) {
- leadcol = 0;
- while (ascii_iswhite(ptr[leadcol])) {
- leadcol++;
- }
- if (ptr[leadcol] == NUL) {
- // in a line full of spaces all of them are treated as trailing
- leadcol = (colnr_T)0;
- } else {
- // keep track of the first column not filled with spaces
- leadcol += (colnr_T)(ptr - line) + 1;
- }
- }
- }
-
- /*
- * 'nowrap' or 'wrap' and a single line that doesn't fit: Advance to the
- * first character to be displayed.
- */
- if (wp->w_p_wrap) {
- v = wp->w_skipcol;
- } else {
- v = wp->w_leftcol;
- }
- if (v > 0 && !number_only) {
- char_u *prev_ptr = ptr;
- while (vcol < v && *ptr != NUL) {
- c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL);
- vcol += c;
- prev_ptr = ptr;
- MB_PTR_ADV(ptr);
- }
-
- // When:
- // - 'cuc' is set, or
- // - 'colorcolumn' is set, or
- // - 'virtualedit' is set, or
- // - the visual mode is active,
- // the end of the line may be before the start of the displayed part.
- if (vcol < v && (wp->w_p_cuc
- || draw_color_col
- || virtual_active()
- || (VIsual_active && wp->w_buffer == curwin->w_buffer))) {
- vcol = v;
- }
-
- /* Handle a character that's not completely on the screen: Put ptr at
- * that character but skip the first few screen characters. */
- if (vcol > v) {
- vcol -= c;
- ptr = prev_ptr;
- // If the character fits on the screen, don't need to skip it.
- // Except for a TAB.
- if (utf_ptr2cells((char *)ptr) >= c || *ptr == TAB) {
- n_skip = (int)(v - vcol);
- }
- }
-
- /*
- * Adjust for when the inverted text is before the screen,
- * and when the start of the inverted text is before the screen.
- */
- if (tocol <= vcol) {
- fromcol = 0;
- } else if (fromcol >= 0 && fromcol < vcol) {
- fromcol = (int)vcol;
- }
-
- // When w_skipcol is non-zero, first line needs 'showbreak'
- if (wp->w_p_wrap) {
- need_showbreak = true;
- }
- // When spell checking a word we need to figure out the start of the
- // word and if it's badly spelled or not.
- if (has_spell) {
- size_t len;
- colnr_T linecol = (colnr_T)(ptr - line);
- hlf_T spell_hlf = HLF_COUNT;
-
- pos = wp->w_cursor;
- wp->w_cursor.lnum = lnum;
- wp->w_cursor.col = linecol;
- len = spell_move_to(wp, FORWARD, true, true, &spell_hlf);
-
- // spell_move_to() may call ml_get() and make "line" invalid
- line = ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line + linecol;
-
- if (len == 0 || (int)wp->w_cursor.col > ptr - line) {
- /* no bad word found at line start, don't check until end of a
- * word */
- spell_hlf = HLF_COUNT;
- word_end = (int)(spell_to_word_end(ptr, wp) - line + 1);
- } else {
- // bad word found, use attributes until end of word
- assert(len <= INT_MAX);
- word_end = wp->w_cursor.col + (int)len + 1;
-
- // Turn index into actual attributes.
- if (spell_hlf != HLF_COUNT) {
- spell_attr = highlight_attr[spell_hlf];
- }
- }
- wp->w_cursor = pos;
-
- // Need to restart syntax highlighting for this line.
- if (has_syntax) {
- syntax_start(wp, lnum);
- }
- }
- }
-
- /*
- * Correct highlighting for cursor that can't be disabled.
- * Avoids having to check this for each character.
- */
- if (fromcol >= 0) {
- if (noinvcur) {
- if ((colnr_T)fromcol == wp->w_virtcol) {
- /* highlighting starts at cursor, let it start just after the
- * cursor */
- fromcol_prev = fromcol;
- fromcol = -1;
- } else if ((colnr_T)fromcol < wp->w_virtcol) {
- // restart highlighting after the cursor
- fromcol_prev = wp->w_virtcol;
- }
- }
- if (fromcol >= tocol) {
- fromcol = -1;
- }
- }
-
- if (!number_only && !has_fold && !end_fill) {
- v = ptr - line;
- area_highlighting |= prepare_search_hl_line(wp, lnum, (colnr_T)v,
- &line, &search_hl, &search_attr,
- &search_attr_from_match);
- ptr = line + v; // "line" may have been updated
- }
-
- int off = 0; // Offset relative start of line
- int col = 0; // Visual column on screen.
- if (wp->w_p_rl) {
- // Rightleft window: process the text in the normal direction, but put
- // it in linebuf_char[off] from right to left. Start at the
- // rightmost column of the window.
- col = grid->cols - 1;
- off += col;
- }
-
- // won't highlight after TERM_ATTRS_MAX columns
- int term_attrs[TERM_ATTRS_MAX] = { 0 };
- if (wp->w_buffer->terminal) {
- terminal_get_line_attributes(wp->w_buffer->terminal, wp, lnum, term_attrs);
- extra_check = true;
- }
-
- int sign_idx = 0;
- // Repeat for the whole displayed line.
- for (;;) {
- int has_match_conc = 0; ///< match wants to conceal
- int decor_conceal = 0;
-
- bool did_decrement_ptr = false;
-
- // Skip this quickly when working on the text.
- if (draw_state != WL_LINE) {
- if (cul_screenline) {
- cul_attr = 0;
- line_attr = line_attr_save;
- line_attr_lowprio = line_attr_lowprio_save;
- }
-
- if (draw_state == WL_CMDLINE - 1 && n_extra == 0) {
- draw_state = WL_CMDLINE;
- if (cmdwin_type != 0 && wp == curwin) {
- // Draw the cmdline character.
- n_extra = 1;
- c_extra = cmdwin_type;
- c_final = NUL;
- char_attr = win_hl_attr(wp, HLF_AT);
- }
- }
-
- if (draw_state == WL_FOLD - 1 && n_extra == 0) {
- int fdc = compute_foldcolumn(wp, 0);
-
- draw_state = WL_FOLD;
- if (fdc > 0) {
- // Draw the 'foldcolumn'. Allocate a buffer, "extra" may
- // already be in use.
- xfree(p_extra_free);
- p_extra_free = xmalloc(MAX_MCO * (size_t)fdc + 1);
- n_extra = (int)fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
- p_extra_free[n_extra] = NUL;
- p_extra = p_extra_free;
- c_extra = NUL;
- c_final = NUL;
- if (use_cursor_line_sign(wp, lnum)) {
- char_attr = win_hl_attr(wp, HLF_CLF);
- } else {
- char_attr = win_hl_attr(wp, HLF_FC);
- }
- }
- }
-
- // sign column, this is hit until sign_idx reaches count
- if (draw_state == WL_SIGN - 1 && n_extra == 0) {
- draw_state = WL_SIGN;
- /* Show the sign column when there are any signs in this
- * buffer or when using Netbeans. */
- if (wp->w_scwidth > 0) {
- get_sign_display_info(false, wp, lnum, sattrs, row,
- startrow, filler_lines, filler_todo,
- &c_extra, &c_final, extra, sizeof(extra),
- &p_extra, &n_extra, &char_attr, sign_idx);
- sign_idx++;
- if (sign_idx < wp->w_scwidth) {
- draw_state = WL_SIGN - 1;
- } else {
- sign_idx = 0;
- }
- }
- }
-
- if (draw_state == WL_NR - 1 && n_extra == 0) {
- draw_state = WL_NR;
- /* Display the absolute or relative line number. After the
- * first fill with blanks when the 'n' flag isn't in 'cpo' */
- if ((wp->w_p_nu || wp->w_p_rnu)
- && (row == startrow + filler_lines
- || vim_strchr(p_cpo, CPO_NUMCOL) == NULL)) {
- // If 'signcolumn' is set to 'number' and a sign is present
- // in 'lnum', then display the sign instead of the line
- // number.
- if (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u'
- && num_signs > 0 && sign_get_attr(SIGN_TEXT, sattrs, 0, 1)) {
- get_sign_display_info(true, wp, lnum, sattrs, row,
- startrow, filler_lines, filler_todo,
- &c_extra, &c_final, extra, sizeof(extra),
- &p_extra, &n_extra, &char_attr, sign_idx);
- } else {
- // Draw the line number (empty space after wrapping).
- if (row == startrow + filler_lines) {
- get_line_number_str(wp, lnum, (char_u *)extra, sizeof(extra));
- if (wp->w_skipcol > 0) {
- for (p_extra = extra; *p_extra == ' '; p_extra++) {
- *p_extra = '-';
- }
- }
- if (wp->w_p_rl) { // reverse line numbers
- // like rl_mirror(), but keep the space at the end
- char_u *p2 = (char_u *)skipwhite((char *)extra);
- p2 = skiptowhite(p2) - 1;
- for (char_u *p1 = (char_u *)skipwhite((char *)extra); p1 < p2; p1++, p2--) {
- const char_u t = *p1;
- *p1 = *p2;
- *p2 = t;
- }
- }
- p_extra = extra;
- c_extra = NUL;
- } else {
- c_extra = ' ';
- }
- c_final = NUL;
- n_extra = number_width(wp) + 1;
- char_attr = get_line_number_attr(wp, lnum, row, startrow, filler_lines, sattrs);
- }
- }
- }
-
- if (draw_state == WL_NR && n_extra == 0) {
- win_col_offset = off;
- }
-
- if (wp->w_briopt_sbr && draw_state == WL_BRI - 1
- && n_extra == 0 && *get_showbreak_value(wp) != NUL) {
- // draw indent after showbreak value
- draw_state = WL_BRI;
- } else if (wp->w_briopt_sbr && draw_state == WL_SBR && n_extra == 0) {
- // after the showbreak, draw the breakindent
- draw_state = WL_BRI - 1;
- }
-
- // draw 'breakindent': indent wrapped text accordingly
- if (draw_state == WL_BRI - 1 && n_extra == 0) {
- draw_state = WL_BRI;
- // if need_showbreak is set, breakindent also applies
- if (wp->w_p_bri && (row != startrow || need_showbreak)
- && filler_lines == 0) {
- char_attr = 0;
-
- if (diff_hlf != (hlf_T)0) {
- char_attr = win_hl_attr(wp, (int)diff_hlf);
- }
- p_extra = NULL;
- c_extra = ' ';
- c_final = NUL;
- n_extra =
- get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, false));
- if (row == startrow) {
- n_extra -= win_col_off2(wp);
- if (n_extra < 0) {
- n_extra = 0;
- }
- }
- if (wp->w_skipcol > 0 && wp->w_p_wrap && wp->w_briopt_sbr) {
- need_showbreak = false;
- }
- // Correct end of highlighted area for 'breakindent',
- // required wen 'linebreak' is also set.
- if (tocol == vcol) {
- tocol += n_extra;
- }
- }
- }
-
- if (draw_state == WL_SBR - 1 && n_extra == 0) {
- draw_state = WL_SBR;
- if (filler_todo > filler_lines - n_virt_lines) {
- // TODO(bfredl): check this doesn't inhibit TUI-style
- // clear-to-end-of-line.
- c_extra = ' ';
- c_final = NUL;
- if (wp->w_p_rl) {
- n_extra = col + 1;
- } else {
- n_extra = grid->cols - col;
- }
- char_attr = 0;
- } else if (filler_todo > 0) {
- // draw "deleted" diff line(s)
- if (char2cells(wp->w_p_fcs_chars.diff) > 1) {
- c_extra = '-';
- c_final = NUL;
- } else {
- c_extra = wp->w_p_fcs_chars.diff;
- c_final = NUL;
- }
- if (wp->w_p_rl) {
- n_extra = col + 1;
- } else {
- n_extra = grid->cols - col;
- }
- char_attr = win_hl_attr(wp, HLF_DED);
- }
- char_u *const sbr = get_showbreak_value(wp);
- if (*sbr != NUL && need_showbreak) {
- // Draw 'showbreak' at the start of each broken line.
- p_extra = sbr;
- c_extra = NUL;
- c_final = NUL;
- n_extra = (int)STRLEN(sbr);
- char_attr = win_hl_attr(wp, HLF_AT);
- if (wp->w_skipcol == 0 || !wp->w_p_wrap) {
- need_showbreak = false;
- }
- vcol_sbr = vcol + mb_charlen(sbr);
- // Correct end of highlighted area for 'showbreak',
- // required when 'linebreak' is also set.
- if (tocol == vcol) {
- tocol += n_extra;
- }
- // Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
- if (cul_attr) {
- char_attr = hl_combine_attr(cul_attr, char_attr);
- }
- }
- }
-
- if (draw_state == WL_LINE - 1 && n_extra == 0) {
- sign_idx = 0;
- draw_state = WL_LINE;
-
- if (has_decor && row == startrow + filler_lines) {
- // hide virt_text on text hidden by 'nowrap'
- decor_redraw_col(wp->w_buffer, (int)vcol, off, true, &decor_state);
- }
-
- if (saved_n_extra) {
- // Continue item from end of wrapped line.
- n_extra = saved_n_extra;
- c_extra = saved_c_extra;
- c_final = saved_c_final;
- p_extra = saved_p_extra;
- char_attr = saved_char_attr;
- } else {
- char_attr = 0;
- }
- }
- }
-
- if (cul_screenline && draw_state == WL_LINE
- && vcol >= left_curline_col
- && vcol < right_curline_col) {
- cul_attr = win_hl_attr(wp, HLF_CUL);
- HlAttrs ae = syn_attr2entry(cul_attr);
- if (ae.rgb_fg_color == -1 && ae.cterm_fg_color == 0) {
- line_attr_lowprio = cul_attr;
- } else {
- if (!(State & MODE_INSERT) && bt_quickfix(wp->w_buffer)
- && qf_current_entry(wp) == lnum) {
- line_attr = hl_combine_attr(cul_attr, line_attr);
- } else {
- line_attr = cul_attr;
- }
- }
- }
-
- // When still displaying '$' of change command, stop at cursor
- if (((dollar_vcol >= 0
- && wp == curwin
- && lnum == wp->w_cursor.lnum
- && vcol >= (long)wp->w_virtcol)
- || (number_only && draw_state > WL_NR))
- && filler_todo <= 0) {
- draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
- grid_put_linebuf(grid, row, 0, col, -grid->cols, wp->w_p_rl, wp,
- wp->w_hl_attr_normal, false);
- // Pretend we have finished updating the window. Except when
- // 'cursorcolumn' is set.
- if (wp->w_p_cuc) {
- row = wp->w_cline_row + wp->w_cline_height;
- } else {
- row = grid->rows;
- }
- break;
- }
-
- if (draw_state == WL_LINE
- && has_fold
- && col == win_col_offset
- && n_extra == 0
- && row == startrow) {
- char_attr = win_hl_attr(wp, HLF_FL);
-
- linenr_T lnume = lnum + foldinfo.fi_lines - 1;
- memset(buf_fold, ' ', FOLD_TEXT_LEN);
- p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
- n_extra = (int)STRLEN(p_extra);
-
- if (p_extra != buf_fold) {
- xfree(p_extra_free);
- p_extra_free = p_extra;
- }
- c_extra = NUL;
- c_final = NUL;
- p_extra[n_extra] = NUL;
- }
-
- if (draw_state == WL_LINE
- && has_fold
- && col < grid->cols
- && n_extra == 0
- && row == startrow) {
- // fill rest of line with 'fold'
- c_extra = wp->w_p_fcs_chars.fold;
- c_final = NUL;
-
- n_extra = wp->w_p_rl ? (col + 1) : (grid->cols - col);
- }
-
- if (draw_state == WL_LINE
- && has_fold
- && col >= grid->cols
- && n_extra != 0
- && row == startrow) {
- // Truncate the folding.
- n_extra = 0;
- }
-
- if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
- // handle Visual or match highlighting in this line
- if (vcol == fromcol
- || (vcol + 1 == fromcol && n_extra == 0
- && utf_ptr2cells((char *)ptr) > 1)
- || ((int)vcol_prev == fromcol_prev
- && vcol_prev < vcol // not at margin
- && vcol < tocol)) {
- area_attr = attr; // start highlighting
- if (area_highlighting) {
- area_active = true;
- }
- } else if (area_attr != 0 && (vcol == tocol
- || (noinvcur
- && (colnr_T)vcol == wp->w_virtcol))) {
- area_attr = 0; // stop highlighting
- area_active = false;
- }
-
- if (!n_extra) {
- // Check for start/end of 'hlsearch' and other matches.
- // After end, check for start/end of next match.
- // When another match, have to check for start again.
- v = (ptr - line);
- search_attr = update_search_hl(wp, lnum, (colnr_T)v, &line, &search_hl, &has_match_conc,
- &match_conc, lcs_eol_one, &search_attr_from_match);
- ptr = line + v; // "line" may have been changed
-
- // Do not allow a conceal over EOL otherwise EOL will be missed
- // and bad things happen.
- if (*ptr == NUL) {
- has_match_conc = 0;
- }
- }
-
- if (diff_hlf != (hlf_T)0) {
- if (diff_hlf == HLF_CHD && ptr - line >= change_start
- && n_extra == 0) {
- diff_hlf = HLF_TXD; // changed text
- }
- if (diff_hlf == HLF_TXD && ptr - line > change_end
- && n_extra == 0) {
- diff_hlf = HLF_CHD; // changed line
- }
- line_attr = win_hl_attr(wp, (int)diff_hlf);
- // Overlay CursorLine onto diff-mode highlight.
- if (cul_attr) {
- line_attr = 0 != line_attr_lowprio // Low-priority CursorLine
- ? hl_combine_attr(hl_combine_attr(cul_attr, line_attr),
- hl_get_underline())
- : hl_combine_attr(line_attr, cul_attr);
- }
- }
-
- // Decide which of the highlight attributes to use.
- attr_pri = true;
-
- if (area_attr != 0) {
- char_attr = hl_combine_attr(line_attr, area_attr);
- if (!highlight_match) {
- // let search highlight show in Visual area if possible
- char_attr = hl_combine_attr(search_attr, char_attr);
- }
- } else if (search_attr != 0) {
- char_attr = hl_combine_attr(line_attr, search_attr);
- }
- // Use line_attr when not in the Visual or 'incsearch' area
- // (area_attr may be 0 when "noinvcur" is set).
- else if (line_attr != 0 && ((fromcol == -10 && tocol == MAXCOL)
- || vcol < fromcol || vcol_prev < fromcol_prev
- || vcol >= tocol)) {
- char_attr = line_attr;
- } else {
- attr_pri = false;
- if (has_syntax) {
- char_attr = syntax_attr;
- } else {
- char_attr = 0;
- }
- }
- }
-
- // Get the next character to put on the screen.
- //
- // The "p_extra" points to the extra stuff that is inserted to
- // represent special characters (non-printable stuff) and other
- // things. When all characters are the same, c_extra is used.
- // If c_final is set, it will compulsorily be used at the end.
- // "p_extra" must end in a NUL to avoid utfc_ptr2len() reads past
- // "p_extra[n_extra]".
- // For the '$' of the 'list' option, n_extra == 1, p_extra == "".
- if (n_extra > 0) {
- if (c_extra != NUL || (n_extra == 1 && c_final != NUL)) {
- c = (n_extra == 1 && c_final != NUL) ? c_final : c_extra;
- mb_c = c; // doesn't handle non-utf-8 multi-byte!
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- } else {
- assert(p_extra != NULL);
- c = *p_extra;
- mb_c = c;
- // If the UTF-8 character is more than one byte:
- // Decode it into "mb_c".
- mb_l = utfc_ptr2len((char *)p_extra);
- mb_utf8 = false;
- if (mb_l > n_extra) {
- mb_l = 1;
- } else if (mb_l > 1) {
- mb_c = utfc_ptr2char(p_extra, u8cc);
- mb_utf8 = true;
- c = 0xc0;
- }
- if (mb_l == 0) { // at the NUL at end-of-line
- mb_l = 1;
- }
-
- // If a double-width char doesn't fit display a '>' in the last column.
- if ((wp->w_p_rl ? (col <= 0) : (col >= grid->cols - 1))
- && utf_char2cells(mb_c) == 2) {
- c = '>';
- mb_c = c;
- mb_l = 1;
- (void)mb_l;
- multi_attr = win_hl_attr(wp, HLF_AT);
-
- if (cul_attr) {
- multi_attr = 0 != line_attr_lowprio
- ? hl_combine_attr(cul_attr, multi_attr)
- : hl_combine_attr(multi_attr, cul_attr);
- }
-
- // put the pointer back to output the double-width
- // character at the start of the next line.
- n_extra++;
- p_extra--;
- } else {
- n_extra -= mb_l - 1;
- p_extra += mb_l - 1;
- }
- p_extra++;
- }
- n_extra--;
- } else if (foldinfo.fi_lines > 0) {
- // skip writing the buffer line itself
- c = NUL;
- XFREE_CLEAR(p_extra_free);
- } else {
- int c0;
-
- XFREE_CLEAR(p_extra_free);
-
- // Get a character from the line itself.
- c0 = c = *ptr;
- mb_c = c;
- // If the UTF-8 character is more than one byte: Decode it
- // into "mb_c".
- mb_l = utfc_ptr2len((char *)ptr);
- mb_utf8 = false;
- if (mb_l > 1) {
- mb_c = utfc_ptr2char(ptr, u8cc);
- // Overlong encoded ASCII or ASCII with composing char
- // is displayed normally, except a NUL.
- if (mb_c < 0x80) {
- c0 = c = mb_c;
- }
- mb_utf8 = true;
-
- // At start of the line we can have a composing char.
- // Draw it as a space with a composing char.
- if (utf_iscomposing(mb_c)) {
- int i;
-
- for (i = MAX_MCO - 1; i > 0; i--) {
- u8cc[i] = u8cc[i - 1];
- }
- u8cc[0] = mb_c;
- mb_c = ' ';
- }
- }
-
- if ((mb_l == 1 && c >= 0x80)
- || (mb_l >= 1 && mb_c == 0)
- || (mb_l > 1 && (!vim_isprintc(mb_c)))) {
- // Illegal UTF-8 byte: display as <xx>.
- // Non-BMP character : display as ? or fullwidth ?.
- transchar_hex((char *)extra, mb_c);
- if (wp->w_p_rl) { // reverse
- rl_mirror(extra);
- }
-
- p_extra = extra;
- c = *p_extra;
- mb_c = mb_ptr2char_adv((const char_u **)&p_extra);
- mb_utf8 = (c >= 0x80);
- n_extra = (int)STRLEN(p_extra);
- c_extra = NUL;
- c_final = NUL;
- if (area_attr == 0 && search_attr == 0) {
- n_attr = n_extra + 1;
- extra_attr = win_hl_attr(wp, HLF_8);
- saved_attr2 = char_attr; // save current attr
- }
- } else if (mb_l == 0) { // at the NUL at end-of-line
- mb_l = 1;
- } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) {
- // Do Arabic shaping.
- int pc, pc1, nc;
- int pcc[MAX_MCO];
-
- // The idea of what is the previous and next
- // character depends on 'rightleft'.
- if (wp->w_p_rl) {
- pc = prev_c;
- pc1 = prev_c1;
- nc = utf_ptr2char((char *)ptr + mb_l);
- prev_c1 = u8cc[0];
- } else {
- pc = utfc_ptr2char(ptr + mb_l, pcc);
- nc = prev_c;
- pc1 = pcc[0];
- }
- prev_c = mb_c;
-
- mb_c = arabic_shape(mb_c, &c, &u8cc[0], pc, pc1, nc);
- } else {
- prev_c = mb_c;
- }
- // If a double-width char doesn't fit display a '>' in the
- // last column; the character is displayed at the start of the
- // next line.
- if ((wp->w_p_rl ? (col <= 0) :
- (col >= grid->cols - 1))
- && utf_char2cells(mb_c) == 2) {
- c = '>';
- mb_c = c;
- mb_utf8 = false;
- mb_l = 1;
- multi_attr = win_hl_attr(wp, HLF_AT);
- // Put pointer back so that the character will be
- // displayed at the start of the next line.
- ptr--;
- did_decrement_ptr = true;
- } else if (*ptr != NUL) {
- ptr += mb_l - 1;
- }
-
- // If a double-width char doesn't fit at the left side display a '<' in
- // the first column. Don't do this for unprintable characters.
- if (n_skip > 0 && mb_l > 1 && n_extra == 0) {
- n_extra = 1;
- c_extra = MB_FILLER_CHAR;
- c_final = NUL;
- c = ' ';
- if (area_attr == 0 && search_attr == 0) {
- n_attr = n_extra + 1;
- extra_attr = win_hl_attr(wp, HLF_AT);
- saved_attr2 = char_attr; // save current attr
- }
- mb_c = c;
- mb_utf8 = false;
- mb_l = 1;
- }
- ptr++;
-
- if (extra_check) {
- bool can_spell = true;
-
- /* Get syntax attribute, unless still at the start of the line
- * (double-wide char that doesn't fit). */
- v = (ptr - line);
- if (has_syntax && v > 0) {
- /* Get the syntax attribute for the character. If there
- * is an error, disable syntax highlighting. */
- save_did_emsg = did_emsg;
- did_emsg = FALSE;
-
- syntax_attr = get_syntax_attr((colnr_T)v - 1,
- has_spell ? &can_spell : NULL, false);
-
- if (did_emsg) {
- wp->w_s->b_syn_error = TRUE;
- has_syntax = FALSE;
- } else {
- did_emsg = save_did_emsg;
- }
-
- if (wp->w_s->b_syn_slow) {
- has_syntax = false;
- }
-
- // Need to get the line again, a multi-line regexp may
- // have made it invalid.
- line = ml_get_buf(wp->w_buffer, lnum, false);
- ptr = line + v;
-
- if (!attr_pri) {
- if (cul_attr) {
- char_attr = 0 != line_attr_lowprio
- ? hl_combine_attr(cul_attr, syntax_attr)
- : hl_combine_attr(syntax_attr, cul_attr);
- } else {
- char_attr = syntax_attr;
- }
- } else {
- char_attr = hl_combine_attr(syntax_attr, char_attr);
- }
- // no concealing past the end of the line, it interferes
- // with line highlighting.
- if (c == NUL) {
- syntax_flags = 0;
- } else {
- syntax_flags = get_syntax_info(&syntax_seqnr);
- }
- } else if (!attr_pri) {
- char_attr = 0;
- }
-
- /* Check spelling (unless at the end of the line).
- * Only do this when there is no syntax highlighting, the
- * @Spell cluster is not used or the current syntax item
- * contains the @Spell cluster. */
- v = (ptr - line);
- if (has_spell && v >= word_end && v > cur_checked_col) {
- spell_attr = 0;
- if (!attr_pri) {
- char_attr = syntax_attr;
- }
- if (c != 0 && (!has_syntax || can_spell)) {
- char_u *prev_ptr;
- char_u *p;
- int len;
- hlf_T spell_hlf = HLF_COUNT;
- prev_ptr = ptr - mb_l;
- v -= mb_l - 1;
-
- /* Use nextline[] if possible, it has the start of the
- * next line concatenated. */
- if ((prev_ptr - line) - nextlinecol >= 0) {
- p = nextline + ((prev_ptr - line) - nextlinecol);
- } else {
- p = prev_ptr;
- }
- cap_col -= (int)(prev_ptr - line);
- size_t tmplen = spell_check(wp, p, &spell_hlf, &cap_col, nochange);
- assert(tmplen <= INT_MAX);
- len = (int)tmplen;
- word_end = (int)v + len;
-
- /* In Insert mode only highlight a word that
- * doesn't touch the cursor. */
- if (spell_hlf != HLF_COUNT
- && (State & MODE_INSERT)
- && wp->w_cursor.lnum == lnum
- && wp->w_cursor.col >=
- (colnr_T)(prev_ptr - line)
- && wp->w_cursor.col < (colnr_T)word_end) {
- spell_hlf = HLF_COUNT;
- spell_redraw_lnum = lnum;
- }
-
- if (spell_hlf == HLF_COUNT && p != prev_ptr
- && (p - nextline) + len > nextline_idx) {
- /* Remember that the good word continues at the
- * start of the next line. */
- checked_lnum = lnum + 1;
- checked_col = (int)((p - nextline) + len - nextline_idx);
- }
-
- // Turn index into actual attributes.
- if (spell_hlf != HLF_COUNT) {
- spell_attr = highlight_attr[spell_hlf];
- }
-
- if (cap_col > 0) {
- if (p != prev_ptr
- && (p - nextline) + cap_col >= nextline_idx) {
- /* Remember that the word in the next line
- * must start with a capital. */
- capcol_lnum = lnum + 1;
- cap_col = (int)((p - nextline) + cap_col
- - nextline_idx);
- } else {
- // Compute the actual column.
- cap_col += (int)(prev_ptr - line);
- }
- }
- }
- }
- if (spell_attr != 0) {
- if (!attr_pri) {
- char_attr = hl_combine_attr(char_attr, spell_attr);
- } else {
- char_attr = hl_combine_attr(spell_attr, char_attr);
- }
- }
-
- if (wp->w_buffer->terminal) {
- char_attr = hl_combine_attr(term_attrs[vcol], char_attr);
- }
-
- if (has_decor && v > 0) {
- bool selected = (area_active || (area_highlighting && noinvcur
- && (colnr_T)vcol == wp->w_virtcol));
- int extmark_attr = decor_redraw_col(wp->w_buffer, (colnr_T)v - 1, off,
- selected, &decor_state);
- if (extmark_attr != 0) {
- if (!attr_pri) {
- char_attr = hl_combine_attr(char_attr, extmark_attr);
- } else {
- char_attr = hl_combine_attr(extmark_attr, char_attr);
- }
- }
-
- decor_conceal = decor_state.conceal;
- if (decor_conceal && decor_state.conceal_char) {
- decor_conceal = 2; // really??
- }
- }
-
- // Found last space before word: check for line break.
- if (wp->w_p_lbr && c0 == c && vim_isbreak(c)
- && !vim_isbreak((int)(*ptr))) {
- int mb_off = utf_head_off(line, ptr - 1);
- char_u *p = ptr - (mb_off + 1);
- // TODO: is passing p for start of the line OK?
- n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
-
- // We have just drawn the showbreak value, no need to add
- // space for it again.
- if (vcol == vcol_sbr) {
- n_extra -= mb_charlen(get_showbreak_value(wp));
- if (n_extra < 0) {
- n_extra = 0;
- }
- }
-
- if (c == TAB && n_extra + col > grid->cols) {
- n_extra = tabstop_padding((colnr_T)vcol, wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array) - 1;
- }
- c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
- c_final = NUL;
- if (ascii_iswhite(c)) {
- if (c == TAB) {
- // See "Tab alignment" below.
- FIX_FOR_BOGUSCOLS;
- }
- if (!wp->w_p_list) {
- c = ' ';
- }
- }
- }
-
- in_multispace = c == ' ' && ((ptr > line + 1 && ptr[-2] == ' ') || *ptr == ' ');
- if (!in_multispace) {
- multispace_pos = 0;
- }
-
- // 'list': Change char 160 to 'nbsp' and space to 'space'.
- // But not when the character is followed by a composing
- // character (use mb_l to check that).
- if (wp->w_p_list
- && ((((c == 160 && mb_l == 1)
- || (mb_utf8
- && ((mb_c == 160 && mb_l == 2)
- || (mb_c == 0x202f && mb_l == 3))))
- && wp->w_p_lcs_chars.nbsp)
- || (c == ' '
- && mb_l == 1
- && (wp->w_p_lcs_chars.space
- || (in_multispace && wp->w_p_lcs_chars.multispace != NULL))
- && ptr - line >= leadcol
- && ptr - line <= trailcol))) {
- if (in_multispace && wp->w_p_lcs_chars.multispace != NULL) {
- c = wp->w_p_lcs_chars.multispace[multispace_pos++];
- if (wp->w_p_lcs_chars.multispace[multispace_pos] == NUL) {
- multispace_pos = 0;
- }
- } else {
- c = (c == ' ') ? wp->w_p_lcs_chars.space : wp->w_p_lcs_chars.nbsp;
- }
- n_attr = 1;
- extra_attr = win_hl_attr(wp, HLF_0);
- saved_attr2 = char_attr; // save current attr
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- }
-
- if (c == ' ' && ((trailcol != MAXCOL && ptr > line + trailcol)
- || (leadcol != 0 && ptr < line + leadcol))) {
- if (leadcol != 0 && in_multispace && ptr < line + leadcol
- && wp->w_p_lcs_chars.leadmultispace != NULL) {
- c = wp->w_p_lcs_chars.leadmultispace[multispace_pos++];
- if (wp->w_p_lcs_chars.leadmultispace[multispace_pos] == NUL) {
- multispace_pos = 0;
- }
- } else if (ptr > line + trailcol && wp->w_p_lcs_chars.trail) {
- c = wp->w_p_lcs_chars.trail;
- } else if (ptr < line + leadcol && wp->w_p_lcs_chars.lead) {
- c = wp->w_p_lcs_chars.lead;
- } else if (leadcol != 0 && wp->w_p_lcs_chars.space) {
- c = wp->w_p_lcs_chars.space;
- }
-
- n_attr = 1;
- extra_attr = win_hl_attr(wp, HLF_0);
- saved_attr2 = char_attr; // save current attr
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- }
- }
-
- /*
- * Handling of non-printable characters.
- */
- if (!vim_isprintc(c)) {
- // when getting a character from the file, we may have to
- // turn it into something else on the way to putting it on the screen.
- if (c == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- int tab_len = 0;
- long vcol_adjusted = vcol; // removed showbreak length
- char_u *const sbr = get_showbreak_value(wp);
-
- // Only adjust the tab_len, when at the first column after the
- // showbreak value was drawn.
- if (*sbr != NUL && vcol == vcol_sbr && wp->w_p_wrap) {
- vcol_adjusted = vcol - mb_charlen(sbr);
- }
- // tab amount depends on current column
- tab_len = tabstop_padding((colnr_T)vcol_adjusted,
- wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array) - 1;
-
- if (!wp->w_p_lbr || !wp->w_p_list) {
- n_extra = tab_len;
- } else {
- char_u *p;
- int i;
- int saved_nextra = n_extra;
-
- if (vcol_off > 0) {
- // there are characters to conceal
- tab_len += vcol_off;
- }
- // boguscols before FIX_FOR_BOGUSCOLS macro from above.
- if (wp->w_p_lcs_chars.tab1 && old_boguscols > 0
- && n_extra > tab_len) {
- tab_len += n_extra - tab_len;
- }
-
- // If n_extra > 0, it gives the number of chars
- // to use for a tab, else we need to calculate the width
- // for a tab.
- int len = (tab_len * utf_char2len(wp->w_p_lcs_chars.tab2));
- if (wp->w_p_lcs_chars.tab3) {
- len += utf_char2len(wp->w_p_lcs_chars.tab3);
- }
- if (n_extra > 0) {
- len += n_extra - tab_len;
- }
- c = wp->w_p_lcs_chars.tab1;
- p = xmalloc((size_t)len + 1);
- memset(p, ' ', (size_t)len);
- p[len] = NUL;
- xfree(p_extra_free);
- p_extra_free = p;
- for (i = 0; i < tab_len; i++) {
- if (*p == NUL) {
- tab_len = i;
- break;
- }
- int lcs = wp->w_p_lcs_chars.tab2;
-
- // if tab3 is given, use it for the last char
- if (wp->w_p_lcs_chars.tab3 && i == tab_len - 1) {
- lcs = wp->w_p_lcs_chars.tab3;
- }
- p += utf_char2bytes(lcs, (char *)p);
- n_extra += utf_char2len(lcs) - (saved_nextra > 0 ? 1 : 0);
- }
- p_extra = p_extra_free;
-
- // n_extra will be increased by FIX_FOX_BOGUSCOLS
- // macro below, so need to adjust for that here
- if (vcol_off > 0) {
- n_extra -= vcol_off;
- }
- }
-
- {
- int vc_saved = vcol_off;
-
- // Tab alignment should be identical regardless of
- // 'conceallevel' value. So tab compensates of all
- // previous concealed characters, and thus resets
- // vcol_off and boguscols accumulated so far in the
- // line. Note that the tab can be longer than
- // 'tabstop' when there are concealed characters.
- FIX_FOR_BOGUSCOLS;
-
- // Make sure, the highlighting for the tab char will be
- // correctly set further below (effectively reverts the
- // FIX_FOR_BOGSUCOLS macro).
- if (n_extra == tab_len + vc_saved && wp->w_p_list
- && wp->w_p_lcs_chars.tab1) {
- tab_len += vc_saved;
- }
- }
-
- mb_utf8 = false; // don't draw as UTF-8
- if (wp->w_p_list) {
- c = (n_extra == 0 && wp->w_p_lcs_chars.tab3)
- ? wp->w_p_lcs_chars.tab3
- : wp->w_p_lcs_chars.tab1;
- if (wp->w_p_lbr) {
- c_extra = NUL; // using p_extra from above
- } else {
- c_extra = wp->w_p_lcs_chars.tab2;
- }
- c_final = wp->w_p_lcs_chars.tab3;
- n_attr = tab_len + 1;
- extra_attr = win_hl_attr(wp, HLF_0);
- saved_attr2 = char_attr; // save current attr
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- }
- } else {
- c_final = NUL;
- c_extra = ' ';
- c = ' ';
- }
- } else if (c == NUL
- && (wp->w_p_list
- || ((fromcol >= 0 || fromcol_prev >= 0)
- && tocol > vcol
- && VIsual_mode != Ctrl_V
- && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))
- && !(noinvcur
- && lnum == wp->w_cursor.lnum
- && (colnr_T)vcol == wp->w_virtcol)))
- && lcs_eol_one > 0) {
- // Display a '$' after the line or highlight an extra
- // character if the line break is included.
- // For a diff line the highlighting continues after the "$".
- if (diff_hlf == (hlf_T)0
- && line_attr == 0
- && line_attr_lowprio == 0) {
- // In virtualedit, visual selections may extend beyond end of line
- if (area_highlighting && virtual_active()
- && tocol != MAXCOL && vcol < tocol) {
- n_extra = 0;
- } else {
- p_extra = at_end_str;
- n_extra = 1;
- c_extra = NUL;
- c_final = NUL;
- }
- }
- if (wp->w_p_list && wp->w_p_lcs_chars.eol > 0) {
- c = wp->w_p_lcs_chars.eol;
- } else {
- c = ' ';
- }
- lcs_eol_one = -1;
- ptr--; // put it back at the NUL
- extra_attr = win_hl_attr(wp, HLF_AT);
- n_attr = 1;
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false; // don't draw as UTF-8
- }
- } else if (c != NUL) {
- p_extra = transchar_buf(wp->w_buffer, c);
- if (n_extra == 0) {
- n_extra = byte2cells(c) - 1;
- }
- if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
- rl_mirror(p_extra); // reverse "<12>"
- }
- c_extra = NUL;
- c_final = NUL;
- if (wp->w_p_lbr) {
- char_u *p;
-
- c = *p_extra;
- p = xmalloc((size_t)n_extra + 1);
- memset(p, ' ', (size_t)n_extra);
- STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1); // NOLINT(runtime/printf)
- p[n_extra] = NUL;
- xfree(p_extra_free);
- p_extra_free = p_extra = p;
- } else {
- n_extra = byte2cells(c) - 1;
- c = *p_extra++;
- }
- n_attr = n_extra + 1;
- extra_attr = win_hl_attr(wp, HLF_8);
- saved_attr2 = char_attr; // save current attr
- mb_utf8 = false; // don't draw as UTF-8
- } else if (VIsual_active
- && (VIsual_mode == Ctrl_V || VIsual_mode == 'v')
- && virtual_active()
- && tocol != MAXCOL
- && vcol < tocol
- && (wp->w_p_rl ? (col >= 0) : (col < grid->cols))) {
- c = ' ';
- ptr--; // put it back at the NUL
- }
- }
-
- if (wp->w_p_cole > 0
- && (wp != curwin || lnum != wp->w_cursor.lnum || conceal_cursor_line(wp))
- && ((syntax_flags & HL_CONCEAL) != 0 || has_match_conc > 0 || decor_conceal > 0)
- && !(lnum_in_visual_area && vim_strchr((char *)wp->w_p_cocu, 'v') == NULL)) {
- char_attr = conceal_attr;
- if (((prev_syntax_id != syntax_seqnr && (syntax_flags & HL_CONCEAL) != 0)
- || has_match_conc > 1 || decor_conceal > 1)
- && (syn_get_sub_char() != NUL
- || (has_match_conc && match_conc)
- || (decor_conceal && decor_state.conceal_char)
- || wp->w_p_cole == 1)
- && wp->w_p_cole != 3) {
- // First time at this concealed item: display one
- // character.
- if (has_match_conc && match_conc) {
- c = match_conc;
- } else if (decor_conceal && decor_state.conceal_char) {
- c = decor_state.conceal_char;
- if (decor_state.conceal_attr) {
- char_attr = decor_state.conceal_attr;
- }
- } else if (syn_get_sub_char() != NUL) {
- c = syn_get_sub_char();
- } else if (wp->w_p_lcs_chars.conceal != NUL) {
- c = wp->w_p_lcs_chars.conceal;
- } else {
- c = ' ';
- }
-
- prev_syntax_id = syntax_seqnr;
-
- if (n_extra > 0) {
- vcol_off += n_extra;
- }
- vcol += n_extra;
- if (wp->w_p_wrap && n_extra > 0) {
- if (wp->w_p_rl) {
- col -= n_extra;
- boguscols -= n_extra;
- } else {
- boguscols += n_extra;
- col += n_extra;
- }
- }
- n_extra = 0;
- n_attr = 0;
- } else if (n_skip == 0) {
- is_concealing = true;
- n_skip = 1;
- }
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false; // don't draw as UTF-8
- }
- } else {
- prev_syntax_id = 0;
- is_concealing = false;
- }
-
- if (n_skip > 0 && did_decrement_ptr) {
- // not showing the '>', put pointer back to avoid getting stuck
- ptr++;
- }
- } // end of printing from buffer content
-
- /* In the cursor line and we may be concealing characters: correct
- * the cursor column when we reach its position. */
- if (!did_wcol && draw_state == WL_LINE
- && wp == curwin && lnum == wp->w_cursor.lnum
- && conceal_cursor_line(wp)
- && (int)wp->w_virtcol <= vcol + n_skip) {
- if (wp->w_p_rl) {
- wp->w_wcol = grid->cols - col + boguscols - 1;
- } else {
- wp->w_wcol = col - boguscols;
- }
- wp->w_wrow = row;
- did_wcol = true;
- wp->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL;
- }
-
- // Don't override visual selection highlighting.
- if (n_attr > 0 && draw_state == WL_LINE && !search_attr_from_match) {
- char_attr = hl_combine_attr(char_attr, extra_attr);
- }
-
- // Handle the case where we are in column 0 but not on the first
- // character of the line and the user wants us to show us a
- // special character (via 'listchars' option "precedes:<char>".
- if (lcs_prec_todo != NUL
- && wp->w_p_list
- && (wp->w_p_wrap ? (wp->w_skipcol > 0 && row == 0) : wp->w_leftcol > 0)
- && filler_todo <= 0
- && draw_state > WL_NR
- && c != NUL) {
- c = wp->w_p_lcs_chars.prec;
- lcs_prec_todo = NUL;
- if (utf_char2cells(mb_c) > 1) {
- // Double-width character being overwritten by the "precedes"
- // character, need to fill up half the character.
- c_extra = MB_FILLER_CHAR;
- c_final = NUL;
- n_extra = 1;
- n_attr = 2;
- extra_attr = win_hl_attr(wp, HLF_AT);
- }
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false; // don't draw as UTF-8
- }
- saved_attr3 = char_attr; // save current attr
- char_attr = win_hl_attr(wp, HLF_AT); // overwriting char_attr
- n_attr3 = 1;
- }
-
- // At end of the text line or just after the last character.
- if (c == NUL && eol_hl_off == 0) {
- // flag to indicate whether prevcol equals startcol of search_hl or
- // one of the matches
- bool prevcol_hl_flag = get_prevcol_hl_flag(wp, &search_hl,
- (long)(ptr - line) - 1);
-
- // Invert at least one char, used for Visual and empty line or
- // highlight match at end of line. If it's beyond the last
- // char on the screen, just overwrite that one (tricky!) Not
- // needed when a '$' was displayed for 'list'.
- if (wp->w_p_lcs_chars.eol == lcs_eol_one
- && ((area_attr != 0 && vcol == fromcol
- && (VIsual_mode != Ctrl_V
- || lnum == VIsual.lnum
- || lnum == curwin->w_cursor.lnum))
- // highlight 'hlsearch' match at end of line
- || prevcol_hl_flag)) {
- int n = 0;
-
- if (wp->w_p_rl) {
- if (col < 0) {
- n = 1;
- }
- } else {
- if (col >= grid->cols) {
- n = -1;
- }
- }
- if (n != 0) {
- // At the window boundary, highlight the last character
- // instead (better than nothing).
- off += n;
- col += n;
- } else {
- // Add a blank character to highlight.
- schar_from_ascii(linebuf_char[off], ' ');
- }
- if (area_attr == 0 && !has_fold) {
- // Use attributes from match with highest priority among
- // 'search_hl' and the match list.
- get_search_match_hl(wp, &search_hl, (long)(ptr - line), &char_attr);
- }
-
- int eol_attr = char_attr;
- if (cul_attr) {
- eol_attr = hl_combine_attr(cul_attr, eol_attr);
- }
- linebuf_attr[off] = eol_attr;
- if (wp->w_p_rl) {
- --col;
- --off;
- } else {
- ++col;
- ++off;
- }
- ++vcol;
- eol_hl_off = 1;
- }
- // Highlight 'cursorcolumn' & 'colorcolumn' past end of the line.
- if (wp->w_p_wrap) {
- v = wp->w_skipcol;
- } else {
- v = wp->w_leftcol;
- }
-
- // check if line ends before left margin
- if (vcol < v + col - win_col_off(wp)) {
- vcol = v + col - win_col_off(wp);
- }
- // Get rid of the boguscols now, we want to draw until the right
- // edge for 'cursorcolumn'.
- col -= boguscols;
- // boguscols = 0; // Disabled because value never read after this
-
- if (draw_color_col) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- bool has_virttext = false;
- // Make sure alignment is the same regardless
- // if listchars=eol:X is used or not.
- int eol_skip = (wp->w_p_lcs_chars.eol == lcs_eol_one && eol_hl_off == 0
- ? 1 : 0);
-
- if (has_decor) {
- has_virttext = decor_redraw_eol(wp->w_buffer, &decor_state, &line_attr,
- col + eol_skip);
- }
-
- if (((wp->w_p_cuc
- && (int)wp->w_virtcol >= VCOL_HLC - eol_hl_off
- && (int)wp->w_virtcol <
- (long)grid->cols * (row - startrow + 1) + v
- && lnum != wp->w_cursor.lnum)
- || draw_color_col || line_attr_lowprio || line_attr
- || diff_hlf != (hlf_T)0 || has_virttext)) {
- int rightmost_vcol = 0;
- int i;
-
- if (wp->w_p_cuc) {
- rightmost_vcol = wp->w_virtcol;
- }
-
- if (draw_color_col) {
- // determine rightmost colorcolumn to possibly draw
- for (i = 0; color_cols[i] >= 0; i++) {
- if (rightmost_vcol < color_cols[i]) {
- rightmost_vcol = color_cols[i];
- }
- }
- }
-
- int cuc_attr = win_hl_attr(wp, HLF_CUC);
- int mc_attr = win_hl_attr(wp, HLF_MC);
-
- int diff_attr = 0;
- if (diff_hlf == HLF_TXD) {
- diff_hlf = HLF_CHD;
- }
- if (diff_hlf != 0) {
- diff_attr = win_hl_attr(wp, (int)diff_hlf);
- }
-
- int base_attr = hl_combine_attr(line_attr_lowprio, diff_attr);
- if (base_attr || line_attr || has_virttext) {
- rightmost_vcol = INT_MAX;
- }
-
- int col_stride = wp->w_p_rl ? -1 : 1;
-
- while (wp->w_p_rl ? col >= 0 : col < grid->cols) {
- schar_from_ascii(linebuf_char[off], ' ');
- col += col_stride;
- if (draw_color_col) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- int col_attr = base_attr;
-
- if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol) {
- col_attr = cuc_attr;
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
- col_attr = mc_attr;
- }
-
- col_attr = hl_combine_attr(col_attr, line_attr);
-
- linebuf_attr[off] = col_attr;
- off += col_stride;
-
- if (VCOL_HLC >= rightmost_vcol) {
- break;
- }
-
- vcol += 1;
- }
- }
-
- // TODO(bfredl): integrate with the common beyond-the-end-loop
- if (wp->w_buffer->terminal) {
- // terminal buffers may need to highlight beyond the end of the
- // logical line
- int n = wp->w_p_rl ? -1 : 1;
- while (col >= 0 && col < grid->cols) {
- schar_from_ascii(linebuf_char[off], ' ');
- linebuf_attr[off] = vcol >= TERM_ATTRS_MAX ? 0 : term_attrs[vcol];
- off += n;
- vcol += n;
- col += n;
- }
- }
-
- draw_virt_text(wp, buf, win_col_offset, &col, grid->cols, row);
- grid_put_linebuf(grid, row, 0, col, grid->cols, wp->w_p_rl, wp,
- wp->w_hl_attr_normal, false);
- row++;
-
- /*
- * Update w_cline_height and w_cline_folded if the cursor line was
- * updated (saves a call to plines_win() later).
- */
- if (wp == curwin && lnum == curwin->w_cursor.lnum) {
- curwin->w_cline_row = startrow;
- curwin->w_cline_height = row - startrow;
- curwin->w_cline_folded = foldinfo.fi_lines > 0;
- curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
- conceal_cursor_used = conceal_cursor_line(curwin);
- }
- break;
- }
-
- // Show "extends" character from 'listchars' if beyond the line end and
- // 'list' is set.
- if (wp->w_p_lcs_chars.ext != NUL
- && draw_state == WL_LINE
- && wp->w_p_list
- && !wp->w_p_wrap
- && filler_todo <= 0
- && (wp->w_p_rl ? col == 0 : col == grid->cols - 1)
- && !has_fold
- && (*ptr != NUL
- || lcs_eol_one > 0
- || (n_extra && (c_extra != NUL || *p_extra != NUL)))) {
- c = wp->w_p_lcs_chars.ext;
- char_attr = win_hl_attr(wp, HLF_AT);
- mb_c = c;
- if (utf_char2len(c) > 1) {
- mb_utf8 = true;
- u8cc[0] = 0;
- c = 0xc0;
- } else {
- mb_utf8 = false;
- }
- }
-
- // advance to the next 'colorcolumn'
- if (draw_color_col) {
- draw_color_col = advance_color_col((int)VCOL_HLC, &color_cols);
- }
-
- // Highlight the cursor column if 'cursorcolumn' is set. But don't
- // highlight the cursor position itself.
- // Also highlight the 'colorcolumn' if it is different than
- // 'cursorcolumn'
- // Also highlight the 'colorcolumn' if 'breakindent' and/or 'showbreak'
- // options are set
- vcol_save_attr = -1;
- if ((draw_state == WL_LINE
- || draw_state == WL_BRI
- || draw_state == WL_SBR)
- && !lnum_in_visual_area
- && search_attr == 0
- && area_attr == 0
- && filler_todo <= 0) {
- if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol
- && lnum != wp->w_cursor.lnum) {
- vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(win_hl_attr(wp, HLF_CUC), char_attr);
- } else if (draw_color_col && VCOL_HLC == *color_cols) {
- vcol_save_attr = char_attr;
- char_attr = hl_combine_attr(win_hl_attr(wp, HLF_MC), char_attr);
- }
- }
-
- // Apply lowest-priority line attr now, so everything can override it.
- if (draw_state == WL_LINE) {
- char_attr = hl_combine_attr(line_attr_lowprio, char_attr);
- }
-
- // Store character to be displayed.
- // Skip characters that are left of the screen for 'nowrap'.
- vcol_prev = vcol;
- if (draw_state < WL_LINE || n_skip <= 0) {
- //
- // Store the character.
- //
- if (wp->w_p_rl && utf_char2cells(mb_c) > 1) {
- // A double-wide character is: put first half in left cell.
- off--;
- col--;
- }
- if (mb_utf8) {
- schar_from_cc(linebuf_char[off], mb_c, u8cc);
- } else {
- schar_from_ascii(linebuf_char[off], (char)c);
- }
- if (multi_attr) {
- linebuf_attr[off] = multi_attr;
- multi_attr = 0;
- } else {
- linebuf_attr[off] = char_attr;
- }
-
- if (utf_char2cells(mb_c) > 1) {
- // Need to fill two screen columns.
- off++;
- col++;
- // UTF-8: Put a 0 in the second screen char.
- linebuf_char[off][0] = 0;
- if (draw_state > WL_NR && filler_todo <= 0) {
- vcol++;
- }
- // When "tocol" is halfway through a character, set it to the end of
- // the character, otherwise highlighting won't stop.
- if (tocol == vcol) {
- tocol++;
- }
- if (wp->w_p_rl) {
- // now it's time to backup one cell
- --off;
- --col;
- }
- }
- if (wp->w_p_rl) {
- --off;
- --col;
- } else {
- ++off;
- ++col;
- }
- } else if (wp->w_p_cole > 0 && is_concealing) {
- --n_skip;
- ++vcol_off;
- if (n_extra > 0) {
- vcol_off += n_extra;
- }
- if (wp->w_p_wrap) {
- /*
- * Special voodoo required if 'wrap' is on.
- *
- * Advance the column indicator to force the line
- * drawing to wrap early. This will make the line
- * take up the same screen space when parts are concealed,
- * so that cursor line computations aren't messed up.
- *
- * To avoid the fictitious advance of 'col' causing
- * trailing junk to be written out of the screen line
- * we are building, 'boguscols' keeps track of the number
- * of bad columns we have advanced.
- */
- if (n_extra > 0) {
- vcol += n_extra;
- if (wp->w_p_rl) {
- col -= n_extra;
- boguscols -= n_extra;
- } else {
- col += n_extra;
- boguscols += n_extra;
- }
- n_extra = 0;
- n_attr = 0;
- }
-
- if (utf_char2cells(mb_c) > 1) {
- // Need to fill two screen columns.
- if (wp->w_p_rl) {
- --boguscols;
- --col;
- } else {
- ++boguscols;
- ++col;
- }
- }
-
- if (wp->w_p_rl) {
- --boguscols;
- --col;
- } else {
- ++boguscols;
- ++col;
- }
- } else {
- if (n_extra > 0) {
- vcol += n_extra;
- n_extra = 0;
- n_attr = 0;
- }
- }
- } else {
- --n_skip;
- }
-
- /* Only advance the "vcol" when after the 'number' or 'relativenumber'
- * column. */
- if (draw_state > WL_NR
- && filler_todo <= 0) {
- ++vcol;
- }
-
- if (vcol_save_attr >= 0) {
- char_attr = vcol_save_attr;
- }
-
- // restore attributes after "predeces" in 'listchars'
- if (draw_state > WL_NR && n_attr3 > 0 && --n_attr3 == 0) {
- char_attr = saved_attr3;
- }
-
- // restore attributes after last 'listchars' or 'number' char
- if (n_attr > 0 && draw_state == WL_LINE && --n_attr == 0) {
- char_attr = saved_attr2;
- }
-
- /*
- * At end of screen line and there is more to come: Display the line
- * so far. If there is no more to display it is caught above.
- */
- if ((wp->w_p_rl ? (col < 0) : (col >= grid->cols))
- && foldinfo.fi_lines == 0
- && (draw_state != WL_LINE
- || *ptr != NUL
- || filler_todo > 0
- || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
- && p_extra != at_end_str)
- || (n_extra != 0
- && (c_extra != NUL || *p_extra != NUL)))) {
- bool wrap = wp->w_p_wrap // Wrapping enabled.
- && filler_todo <= 0 // Not drawing diff filler lines.
- && lcs_eol_one != -1 // Haven't printed the lcs_eol character.
- && row != endrow - 1 // Not the last line being displayed.
- && (grid->cols == Columns // Window spans the width of the screen,
- || ui_has(kUIMultigrid)) // or has dedicated grid.
- && !wp->w_p_rl; // Not right-to-left.
-
- int draw_col = col - boguscols;
- if (filler_todo > 0) {
- int index = filler_todo - (filler_lines - n_virt_lines);
- if (index > 0) {
- int i = (int)kv_size(virt_lines) - index;
- assert(i >= 0);
- int offset = kv_A(virt_lines, i).left_col ? 0 : win_col_offset;
- draw_virt_text_item(buf, offset, kv_A(virt_lines, i).line,
- kHlModeReplace, grid->cols, offset);
- }
- } else {
- draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, row);
- }
-
- grid_put_linebuf(grid, row, 0, draw_col, grid->cols, wp->w_p_rl,
- wp, wp->w_hl_attr_normal, wrap);
- if (wrap) {
- ScreenGrid *current_grid = grid;
- int current_row = row, dummy_col = 0; // dummy_col unused
- grid_adjust(&current_grid, &current_row, &dummy_col);
-
- // Force a redraw of the first column of the next line.
- current_grid->attrs[current_grid->line_offset[current_row + 1]] = -1;
-
- // Remember that the line wraps, used for modeless copy.
- current_grid->line_wraps[current_row] = true;
- }
-
- boguscols = 0;
- row++;
-
- /* When not wrapping and finished diff lines, or when displayed
- * '$' and highlighting until last column, break here. */
- if ((!wp->w_p_wrap
- && filler_todo <= 0
- ) || lcs_eol_one == -1) {
- break;
- }
-
- // When the window is too narrow draw all "@" lines.
- if (draw_state != WL_LINE && filler_todo <= 0) {
- win_draw_end(wp, '@', ' ', true, row, wp->w_grid.rows, HLF_AT);
- row = endrow;
- }
-
- // When line got too long for screen break here.
- if (row == endrow) {
- ++row;
- break;
- }
-
- col = 0;
- off = 0;
- if (wp->w_p_rl) {
- col = grid->cols - 1; // col is not used if breaking!
- off += col;
- }
-
- // reset the drawing state for the start of a wrapped line
- draw_state = WL_START;
- saved_n_extra = n_extra;
- saved_p_extra = p_extra;
- saved_c_extra = c_extra;
- saved_c_final = c_final;
- saved_char_attr = char_attr;
- n_extra = 0;
- lcs_prec_todo = wp->w_p_lcs_chars.prec;
- if (filler_todo <= 0) {
- need_showbreak = true;
- }
- filler_todo--;
- // When the filler lines are actually below the last line of the
- // file, don't draw the line itself, break here.
- if (filler_todo == 0 && (wp->w_botfill || end_fill)) {
- break;
- }
- }
- } // for every character in the line
-
- // After an empty line check first word for capital.
- if (*skipwhite((char *)line) == NUL) {
- capcol_lnum = lnum + 1;
- cap_col = 0;
- }
-
- kv_destroy(virt_lines);
- xfree(p_extra_free);
- return row;
-}
-
-void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int max_col, int win_row)
-{
- DecorState *state = &decor_state;
- int right_pos = max_col;
- bool do_eol = state->eol_col > -1;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
- if (!(item->start_row == state->row
- && (kv_size(item->decor.virt_text) || item->decor.ui_watched))) {
- continue;
- }
- if (item->win_col == -1) {
- if (item->decor.virt_text_pos == kVTRightAlign) {
- right_pos -= item->decor.virt_text_width;
- item->win_col = right_pos;
- } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
- item->win_col = state->eol_col;
- } else if (item->decor.virt_text_pos == kVTWinCol) {
- item->win_col = MAX(item->decor.col + col_off, 0);
- }
- }
- if (item->win_col < 0) {
- continue;
- }
- int col;
- if (item->decor.ui_watched) {
- // send mark position to UI
- col = item->win_col;
- WinExtmark m = { (NS)item->ns_id, item->mark_id, win_row, col };
- kv_push(win_extmark_arr, m);
- }
- if (kv_size(item->decor.virt_text)) {
- col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text,
- item->decor.hl_mode, max_col, item->win_col - col_off);
- }
- item->win_col = -2; // deactivate
- if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) {
- state->eol_col = col + 1;
- }
-
- *end_col = MAX(*end_col, col);
- }
-}
-
-static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col,
- int vcol)
-{
- LineState s = LINE_STATE("");
- int virt_attr = 0;
- size_t virt_pos = 0;
-
- while (col < max_col) {
- if (!*s.p) {
- if (virt_pos >= kv_size(vt)) {
- break;
- }
- virt_attr = 0;
- do {
- s.p = kv_A(vt, virt_pos).text;
- int hl_id = kv_A(vt, virt_pos).hl_id;
- virt_attr = hl_combine_attr(virt_attr,
- hl_id > 0 ? syn_id2attr(hl_id) : 0);
- virt_pos++;
- } while (!s.p && virt_pos < kv_size(vt));
- if (!s.p) {
- break;
- }
- }
- if (!*s.p) {
- continue;
- }
- int attr;
- bool through = false;
- if (hl_mode == kHlModeCombine) {
- attr = hl_combine_attr(linebuf_attr[col], virt_attr);
- } else if (hl_mode == kHlModeBlend) {
- through = (*s.p == ' ');
- attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through);
- } else {
- attr = virt_attr;
- }
- schar_T dummy[2];
- int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col],
- max_col - col, false, vcol);
- // if we failed to emit a char, we still need to advance
- cells = MAX(cells, 1);
-
- for (int c = 0; c < cells; c++) {
- linebuf_attr[col++] = attr;
- }
- vcol += cells;
- }
- return col;
-}
-
-// Return true if CursorLineSign highlight is to be used.
-static bool use_cursor_line_sign(win_T *wp, linenr_T lnum)
-{
- return wp->w_p_cul
- && lnum == wp->w_cursor.lnum
- && (wp->w_p_culopt_flags & CULOPT_NBR);
-}
-
-/// Return true if CursorLineNr highlight is to be used for the number column.
-///
-/// - 'cursorline' must be set
-/// - lnum must be the cursor line
-/// - 'cursorlineopt' has "number"
-/// - don't highlight filler lines (when in diff mode)
-/// - When line is wrapped and 'cursorlineopt' does not have "line", only highlight the line number
-/// itself on the first screenline of the wrapped line, otherwise highlight the number column of
-/// all screenlines of the wrapped line.
-static bool use_cursor_line_nr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines)
-{
- return wp->w_p_cul
- && lnum == wp->w_cursor.lnum
- && (wp->w_p_culopt_flags & CULOPT_NBR)
- && (row == startrow + filler_lines
- || (row > startrow + filler_lines
- && (wp->w_p_culopt_flags & CULOPT_LINE)));
-}
-
-static int get_line_number_attr(win_T *wp, linenr_T lnum, int row, int startrow, int filler_lines,
- sign_attrs_T *sattrs)
-{
- sign_attrs_T *num_sattr = sign_get_attr(SIGN_NUMHL, sattrs, 0, 1);
- if (num_sattr != NULL) {
- // :sign defined with "numhl" highlight.
- return num_sattr->sat_numhl;
- }
-
- if (wp->w_p_rnu) {
- if (lnum < wp->w_cursor.lnum) {
- // Use LineNrAbove
- return win_hl_attr(wp, HLF_LNA);
- }
- if (lnum > wp->w_cursor.lnum) {
- // Use LineNrBelow
- return win_hl_attr(wp, HLF_LNB);
- }
- }
-
- if (use_cursor_line_nr(wp, lnum, row, startrow, filler_lines)) {
- // TODO(vim): Can we use CursorLine instead of CursorLineNr
- // when CursorLineNr isn't set?
- return win_hl_attr(wp, HLF_CLN);
- }
-
- return win_hl_attr(wp, HLF_N);
-}
-
-// Get information needed to display the sign in line 'lnum' in window 'wp'.
-// If 'nrcol' is TRUE, the sign is going to be displayed in the number column.
-// Otherwise the sign is going to be displayed in the sign column.
-//
-// @param count max number of signs
-// @param[out] n_extrap number of characters from pp_extra to display
-// @param sign_idxp Index of the displayed sign
-static void get_sign_display_info(bool nrcol, win_T *wp, linenr_T lnum, sign_attrs_T sattrs[],
- int row, int startrow, int filler_lines, int filler_todo,
- int *c_extrap, int *c_finalp, char_u *extra, size_t extra_size,
- char_u **pp_extra, int *n_extrap, int *char_attrp, int sign_idx)
-{
- // Draw cells with the sign value or blank.
- *c_extrap = ' ';
- *c_finalp = NUL;
- if (nrcol) {
- *n_extrap = number_width(wp) + 1;
- } else {
- if (use_cursor_line_sign(wp, lnum)) {
- *char_attrp = win_hl_attr(wp, HLF_CLS);
- } else {
- *char_attrp = win_hl_attr(wp, HLF_SC);
- }
- *n_extrap = win_signcol_width(wp);
- }
-
- if (row == startrow + filler_lines && filler_todo <= 0) {
- sign_attrs_T *sattr = sign_get_attr(SIGN_TEXT, sattrs, sign_idx, wp->w_scwidth);
- if (sattr != NULL) {
- *pp_extra = sattr->sat_text;
- if (*pp_extra != NULL) {
- *c_extrap = NUL;
- *c_finalp = NUL;
-
- if (nrcol) {
- int n, width = number_width(wp) - 2;
- for (n = 0; n < width; n++) {
- extra[n] = ' ';
- }
- extra[n] = NUL;
- STRCAT(extra, *pp_extra);
- STRCAT(extra, " ");
- *pp_extra = extra;
- *n_extrap = (int)STRLEN(*pp_extra);
- } else {
- int symbol_blen = (int)STRLEN(*pp_extra);
-
- // TODO(oni-link): Is sign text already extended to
- // full cell width?
- assert((size_t)win_signcol_width(wp) >= mb_string2cells((char *)(*pp_extra)));
- // symbol(s) bytes + (filling spaces) (one byte each)
- *n_extrap = symbol_blen + win_signcol_width(wp) -
- (int)mb_string2cells((char *)(*pp_extra));
-
- assert(extra_size > (size_t)symbol_blen);
- memset(extra, ' ', extra_size);
- memcpy(extra, *pp_extra, (size_t)symbol_blen);
-
- *pp_extra = extra;
- (*pp_extra)[*n_extrap] = NUL;
- }
- }
-
- if (use_cursor_line_sign(wp, lnum) && sattr->sat_culhl > 0) {
- *char_attrp = sattr->sat_culhl;
- } else {
- *char_attrp = sattr->sat_texthl;
- }
- }
- }
-}
-
-/*
- * Mirror text "str" for right-left displaying.
- * Only works for single-byte characters (e.g., numbers).
- */
+/// Mirror text "str" for right-left displaying.
+/// Only works for single-byte characters (e.g., numbers).
void rl_mirror(char_u *str)
{
char_u *p1, *p2;
char_u t;
- for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; ++p1, --p2) {
+ for (p1 = str, p2 = str + STRLEN(str) - 1; p1 < p2; p1++, p2--) {
t = *p1;
*p1 = *p2;
*p2 = t;
}
}
-/// Mark all status lines and window bars for redraw; used after first :cd
-void status_redraw_all(void)
-{
- bool is_stl_global = global_stl_height() != 0;
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if ((!is_stl_global && wp->w_status_height) || (is_stl_global && wp == curwin)
- || wp->w_winbar_height) {
- wp->w_redr_status = true;
- redraw_later(wp, VALID);
- }
- }
-}
-
-/// Marks all status lines and window bars of the current buffer for redraw.
-void status_redraw_curbuf(void)
-{
- status_redraw_buf(curbuf);
-}
-
-/// Marks all status lines and window bars of the given buffer for redraw.
-void status_redraw_buf(buf_T *buf)
-{
- bool is_stl_global = global_stl_height() != 0;
-
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_buffer == buf && ((!is_stl_global && wp->w_status_height)
- || (is_stl_global && wp == curwin) || wp->w_winbar_height)) {
- wp->w_redr_status = true;
- redraw_later(wp, VALID);
- }
- }
-}
-
-/*
- * Redraw all status lines that need to be redrawn.
- */
-void redraw_statuslines(void)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_redr_status) {
- win_redr_winbar(wp);
- win_redr_status(wp);
- }
- }
- if (redraw_tabline) {
- draw_tabline();
- }
-}
-
-/*
- * Redraw all status lines at the bottom of frame "frp".
- */
-void win_redraw_last_status(const frame_T *frp)
- FUNC_ATTR_NONNULL_ARG(1)
-{
- if (frp->fr_layout == FR_LEAF) {
- frp->fr_win->w_redr_status = true;
- } else if (frp->fr_layout == FR_ROW) {
- FOR_ALL_FRAMES(frp, frp->fr_child) {
- win_redraw_last_status(frp);
- }
- } else {
- assert(frp->fr_layout == FR_COL);
- frp = frp->fr_child;
- while (frp->fr_next != NULL) {
- frp = frp->fr_next;
- }
- win_redraw_last_status(frp);
- }
-}
-
-/// Draw the vertical separator right of window "wp"
-static void draw_vsep_win(win_T *wp)
-{
- int hl;
- int c;
-
- if (wp->w_vsep_width) {
- // draw the vertical separator right of this window
- c = fillchar_vsep(wp, &hl);
- grid_fill(&default_grid, wp->w_winrow, W_ENDROW(wp),
- W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl);
- }
-}
-
-/// Draw the horizontal separator below window "wp"
-static void draw_hsep_win(win_T *wp)
-{
- int hl;
- int c;
-
- if (wp->w_hsep_height) {
- // draw the horizontal separator below this window
- c = fillchar_hsep(wp, &hl);
- grid_fill(&default_grid, W_ENDROW(wp), W_ENDROW(wp) + 1,
- wp->w_wincol, W_ENDCOL(wp), c, c, hl);
- }
-}
-
-/// Get the separator connector for specified window corner of window "wp"
-static int get_corner_sep_connector(win_T *wp, WindowCorner corner)
-{
- // It's impossible for windows to be connected neither vertically nor horizontally
- // So if they're not vertically connected, assume they're horizontally connected
- if (vsep_connected(wp, corner)) {
- if (hsep_connected(wp, corner)) {
- return wp->w_p_fcs_chars.verthoriz;
- } else if (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT) {
- return wp->w_p_fcs_chars.vertright;
- } else {
- return wp->w_p_fcs_chars.vertleft;
- }
- } else if (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT) {
- return wp->w_p_fcs_chars.horizdown;
- } else {
- return wp->w_p_fcs_chars.horizup;
- }
-}
-
-/// Draw separator connecting characters on the corners of window "wp"
-static void draw_sep_connectors_win(win_T *wp)
-{
- // Don't draw separator connectors unless global statusline is enabled and the window has
- // either a horizontal or vertical separator
- if (global_stl_height() == 0 || !(wp->w_hsep_height == 1 || wp->w_vsep_width == 1)) {
- return;
- }
-
- int hl = win_hl_attr(wp, HLF_C);
-
- // Determine which edges of the screen the window is located on so we can avoid drawing separators
- // on corners contained in those edges
- bool win_at_top;
- bool win_at_bottom = wp->w_hsep_height == 0;
- bool win_at_left;
- bool win_at_right = wp->w_vsep_width == 0;
- frame_T *frp;
-
- for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
- if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL) {
- break;
- }
- }
- win_at_top = frp->fr_parent == NULL;
- for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) {
- if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL) {
- break;
- }
- }
- win_at_left = frp->fr_parent == NULL;
-
- // Draw the appropriate separator connector in every corner where drawing them is necessary
- if (!(win_at_top || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_LEFT),
- wp->w_winrow - 1, wp->w_wincol - 1, hl);
- }
- if (!(win_at_top || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_TOP_RIGHT),
- wp->w_winrow - 1, W_ENDCOL(wp), hl);
- }
- if (!(win_at_bottom || win_at_left)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_LEFT),
- W_ENDROW(wp), wp->w_wincol - 1, hl);
- }
- if (!(win_at_bottom || win_at_right)) {
- grid_putchar(&default_grid, get_corner_sep_connector(wp, WC_BOTTOM_RIGHT),
- W_ENDROW(wp), W_ENDCOL(wp), hl);
- }
-}
-
/// Get the length of an item as it will be shown in the status line.
-static int status_match_len(expand_T *xp, char_u *s)
+static int wildmenu_match_len(expand_T *xp, char_u *s)
{
int len = 0;
@@ -4725,7 +265,7 @@ static int status_match_len(expand_T *xp, char_u *s)
}
while (*s != NUL) {
- s += skip_status_match_char(xp, s);
+ s += skip_wildmenu_char(xp, s);
len += ptr2cells((char *)s);
MB_PTR_ADV(s);
}
@@ -4733,18 +273,20 @@ static int status_match_len(expand_T *xp, char_u *s)
return len;
}
-/*
- * Return the number of characters that should be skipped in a status match.
- * These are backslashes used for escaping. Do show backslashes in help tags.
- */
-static int skip_status_match_char(expand_T *xp, char_u *s)
+/// Return the number of characters that should be skipped in the wildmenu
+/// These are backslashes used for escaping. Do show backslashes in help tags.
+static int skip_wildmenu_char(expand_T *xp, char_u *s)
{
- if ((rem_backslash(s) && xp->xp_context != EXPAND_HELP)
+ if ((rem_backslash((char *)s) && xp->xp_context != EXPAND_HELP)
|| ((xp->xp_context == EXPAND_MENUS
|| xp->xp_context == EXPAND_MENUNAMES)
&& (s[0] == '\t'
|| (s[0] == '\\' && s[1] != NUL)))) {
#ifndef BACKSLASH_IN_FILENAME
+ // TODO(bfredl): Why in the actual fuck are we special casing the
+ // shell variety deep in the redraw logic? Shell special snowflakiness
+ // should already be eliminated multiple layers before reaching the
+ // screen infracstructure.
if (xp->xp_shell && csh_like_shell() && s[1] == '\\' && s[2] == '!') {
return 2;
}
@@ -4761,7 +303,7 @@ static int skip_status_match_char(expand_T *xp, char_u *s)
/// If inversion is possible we use it. Else '=' characters are used.
///
/// @param matches list of matches
-void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int match, int showtail)
+void redraw_wildmenu(expand_T *xp, int num_matches, char **matches, int match, int showtail)
{
#define L_MATCH(m) (showtail ? sm_gettail(matches[m], false) : matches[m])
int row;
@@ -4792,7 +334,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
highlight = false;
}
// count 1 for the ending ">"
- clen = status_match_len(xp, (char_u *)L_MATCH(match)) + 3;
+ clen = wildmenu_match_len(xp, (char_u *)L_MATCH(match)) + 3;
if (match == 0) {
first_match = 0;
} else if (match < first_match) {
@@ -4802,7 +344,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
} else {
// check if match fits on the screen
for (i = first_match; i < match; i++) {
- clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+ clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
}
if (first_match > 0) {
clen += 2;
@@ -4813,7 +355,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
// if showing the last match, we can add some on the left
clen = 2;
for (i = match; i < num_matches; i++) {
- clen += status_match_len(xp, (char_u *)L_MATCH(i)) + 2;
+ clen += wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2;
if ((long)clen >= Columns) {
break;
}
@@ -4825,7 +367,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
}
if (add_left) {
while (first_match > 0) {
- clen += status_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
+ clen += wildmenu_match_len(xp, (char_u *)L_MATCH(first_match - 1)) + 2;
if ((long)clen >= Columns) {
break;
}
@@ -4845,7 +387,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
clen = len;
i = first_match;
- while (clen + status_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
+ while (clen + wildmenu_match_len(xp, (char_u *)L_MATCH(i)) + 2 < Columns) {
if (i == match) {
selstart = buf + len;
selstart_col = clen;
@@ -4861,8 +403,8 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
len += l;
clen += l;
} else {
- for (; *s != NUL; ++s) {
- s += skip_status_match_char(xp, s);
+ for (; *s != NUL; s++) {
+ s += skip_wildmenu_char(xp, s);
clen += ptr2cells((char *)s);
if ((l = utfc_ptr2len((char *)s)) > 1) {
STRNCPY(buf + len, s, l); // NOLINT(runtime/printf)
@@ -4888,7 +430,7 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
if (i != num_matches) {
*(buf + len++) = '>';
- ++clen;
+ clen++;
}
buf[len] = NUL;
@@ -4927,10 +469,10 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
ScreenGrid *grid = (wild_menu_showing == WM_SCROLLED)
? &msg_grid_adj : &default_grid;
- grid_puts(grid, buf, row, 0, attr);
+ grid_puts(grid, (char *)buf, row, 0, attr);
if (selstart != NULL && highlight) {
*selend = NUL;
- grid_puts(grid, selstart, row, selstart_col, HL_ATTR(HLF_WM));
+ grid_puts(grid, (char *)selstart, row, selstart_col, HL_ATTR(HLF_WM));
}
grid_fill(grid, row, row + 1, clen, Columns,
@@ -4941,190 +483,6 @@ void win_redr_status_matches(expand_T *xp, int num_matches, char **matches, int
xfree(buf);
}
-/// Redraw the status line of window `wp`.
-///
-/// If inversion is possible we use it. Else '=' characters are used.
-static void win_redr_status(win_T *wp)
-{
- int row;
- int col;
- char_u *p;
- int len;
- int fillchar;
- int attr;
- int width;
- int this_ru_col;
- bool is_stl_global = global_stl_height() > 0;
- static int busy = false;
-
- // May get here recursively when 'statusline' (indirectly)
- // invokes ":redrawstatus". Simply ignore the call then.
- if (busy
- // Also ignore if wildmenu is showing.
- || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) {
- return;
- }
- busy = true;
-
- wp->w_redr_status = false;
- if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
- // no status line, either global statusline is enabled or the window is a last window
- redraw_cmdline = true;
- } else if (!redrawing()) {
- // Don't redraw right now, do it later. Don't update status line when
- // popup menu is visible and may be drawn over it
- wp->w_redr_status = true;
- } else if (*p_stl != NUL || *wp->w_p_stl != NUL) {
- // redraw custom status line
- redraw_custom_statusline(wp);
- } else {
- fillchar = fillchar_status(&attr, wp);
- width = is_stl_global ? Columns : wp->w_width;
-
- get_trans_bufname(wp->w_buffer);
- p = NameBuff;
- len = (int)STRLEN(p);
-
- if (bt_help(wp->w_buffer)
- || wp->w_p_pvw
- || bufIsChanged(wp->w_buffer)
- || wp->w_buffer->b_p_ro) {
- *(p + len++) = ' ';
- }
- if (bt_help(wp->w_buffer)) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
- len += (int)STRLEN(p + len);
- }
- if (wp->w_p_pvw) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
- len += (int)STRLEN(p + len);
- }
- if (bufIsChanged(wp->w_buffer)) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]");
- len += (int)STRLEN(p + len);
- }
- if (wp->w_buffer->b_p_ro) {
- snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
- // len += (int)STRLEN(p + len); // dead assignment
- }
-
- this_ru_col = ru_col - (Columns - width);
- if (this_ru_col < (width + 1) / 2) {
- this_ru_col = (width + 1) / 2;
- }
- if (this_ru_col <= 1) {
- p = (char_u *)"<"; // No room for file name!
- len = 1;
- } else {
- int clen = 0, i;
-
- // Count total number of display cells.
- clen = (int)mb_string2cells((char *)p);
-
- // Find first character that will fit.
- // Going from start to end is much faster for DBCS.
- for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
- i += utfc_ptr2len((char *)p + i)) {
- clen -= utf_ptr2cells((char *)p + i);
- }
- len = clen;
- if (i > 0) {
- p = p + i - 1;
- *p = '<';
- ++len;
- }
- }
-
- row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
- col = is_stl_global ? 0 : wp->w_wincol;
- grid_puts(&default_grid, p, row, col, attr);
- grid_fill(&default_grid, row, row + 1, len + col,
- this_ru_col + col, fillchar, fillchar, attr);
-
- if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL)
- && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
- grid_puts(&default_grid, NameBuff, row,
- (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr);
- }
-
- win_redr_ruler(wp, true);
- }
-
- /*
- * May need to draw the character below the vertical separator.
- */
- if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) {
- if (stl_connected(wp)) {
- fillchar = fillchar_status(&attr, wp);
- } else {
- fillchar = fillchar_vsep(wp, &attr);
- }
- grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr);
- }
- busy = FALSE;
-}
-
-/*
- * Redraw the status line according to 'statusline' and take care of any
- * errors encountered.
- */
-static void redraw_custom_statusline(win_T *wp)
-{
- static bool entered = false;
- int saved_did_emsg = did_emsg;
-
- /* When called recursively return. This can happen when the statusline
- * contains an expression that triggers a redraw. */
- if (entered) {
- return;
- }
- entered = true;
-
- did_emsg = false;
- win_redr_custom(wp, false, false);
- if (did_emsg) {
- // When there is an error disable the statusline, otherwise the
- // display is messed up with errors and a redraw triggers the problem
- // again and again.
- set_string_option_direct("statusline", -1, "",
- OPT_FREE | (*wp->w_p_stl != NUL
- ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
- }
- did_emsg |= saved_did_emsg;
- entered = false;
-}
-
-static void win_redr_winbar(win_T *wp)
-{
- static bool entered = false;
-
- // Return when called recursively. This can happen when the winbar contains an expression
- // that triggers a redraw.
- if (entered) {
- return;
- }
- entered = true;
-
- if (wp->w_winbar_height == 0 || !redrawing()) {
- // Do nothing.
- } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
- int saved_did_emsg = did_emsg;
-
- did_emsg = false;
- win_redr_custom(wp, true, false);
- if (did_emsg) {
- // When there is an error disable the winbar, otherwise the
- // display is messed up with errors and a redraw triggers the problem
- // again and again.
- set_string_option_direct("winbar", -1, "",
- OPT_FREE | (*wp->w_p_stl != NUL
- ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
- }
- did_emsg |= saved_did_emsg;
- }
- entered = false;
-}
-
/// Only call if (wp->w_vsep_width != 0).
///
/// @return true if the status line of window "wp" is connected to the status
@@ -5149,77 +507,6 @@ bool stl_connected(win_T *wp)
return false;
}
-/// Check if horizontal separator of window "wp" at specified window corner is connected to the
-/// horizontal separator of another window
-/// Assumes global statusline is enabled
-static bool hsep_connected(win_T *wp, WindowCorner corner)
-{
- bool before = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT);
- int sep_row = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT)
- ? wp->w_winrow - 1 : W_ENDROW(wp);
- frame_T *fr = wp->w_frame;
-
- while (fr->fr_parent != NULL) {
- if (fr->fr_parent->fr_layout == FR_ROW && (before ? fr->fr_prev : fr->fr_next) != NULL) {
- fr = before ? fr->fr_prev : fr->fr_next;
- break;
- }
- fr = fr->fr_parent;
- }
- if (fr->fr_parent == NULL) {
- return false;
- }
- while (fr->fr_layout != FR_LEAF) {
- fr = fr->fr_child;
- if (fr->fr_parent->fr_layout == FR_ROW && before) {
- while (fr->fr_next != NULL) {
- fr = fr->fr_next;
- }
- } else {
- while (fr->fr_next != NULL && frame2win(fr)->w_winrow + fr->fr_height < sep_row) {
- fr = fr->fr_next;
- }
- }
- }
-
- return (sep_row == fr->fr_win->w_winrow - 1 || sep_row == W_ENDROW(fr->fr_win));
-}
-
-/// Check if vertical separator of window "wp" at specified window corner is connected to the
-/// vertical separator of another window
-static bool vsep_connected(win_T *wp, WindowCorner corner)
-{
- bool before = (corner == WC_TOP_LEFT || corner == WC_TOP_RIGHT);
- int sep_col = (corner == WC_TOP_LEFT || corner == WC_BOTTOM_LEFT)
- ? wp->w_wincol - 1 : W_ENDCOL(wp);
- frame_T *fr = wp->w_frame;
-
- while (fr->fr_parent != NULL) {
- if (fr->fr_parent->fr_layout == FR_COL && (before ? fr->fr_prev : fr->fr_next) != NULL) {
- fr = before ? fr->fr_prev : fr->fr_next;
- break;
- }
- fr = fr->fr_parent;
- }
- if (fr->fr_parent == NULL) {
- return false;
- }
- while (fr->fr_layout != FR_LEAF) {
- fr = fr->fr_child;
- if (fr->fr_parent->fr_layout == FR_COL && before) {
- while (fr->fr_next != NULL) {
- fr = fr->fr_next;
- }
- } else {
- while (fr->fr_next != NULL && frame2win(fr)->w_wincol + fr->fr_width < sep_col) {
- fr = fr->fr_next;
- }
- }
- }
-
- return (sep_col == fr->fr_win->w_wincol - 1 || sep_col == W_ENDCOL(fr->fr_win));
-}
-
/// Get the value to show for the language mappings, active 'keymap'.
///
/// @param fmt format string containing one %s item
@@ -5248,7 +535,7 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
curwin = old_curwin;
if (p == NULL || *p == NUL) {
if (wp->w_buffer->b_kmap_state & KEYMAP_LOADED) {
- p = (char *)wp->w_buffer->b_p_keymap;
+ p = wp->w_buffer->b_p_keymap;
} else {
p = "lang";
}
@@ -5261,305 +548,23 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
return buf[0] != NUL;
}
-/// Redraw the status line, window bar or ruler of window "wp".
-/// When "wp" is NULL redraw the tab pages line from 'tabline'.
-static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
-{
- static bool entered = false;
- int attr;
- int curattr;
- int row;
- int col = 0;
- int maxwidth;
- int width;
- int n;
- int len;
- int fillchar;
- char buf[MAXPATHL];
- char_u *stl;
- char *p;
- stl_hlrec_t *hltab;
- StlClickRecord *tabtab;
- int use_sandbox = false;
- win_T *ewp;
- int p_crb_save;
- bool is_stl_global = global_stl_height() > 0;
-
- ScreenGrid *grid = &default_grid;
-
- /* There is a tiny chance that this gets called recursively: When
- * redrawing a status line triggers redrawing the ruler or tabline.
- * Avoid trouble by not allowing recursion. */
- if (entered) {
- return;
- }
- entered = true;
-
- // setup environment for the task at hand
- if (wp == NULL) {
- // Use 'tabline'. Always at the first line of the screen.
- stl = p_tal;
- row = 0;
- fillchar = ' ';
- attr = HL_ATTR(HLF_TPF);
- maxwidth = Columns;
- use_sandbox = was_set_insecurely(wp, "tabline", 0);
- } else if (draw_winbar) {
- stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
- row = -1; // row zero is first row of text
- col = 0;
- grid = &wp->w_grid;
- grid_adjust(&grid, &row, &col);
-
- if (row < 0) {
- return;
- }
-
- fillchar = wp->w_p_fcs_chars.wbr;
- attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
- maxwidth = wp->w_width_inner;
- use_sandbox = was_set_insecurely(wp, "winbar", 0);
-
- stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
- // Allocate / resize the click definitions array for winbar if needed.
- if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) {
- xfree(wp->w_winbar_click_defs);
- wp->w_winbar_click_defs_size = (size_t)maxwidth;
- wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord));
- }
- } else {
- row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
- fillchar = fillchar_status(&attr, wp);
- maxwidth = is_stl_global ? Columns : wp->w_width;
-
- stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
- // Allocate / resize the click definitions array for statusline if needed.
- if (wp->w_status_click_defs_size < (size_t)maxwidth) {
- xfree(wp->w_status_click_defs);
- wp->w_status_click_defs_size = (size_t)maxwidth;
- wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
- }
-
- if (draw_ruler) {
- stl = p_ruf;
- // advance past any leading group spec - implicit in ru_col
- if (*stl == '%') {
- if (*++stl == '-') {
- stl++;
- }
- if (atoi((char *)stl)) {
- while (ascii_isdigit(*stl)) {
- stl++;
- }
- }
- if (*stl++ != '(') {
- stl = p_ruf;
- }
- }
- col = ru_col - (Columns - maxwidth);
- if (col < (maxwidth + 1) / 2) {
- col = (maxwidth + 1) / 2;
- }
- maxwidth = maxwidth - col;
- if (!wp->w_status_height && !is_stl_global) {
- grid = &msg_grid_adj;
- row = Rows - 1;
- maxwidth--; // writing in last column may cause scrolling
- fillchar = ' ';
- attr = HL_ATTR(HLF_MSG);
- }
-
- use_sandbox = was_set_insecurely(wp, "rulerformat", 0);
- } else {
- if (*wp->w_p_stl != NUL) {
- stl = wp->w_p_stl;
- } else {
- stl = p_stl;
- }
- use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
- }
-
- col += is_stl_global ? 0 : wp->w_wincol;
- }
-
- if (maxwidth <= 0) {
- goto theend;
- }
-
- /* Temporarily reset 'cursorbind', we don't want a side effect from moving
- * the cursor away and back. */
- ewp = wp == NULL ? curwin : wp;
- p_crb_save = ewp->w_p_crb;
- ewp->w_p_crb = FALSE;
-
- /* Make a copy, because the statusline may include a function call that
- * might change the option value and free the memory. */
- stl = vim_strsave(stl);
- width =
- build_stl_str_hl(ewp, buf, sizeof(buf), (char *)stl, use_sandbox,
- fillchar, maxwidth, &hltab, &tabtab);
- xfree(stl);
- ewp->w_p_crb = p_crb_save;
-
- // Make all characters printable.
- p = transstr(buf, true);
- len = (int)STRLCPY(buf, p, sizeof(buf));
- len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
- xfree(p);
-
- // fill up with "fillchar"
- while (width < maxwidth && len < (int)sizeof(buf) - 1) {
- len += utf_char2bytes(fillchar, buf + len);
- width++;
- }
- buf[len] = NUL;
-
- /*
- * Draw each snippet with the specified highlighting.
- */
- grid_puts_line_start(grid, row);
-
- curattr = attr;
- p = buf;
- for (n = 0; hltab[n].start != NULL; n++) {
- int textlen = (int)(hltab[n].start - p);
- grid_puts_len(grid, (char_u *)p, textlen, row, col, curattr);
- col += vim_strnsize((char_u *)p, textlen);
- p = hltab[n].start;
-
- if (hltab[n].userhl == 0) {
- curattr = attr;
- } else if (hltab[n].userhl < 0) {
- curattr = syn_id2attr(-hltab[n].userhl);
- } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
- curattr = highlight_stlnc[hltab[n].userhl - 1];
- } else {
- curattr = highlight_user[hltab[n].userhl - 1];
- }
- }
- // Make sure to use an empty string instead of p, if p is beyond buf + len.
- grid_puts(grid, p >= buf + len ? (char_u *)"" : (char_u *)p, row, col,
- curattr);
-
- grid_puts_line_flush(false);
-
- // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
- // in the tab page line, status line or window bar
- StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs
- : draw_winbar ? wp->w_winbar_click_defs
- : wp->w_status_click_defs;
-
- if (click_defs == NULL) {
- goto theend;
- }
-
- col = 0;
- len = 0;
- p = buf;
- StlClickDefinition cur_click_def = {
- .type = kStlClickDisabled,
- };
- for (n = 0; tabtab[n].start != NULL; n++) {
- len += vim_strnsize((char_u *)p, (int)(tabtab[n].start - p));
- while (col < len) {
- click_defs[col++] = cur_click_def;
- }
- p = (char *)tabtab[n].start;
- cur_click_def = tabtab[n].def;
- if ((wp != NULL) && !(cur_click_def.type == kStlClickDisabled
- || cur_click_def.type == kStlClickFuncRun)) {
- // window bar and status line only support click functions
- cur_click_def.type = kStlClickDisabled;
- }
- }
- while (col < maxwidth) {
- click_defs[col++] = cur_click_def;
- }
-
-theend:
- entered = false;
-}
-
-static void win_redr_border(win_T *wp)
-{
- wp->w_redr_border = false;
- if (!(wp->w_floating && wp->w_float_config.border)) {
- return;
- }
-
- ScreenGrid *grid = &wp->w_grid_alloc;
-
- schar_T *chars = wp->w_float_config.border_chars;
- int *attrs = wp->w_float_config.border_attr;
-
- int *adj = wp->w_border_adj;
- int irow = wp->w_height_inner + wp->w_winbar_height, icol = wp->w_width_inner;
-
- if (adj[0]) {
- grid_puts_line_start(grid, 0);
- if (adj[3]) {
- grid_put_schar(grid, 0, 0, chars[0], attrs[0]);
- }
- for (int i = 0; i < icol; i++) {
- grid_put_schar(grid, 0, i + adj[3], chars[1], attrs[1]);
- }
- if (adj[1]) {
- grid_put_schar(grid, 0, icol + adj[3], chars[2], attrs[2]);
- }
- grid_puts_line_flush(false);
- }
-
- for (int i = 0; i < irow; i++) {
- if (adj[3]) {
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], 0, chars[7], attrs[7]);
- grid_puts_line_flush(false);
- }
- if (adj[1]) {
- int ic = (i == 0 && !adj[0] && chars[2][0]) ? 2 : 3;
- grid_puts_line_start(grid, i + adj[0]);
- grid_put_schar(grid, i + adj[0], icol + adj[3], chars[ic], attrs[ic]);
- grid_puts_line_flush(false);
- }
- }
-
- if (adj[2]) {
- grid_puts_line_start(grid, irow + adj[0]);
- if (adj[3]) {
- grid_put_schar(grid, irow + adj[0], 0, chars[6], attrs[6]);
- }
- for (int i = 0; i < icol; i++) {
- int ic = (i == 0 && !adj[3] && chars[6][0]) ? 6 : 5;
- grid_put_schar(grid, irow + adj[0], i + adj[3], chars[ic], attrs[ic]);
- }
- if (adj[1]) {
- grid_put_schar(grid, irow + adj[0], icol + adj[3], chars[4], attrs[4]);
- }
- grid_puts_line_flush(false);
- }
-}
-
-/*
- * Prepare for 'hlsearch' highlighting.
- */
-static void start_search_hl(void)
+/// Prepare for 'hlsearch' highlighting.
+void start_search_hl(void)
{
if (p_hls && !no_hlsearch) {
end_search_hl(); // just in case it wasn't called before
- last_pat_prog(&search_hl.rm);
+ last_pat_prog(&screen_search_hl.rm);
// Set the time limit to 'redrawtime'.
- search_hl.tm = profile_setlimit(p_rdt);
+ screen_search_hl.tm = profile_setlimit(p_rdt);
}
}
-/*
- * Clean up for 'hlsearch' highlighting.
- */
-static void end_search_hl(void)
+/// Clean up for 'hlsearch' highlighting.
+void end_search_hl(void)
{
- if (search_hl.rm.regprog != NULL) {
- vim_regfree(search_hl.rm.regprog);
- search_hl.rm.regprog = NULL;
+ if (screen_search_hl.rm.regprog != NULL) {
+ vim_regfree(screen_search_hl.rm.regprog);
+ screen_search_hl.rm.regprog = NULL;
}
}
@@ -5579,105 +584,6 @@ void check_for_delay(bool check_msg_scroll)
}
}
-/// Resize the screen to Rows and Columns.
-///
-/// Allocate default_grid.chars[] and other grid arrays.
-///
-/// There may be some time between setting Rows and Columns and (re)allocating
-/// default_grid arrays. This happens when starting up and when
-/// (manually) changing the screen size. Always use default_grid.rows and
-/// default_grid.Columns to access items in default_grid.chars[]. Use Rows
-/// and Columns for positioning text etc. where the final size of the screen is
-/// needed.
-void screenalloc(void)
-{
- // It's possible that we produce an out-of-memory message below, which
- // will cause this function to be called again. To break the loop, just
- // return here.
- if (resizing) {
- return;
- }
- resizing = true;
-
- int retry_count = 0;
-
-retry:
- // Allocation of the screen buffers is done only when the size changes and
- // when Rows and Columns have been set and we have started doing full
- // screen stuff.
- if ((default_grid.chars != NULL
- && Rows == default_grid.rows
- && Columns == default_grid.cols
- )
- || Rows == 0
- || Columns == 0
- || (!full_screen && default_grid.chars == NULL)) {
- resizing = false;
- return;
- }
-
- /*
- * Note that the window sizes are updated before reallocating the arrays,
- * thus we must not redraw here!
- */
- ++RedrawingDisabled;
-
- // win_new_screensize will recompute floats position, but tell the
- // compositor to not redraw them yet
- ui_comp_set_screen_valid(false);
- if (msg_grid.chars) {
- msg_grid_invalid = true;
- }
-
- win_new_screensize(); // fit the windows in the new sized screen
-
- comp_col(); // recompute columns for shown command and ruler
-
- // We're changing the size of the screen.
- // - Allocate new arrays for default_grid
- // - Move lines from the old arrays into the new arrays, clear extra
- // lines (unless the screen is going to be cleared).
- // - Free the old arrays.
- //
- // If anything fails, make grid arrays NULL, so we don't do anything!
- // Continuing with the old arrays may result in a crash, because the
- // size is wrong.
-
- grid_alloc(&default_grid, Rows, Columns, true, true);
- StlClickDefinition *new_tab_page_click_defs =
- xcalloc((size_t)Columns, sizeof(*new_tab_page_click_defs));
-
- stl_clear_click_defs(tab_page_click_defs, tab_page_click_defs_size);
- xfree(tab_page_click_defs);
-
- tab_page_click_defs = new_tab_page_click_defs;
- tab_page_click_defs_size = Columns;
-
- default_grid.comp_height = Rows;
- default_grid.comp_width = Columns;
-
- default_grid.row_offset = 0;
- default_grid.col_offset = 0;
- default_grid.handle = DEFAULT_GRID_HANDLE;
-
- must_redraw = CLEAR; // need to clear the screen later
-
- RedrawingDisabled--;
-
- /*
- * Do not apply autocommands more than 3 times to avoid an endless loop
- * in case applying autocommands always changes Rows or Columns.
- */
- if (starting == 0 && ++retry_count <= 3) {
- apply_autocmds(EVENT_VIMRESIZED, NULL, NULL, false, curbuf);
- // In rare cases, autocommands may have altered Rows or Columns,
- // jump back to check if we need to allocate the screen again.
- goto retry;
- }
-
- resizing = false;
-}
-
/// Clear status line, window bar or tab page line click definition table
///
/// @param[out] tpcd Table to clear.
@@ -5694,67 +600,6 @@ void stl_clear_click_defs(StlClickDefinition *const click_defs, const long click
}
}
-void screenclear(void)
-{
- check_for_delay(false);
- screenalloc(); // allocate screen buffers if size changed
-
- int i;
-
- if (starting == NO_SCREEN || default_grid.chars == NULL) {
- return;
- }
-
- // blank out the default grid
- for (i = 0; i < default_grid.rows; i++) {
- grid_clear_line(&default_grid, default_grid.line_offset[i],
- default_grid.cols, true);
- default_grid.line_wraps[i] = false;
- }
-
- ui_call_grid_clear(1); // clear the display
- ui_comp_set_screen_valid(true);
-
- clear_cmdline = false;
- mode_displayed = false;
-
- redraw_all_later(NOT_VALID);
- redraw_cmdline = true;
- redraw_tabline = true;
- redraw_popupmenu = true;
- pum_invalidate();
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_floating) {
- wp->w_redr_type = CLEAR;
- }
- }
- if (must_redraw == CLEAR) {
- must_redraw = NOT_VALID; // no need to clear again
- }
- compute_cmdrow();
- msg_row = cmdline_row; // put cursor on last line for messages
- msg_col = 0;
- msg_scrolled = 0; // can't scroll back
- msg_didany = false;
- msg_didout = false;
- if (HL_ATTR(HLF_MSG) > 0 && msg_use_grid() && msg_grid.chars) {
- grid_invalidate(&msg_grid);
- msg_grid_validate();
- msg_grid_invalid = false;
- clear_cmdline = true;
- }
-}
-
-/// Copy part of a grid line for vertically split window.
-static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
-{
- unsigned off_to = (unsigned)(grid->line_offset[to] + (size_t)col);
- unsigned off_from = (unsigned)(grid->line_offset[from] + (size_t)col);
-
- memmove(grid->chars + off_to, grid->chars + off_from, (size_t)width * sizeof(schar_T));
- memmove(grid->attrs + off_to, grid->attrs + off_from, (size_t)width * sizeof(sattr_T));
-}
-
/// Set cursor to its position in the current window.
void setcursor(void)
{
@@ -5784,9 +629,9 @@ void setcursor_mayforce(bool force)
}
}
-/// Scroll 'line_count' lines at 'row' in window 'wp'.
+/// Scroll `line_count` lines at 'row' in window 'wp'.
///
-/// Positive `line_count' means scrolling down, so that more space is available
+/// Positive `line_count` means scrolling down, so that more space is available
/// at 'row'. Negative `line_count` implies deleting lines at `row`.
void win_scroll_lines(win_T *wp, int row, int line_count)
{
@@ -5808,126 +653,29 @@ void win_scroll_lines(win_T *wp, int row, int line_count)
}
}
-/*
- * The rest of the routines in this file perform screen manipulations. The
- * given operation is performed physically on the screen. The corresponding
- * change is also made to the internal screen image. In this way, the editor
- * anticipates the effect of editing changes on the appearance of the screen.
- * That way, when we call screenupdate a complete redraw isn't usually
- * necessary. Another advantage is that we can keep adding code to anticipate
- * screen changes, and in the meantime, everything still works.
- */
-
-/// insert lines on the screen and move the existing lines down
-/// 'line_count' is the number of lines to be inserted.
-/// 'end' is the line after the scrolled part. Normally it is Rows.
-/// 'col' is the column from with we start inserting.
-//
-/// 'row', 'col' and 'end' are relative to the start of the region.
-void grid_ins_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
-{
- int i;
- int j;
- unsigned temp;
-
- int row_off = 0;
- grid_adjust(&grid, &row_off, &col);
- row += row_off;
- end += row_off;
-
- if (line_count <= 0) {
- return;
- }
-
- // Shift line_offset[] line_count down to reflect the inserted lines.
- // Clear the inserted lines.
- for (i = 0; i < line_count; i++) {
- if (width != grid->cols) {
- // need to copy part of a line
- j = end - 1 - i;
- while ((j -= line_count) >= row) {
- linecopy(grid, j + line_count, j, col, width);
- }
- j += line_count;
- grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
- } else {
- j = end - 1 - i;
- temp = (unsigned)grid->line_offset[j];
- while ((j -= line_count) >= row) {
- grid->line_offset[j + line_count] = grid->line_offset[j];
- grid->line_wraps[j + line_count] = grid->line_wraps[j];
- }
- grid->line_offset[j + line_count] = temp;
- grid->line_wraps[j + line_count] = false;
- grid_clear_line(grid, temp, grid->cols, false);
- }
- }
-
- if (!grid->throttled) {
- ui_call_grid_scroll(grid->handle, row, end, col, col + width, -line_count, 0);
- }
-}
-
-/// delete lines on the screen and move lines up.
-/// 'end' is the line after the scrolled part. Normally it is Rows.
-/// When scrolling region used 'off' is the offset from the top for the region.
-/// 'row' and 'end' are relative to the start of the region.
-void grid_del_lines(ScreenGrid *grid, int row, int line_count, int end, int col, int width)
+/// @return true when postponing displaying the mode message: when not redrawing
+/// or inside a mapping.
+bool skip_showmode(void)
{
- int j;
- int i;
- unsigned temp;
-
- int row_off = 0;
- grid_adjust(&grid, &row_off, &col);
- row += row_off;
- end += row_off;
-
- if (line_count <= 0) {
- return;
- }
-
- // Now shift line_offset[] line_count up to reflect the deleted lines.
- // Clear the inserted lines.
- for (i = 0; i < line_count; i++) {
- if (width != grid->cols) {
- // need to copy part of a line
- j = row + i;
- while ((j += line_count) <= end - 1) {
- linecopy(grid, j - line_count, j, col, width);
- }
- j -= line_count;
- grid_clear_line(grid, grid->line_offset[j] + (size_t)col, width, false);
- grid->line_wraps[j] = false;
- } else {
- // whole width, moving the line pointers is faster
- j = row + i;
- temp = (unsigned)grid->line_offset[j];
- while ((j += line_count) <= end - 1) {
- grid->line_offset[j - line_count] = grid->line_offset[j];
- grid->line_wraps[j - line_count] = grid->line_wraps[j];
- }
- grid->line_offset[j - line_count] = temp;
- grid->line_wraps[j - line_count] = false;
- grid_clear_line(grid, temp, grid->cols, false);
- }
- }
-
- if (!grid->throttled) {
- ui_call_grid_scroll(grid->handle, row, end, col, col + width, line_count, 0);
+ // Call char_avail() only when we are going to show something, because it
+ // takes a bit of time. redrawing() may also call char_avail().
+ if (global_busy || msg_silent != 0 || !redrawing() || (char_avail() && !KeyTyped)) {
+ redraw_mode = true; // show mode later
+ return true;
}
+ return false;
}
-// Show the current mode and ruler.
-//
-// If clear_cmdline is true, clear the rest of the cmdline.
-// If clear_cmdline is false there may be a message there that needs to be
-// cleared only if a mode is shown.
-// Return the length of the message (0 if no message).
+/// Show the current mode and ruler.
+///
+/// If clear_cmdline is true, clear the rest of the cmdline.
+/// If clear_cmdline is false there may be a message there that needs to be
+/// cleared only if a mode is shown.
+/// If redraw_mode is true show or clear the mode.
+/// @return the length of the message (0 if no message).
int showmode(void)
{
- int need_clear;
+ bool need_clear;
int length = 0;
int do_mode;
int attr;
@@ -5948,12 +696,8 @@ int showmode(void)
|| restart_edit != NUL
|| VIsual_active));
if (do_mode || reg_recording != 0) {
- // Don't show mode right now, when not redrawing or inside a mapping.
- // Call char_avail() only when we are going to show something, because
- // it takes a bit of time.
- if (!redrawing() || (char_avail() && !KeyTyped) || msg_silent != 0) {
- redraw_cmdline = true; // show mode later
- return 0;
+ if (skip_showmode()) {
+ return 0; // show mode later
}
bool nwr_save = need_wait_return;
@@ -5990,13 +734,13 @@ int showmode(void)
length = (Rows - msg_row) * Columns - 3;
}
if (edit_submode_extra != NULL) {
- length -= vim_strsize((char *)edit_submode_extra);
+ length -= vim_strsize(edit_submode_extra);
}
if (length > 0) {
if (edit_submode_pre != NULL) {
- length -= vim_strsize((char *)edit_submode_pre);
+ length -= vim_strsize(edit_submode_pre);
}
- if (length - vim_strsize((char *)edit_submode) > 0) {
+ if (length - vim_strsize(edit_submode) > 0) {
if (edit_submode_pre != NULL) {
msg_puts_attr((const char *)edit_submode_pre, attr);
}
@@ -6077,7 +821,7 @@ int showmode(void)
msg_puts_attr(" --", attr);
}
- need_clear = TRUE;
+ need_clear = true;
}
if (reg_recording != 0
&& edit_submode == NULL // otherwise it gets too long
@@ -6087,7 +831,7 @@ int showmode(void)
}
mode_displayed = true;
- if (need_clear || clear_cmdline) {
+ if (need_clear || clear_cmdline || redraw_mode) {
msg_clr_eos();
}
msg_didout = false; // overwrite this message
@@ -6099,6 +843,9 @@ int showmode(void)
} else if (clear_cmdline && msg_silent == 0) {
// Clear the whole command line. Will reset "clear_cmdline".
msg_clr_cmdline();
+ } else if (redraw_mode) {
+ msg_pos_mode();
+ msg_clr_eos();
}
// NB: also handles clearing the showmode if it was empty or disabled
@@ -6116,14 +863,13 @@ int showmode(void)
win_redr_ruler(last, true);
}
redraw_cmdline = false;
+ redraw_mode = false;
clear_cmdline = false;
return length;
}
-/*
- * Position for a mode message.
- */
+/// Position for a mode message.
static void msg_pos_mode(void)
{
msg_col = 0;
@@ -6171,9 +917,7 @@ static void recording_mode(int attr)
}
}
-/*
- * Draw the tab pages line at the top of the Vim window.
- */
+/// Draw the tab pages line at the top of the Vim window.
void draw_tabline(void)
{
int tabcount = 0;
@@ -6191,8 +935,7 @@ void draw_tabline(void)
int attr_fill = HL_ATTR(HLF_TPF);
char_u *p;
int room;
- int use_sep_chars = (t_colors < 8
- );
+ int use_sep_chars = (t_colors < 8);
if (default_grid.chars == NULL) {
return;
@@ -6226,7 +969,7 @@ void draw_tabline(void)
did_emsg |= saved_did_emsg;
} else {
FOR_ALL_TABS(tp) {
- ++tabcount;
+ tabcount++;
}
if (tabcount > 0) {
@@ -6270,7 +1013,7 @@ void draw_tabline(void)
modified = false;
- for (wincount = 0; wp != NULL; wp = wp->w_next, ++wincount) {
+ for (wincount = 0; wp != NULL; wp = wp->w_next, wincount++) {
if (bufIsChanged(wp->w_buffer)) {
modified = true;
}
@@ -6288,7 +1031,7 @@ void draw_tabline(void)
col += len;
}
if (modified) {
- grid_puts_len(&default_grid, (char_u *)"+", 1, 0, col++, attr);
+ grid_puts_len(&default_grid, "+", 1, 0, col++, attr);
}
grid_putchar(&default_grid, ' ', 0, col++, attr);
}
@@ -6299,7 +1042,7 @@ void draw_tabline(void)
get_trans_bufname(cwp->w_buffer);
shorten_dir(NameBuff);
len = vim_strsize((char *)NameBuff);
- p = NameBuff;
+ p = (char_u *)NameBuff;
while (len > room) {
len -= ptr2cells((char *)p);
MB_PTR_ADV(p);
@@ -6308,7 +1051,7 @@ void draw_tabline(void)
len = Columns - col - 1;
}
- grid_puts_len(&default_grid, p, (int)STRLEN(p), 0, col, attr);
+ grid_puts_len(&default_grid, (char *)p, (int)STRLEN(p), 0, col, attr);
col += len;
}
grid_putchar(&default_grid, ' ', 0, col++, attr);
@@ -6348,10 +1091,9 @@ void draw_tabline(void)
redraw_tabline = false;
}
-void ui_ext_tabline_update(void)
+static void ui_ext_tabline_update(void)
{
Arena arena = ARENA_EMPTY;
- arena_start(&arena, &ui_ext_fixblk);
size_t n_tabs = 0;
FOR_ALL_TABS(tp) {
@@ -6392,13 +1134,9 @@ void ui_ext_tabline_update(void)
}
ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
}
-/*
- * Get buffer name for "buf" into NameBuff[].
- * Takes care of special buffer names and translates special characters.
- */
void get_trans_bufname(buf_T *buf)
{
if (buf_spname(buf) != NULL) {
@@ -6409,37 +1147,9 @@ void get_trans_bufname(buf_T *buf)
trans_characters((char *)NameBuff, MAXPATHL);
}
-/*
- * Get the character to use in a status line. Get its attributes in "*attr".
- */
-int fillchar_status(int *attr, win_T *wp)
-{
- int fill;
- bool is_curwin = (wp == curwin);
- if (is_curwin) {
- *attr = win_hl_attr(wp, HLF_S);
- fill = wp->w_p_fcs_chars.stl;
- } else {
- *attr = win_hl_attr(wp, HLF_SNC);
- fill = wp->w_p_fcs_chars.stlnc;
- }
- // Use fill when there is highlighting, and highlighting of current
- // window differs, or the fillchars differ, or this is not the
- // current window
- if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
- || !is_curwin || ONE_WINDOW)
- || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) {
- return fill;
- }
- if (is_curwin) {
- return '^';
- }
- return '=';
-}
-
/// Get the character to use in a separator between vertically split windows.
/// Get its attributes in "*attr".
-static int fillchar_vsep(win_T *wp, int *attr)
+int fillchar_vsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.vert;
@@ -6447,240 +1157,68 @@ static int fillchar_vsep(win_T *wp, int *attr)
/// Get the character to use in a separator between horizontally split windows.
/// Get its attributes in "*attr".
-static int fillchar_hsep(win_T *wp, int *attr)
+int fillchar_hsep(win_T *wp, int *attr)
{
*attr = win_hl_attr(wp, HLF_C);
return wp->w_p_fcs_chars.horiz;
}
-/*
- * Return TRUE if redrawing should currently be done.
- */
-int redrawing(void)
+/// Return true if redrawing should currently be done.
+bool redrawing(void)
{
return !RedrawingDisabled
&& !(p_lz && char_avail() && !KeyTyped && !do_redraw);
}
-/*
- * Return TRUE if printing messages should currently be done.
- */
-int messaging(void)
+/// Return true if printing messages should currently be done.
+bool messaging(void)
{
return !(p_lz && char_avail() && !KeyTyped) && ui_has_messages();
}
-/// Show current status info in ruler and various other places
-///
-/// @param always if false, only show ruler if position has changed.
-void showruler(bool always)
-{
- if (!always && !redrawing()) {
- return;
- }
- if ((*p_stl != NUL || *curwin->w_p_stl != NUL)
- && (curwin->w_status_height || global_stl_height())) {
- redraw_custom_statusline(curwin);
- } else {
- win_redr_ruler(curwin, always);
- }
- if (*p_wbr != NUL || *curwin->w_p_wbr != NUL) {
- win_redr_winbar(curwin);
- }
-
- if (need_maketitle
- || (p_icon && (stl_syntax & STL_IN_ICON))
- || (p_title && (stl_syntax & STL_IN_TITLE))) {
- maketitle();
- }
- // Redraw the tab pages line if needed.
- if (redraw_tabline) {
- draw_tabline();
- }
-}
+#define COL_RULER 17 // columns needed by standard ruler
-static void win_redr_ruler(win_T *wp, bool always)
+/// Compute columns for ruler and shown command. 'sc_col' is also used to
+/// decide what the maximum length of a message on the status line can be.
+/// If there is a status line for the last window, 'sc_col' is independent
+/// of 'ru_col'.
+void comp_col(void)
{
- bool is_stl_global = global_stl_height() > 0;
- static bool did_show_ext_ruler = false;
-
- // If 'ruler' off, don't do anything
- if (!p_ru) {
- return;
- }
-
- /*
- * Check if cursor.lnum is valid, since win_redr_ruler() may be called
- * after deleting lines, before cursor.lnum is corrected.
- */
- if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
- return;
- }
+ int last_has_status = (p_ls > 1 || (p_ls == 1 && !ONE_WINDOW));
- // Don't draw the ruler while doing insert-completion, it might overwrite
- // the (long) mode message.
- if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) {
- if (edit_submode != NULL) {
- return;
+ sc_col = 0;
+ ru_col = 0;
+ if (p_ru) {
+ ru_col = (ru_wid ? ru_wid : COL_RULER) + 1;
+ // no last status line, adjust sc_col
+ if (!last_has_status) {
+ sc_col = ru_col;
}
}
-
- if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) {
- const int called_emsg_before = called_emsg;
- win_redr_custom(wp, false, true);
- if (called_emsg > called_emsg_before) {
- set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);
+ if (p_sc) {
+ sc_col += SHOWCMD_COLS;
+ if (!p_ru || last_has_status) { // no need for separating space
+ sc_col++;
}
- return;
}
-
- // Check if not in Insert mode and the line is empty (will show "0-1").
- int empty_line = false;
- if ((State & MODE_INSERT) == 0 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false) == NUL) {
- empty_line = true;
+ assert(sc_col >= 0
+ && INT_MIN + sc_col <= Columns);
+ sc_col = Columns - sc_col;
+ assert(ru_col >= 0
+ && INT_MIN + ru_col <= Columns);
+ ru_col = Columns - ru_col;
+ if (sc_col <= 0) { // screen too narrow, will become a mess
+ sc_col = 1;
}
-
- /*
- * Only draw the ruler when something changed.
- */
- validate_virtcol_win(wp);
- if (redraw_cmdline
- || always
- || wp->w_cursor.lnum != wp->w_ru_cursor.lnum
- || wp->w_cursor.col != wp->w_ru_cursor.col
- || wp->w_virtcol != wp->w_ru_virtcol
- || wp->w_cursor.coladd != wp->w_ru_cursor.coladd
- || wp->w_topline != wp->w_ru_topline
- || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count
- || wp->w_topfill != wp->w_ru_topfill
- || empty_line != wp->w_ru_empty) {
- int width;
- int row;
- int fillchar;
- int attr;
- int off;
- bool part_of_status = false;
-
- if (wp->w_status_height) {
- row = W_ENDROW(wp);
- fillchar = fillchar_status(&attr, wp);
- off = wp->w_wincol;
- width = wp->w_width;
- part_of_status = true;
- } else if (is_stl_global) {
- row = Rows - (int)p_ch - 1;
- fillchar = fillchar_status(&attr, wp);
- off = 0;
- width = Columns;
- part_of_status = true;
- } else {
- row = Rows - 1;
- fillchar = ' ';
- attr = HL_ATTR(HLF_MSG);
- width = Columns;
- off = 0;
- }
-
- if (!part_of_status && !ui_has_messages()) {
- return;
- }
-
- // In list mode virtcol needs to be recomputed
- colnr_T virtcol = wp->w_virtcol;
- if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
- wp->w_p_list = false;
- getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
- wp->w_p_list = true;
- }
-
-#define RULER_BUF_LEN 70
- char buffer[RULER_BUF_LEN];
-
- /*
- * Some sprintfs return the length, some return a pointer.
- * To avoid portability problems we use strlen() here.
- */
- vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
- (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) ? (int64_t)0L
- : (int64_t)wp->w_cursor.lnum);
- size_t len = STRLEN(buffer);
- col_print(buffer + len, RULER_BUF_LEN - len,
- empty_line ? 0 : (int)wp->w_cursor.col + 1,
- (int)virtcol + 1);
-
- /*
- * Add a "50%" if there is room for it.
- * On the last line, don't print in the last column (scrolls the
- * screen up on some terminals).
- */
- int i = (int)STRLEN(buffer);
- get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
- int o = i + vim_strsize(buffer + i + 1);
- if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
- o++;
- }
- int this_ru_col = ru_col - (Columns - width);
- if (this_ru_col < 0) {
- this_ru_col = 0;
- }
- // Never use more than half the window/screen width, leave the other half
- // for the filename.
- if (this_ru_col < (width + 1) / 2) {
- this_ru_col = (width + 1) / 2;
- }
- if (this_ru_col + o < width) {
- // Need at least 3 chars left for get_rel_pos() + NUL.
- while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) {
- i += utf_char2bytes(fillchar, buffer + i);
- o++;
- }
- get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
- }
-
- if (ui_has(kUIMessages) && !part_of_status) {
- MAXSIZE_TEMP_ARRAY(content, 1);
- MAXSIZE_TEMP_ARRAY(chunk, 2);
- ADD_C(chunk, INTEGER_OBJ(attr));
- ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer)));
- ADD_C(content, ARRAY_OBJ(chunk));
- ui_call_msg_ruler(content);
- did_show_ext_ruler = true;
- } else {
- if (did_show_ext_ruler) {
- ui_call_msg_ruler((Array)ARRAY_DICT_INIT);
- did_show_ext_ruler = false;
- }
- // Truncate at window boundary.
- o = 0;
- for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
- o += utf_ptr2cells(buffer + i);
- if (this_ru_col + o > width) {
- buffer[i] = NUL;
- break;
- }
- }
-
- ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
- grid_puts(grid, (char_u *)buffer, row, this_ru_col + off, attr);
- grid_fill(grid, row, row + 1,
- this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
- fillchar, attr);
- }
-
- wp->w_ru_cursor = wp->w_cursor;
- wp->w_ru_virtcol = wp->w_virtcol;
- wp->w_ru_empty = (char)empty_line;
- wp->w_ru_topline = wp->w_topline;
- wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count;
- wp->w_ru_topfill = wp->w_topfill;
+ if (ru_col <= 0) {
+ ru_col = 1;
}
+ set_vim_var_nr(VV_ECHOSPACE, sc_col - 1);
}
-/*
- * Return the width of the 'number' and 'relativenumber' column.
- * Caller may need to check if 'number' or 'relativenumber' is set.
- * Otherwise it depends on 'numberwidth' and the line count.
- */
+/// Return the width of the 'number' and 'relativenumber' column.
+/// Caller may need to check if 'number' or 'relativenumber' is set.
+/// Otherwise it depends on 'numberwidth' and the line count.
int number_width(win_T *wp)
{
int n;
@@ -6702,7 +1240,7 @@ int number_width(win_T *wp)
n = 0;
do {
lnum /= 10;
- ++n;
+ n++;
} while (lnum > 0);
// 'numberwidth' gives the minimal width plus one
@@ -6721,155 +1259,282 @@ int number_width(win_T *wp)
return n;
}
-/// Used when 'cursorlineopt' contains "screenline": compute the margins between
-/// which the highlighting is used.
-static void margin_columns_win(win_T *wp, int *left_col, int *right_col)
+/// Calls mb_cptr2char_adv(p) and returns the character.
+/// If "p" starts with "\x", "\u" or "\U" the hex or unicode value is used.
+/// Returns 0 for invalid hex or invalid UTF-8 byte.
+static int get_encoded_char_adv(const char_u **p)
{
- // cache previous calculations depending on w_virtcol
- static int saved_w_virtcol;
- static win_T *prev_wp;
- static int prev_left_col;
- static int prev_right_col;
- static int prev_col_off;
-
- int cur_col_off = win_col_off(wp);
- int width1;
- int width2;
-
- if (saved_w_virtcol == wp->w_virtcol && prev_wp == wp
- && prev_col_off == cur_col_off) {
- *right_col = prev_right_col;
- *left_col = prev_left_col;
- return;
- }
-
- width1 = wp->w_width - cur_col_off;
- width2 = width1 + win_col_off2(wp);
+ const char_u *s = *p;
- *left_col = 0;
- *right_col = width1;
-
- if (wp->w_virtcol >= (colnr_T)width1) {
- *right_col = width1 + ((wp->w_virtcol - width1) / width2 + 1) * width2;
- }
- if (wp->w_virtcol >= (colnr_T)width1 && width2 > 0) {
- *left_col = (wp->w_virtcol - width1) / width2 * width2 + width1;
+ if (s[0] == '\\' && (s[1] == 'x' || s[1] == 'u' || s[1] == 'U')) {
+ int64_t num = 0;
+ for (int bytes = s[1] == 'x' ? 1 : s[1] == 'u' ? 2 : 4; bytes > 0; bytes--) {
+ *p += 2;
+ int n = hexhex2nr((char *)(*p));
+ if (n < 0) {
+ return 0;
+ }
+ num = num * 256 + n;
+ }
+ *p += 2;
+ return (int)num;
}
- // cache values
- prev_left_col = *left_col;
- prev_right_col = *right_col;
- prev_wp = wp;
- saved_w_virtcol = wp->w_virtcol;
- prev_col_off = cur_col_off;
+ // TODO(bfredl): use schar_T representation and utfc_ptr2len
+ int clen = utf_ptr2len((const char *)s);
+ int c = mb_cptr2char_adv(p);
+ if (clen == 1 && c > 127) { // Invalid UTF-8 byte
+ return 0;
+ }
+ return c;
}
-/// Set dimensions of the Nvim application "screen".
-void screen_resize(int width, int height)
-{
- // Avoid recursiveness, can happen when setting the window size causes
- // another window-changed signal.
- if (updating_screen || resizing_screen) {
- return;
- }
+/// Handle setting 'listchars' or 'fillchars'.
+/// Assume monocell characters
+///
+/// @param varp either the global or the window-local value.
+/// @param apply if false, do not store the flags, only check for errors.
+/// @return error message, NULL if it's OK.
+char *set_chars_option(win_T *wp, char **varp, bool apply)
+{
+ const char_u *last_multispace = NULL; // Last occurrence of "multispace:"
+ const char_u *last_lmultispace = NULL; // Last occurrence of "leadmultispace:"
+ int multispace_len = 0; // Length of lcs-multispace string
+ int lead_multispace_len = 0; // Length of lcs-leadmultispace string
+ const bool is_listchars = (varp == &p_lcs || varp == &wp->w_p_lcs);
+
+ struct chars_tab {
+ int *cp; ///< char value
+ char *name; ///< char id
+ int def; ///< default value
+ };
- if (width < 0 || height < 0) { // just checking...
- return;
- }
+ // XXX: Characters taking 2 columns is forbidden (TUI limitation?). Set old defaults in this case.
+ struct chars_tab fcs_tab[] = {
+ { &wp->w_p_fcs_chars.stl, "stl", ' ' },
+ { &wp->w_p_fcs_chars.stlnc, "stlnc", ' ' },
+ { &wp->w_p_fcs_chars.wbr, "wbr", ' ' },
+ { &wp->w_p_fcs_chars.horiz, "horiz", char2cells(0x2500) == 1 ? 0x2500 : '-' }, // ─
+ { &wp->w_p_fcs_chars.horizup, "horizup", char2cells(0x2534) == 1 ? 0x2534 : '-' }, // â”´
+ { &wp->w_p_fcs_chars.horizdown, "horizdown", char2cells(0x252c) == 1 ? 0x252c : '-' }, // ┬
+ { &wp->w_p_fcs_chars.vert, "vert", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
+ { &wp->w_p_fcs_chars.vertleft, "vertleft", char2cells(0x2524) == 1 ? 0x2524 : '|' }, // ┤
+ { &wp->w_p_fcs_chars.vertright, "vertright", char2cells(0x251c) == 1 ? 0x251c : '|' }, // ├
+ { &wp->w_p_fcs_chars.verthoriz, "verthoriz", char2cells(0x253c) == 1 ? 0x253c : '+' }, // ┼
+ { &wp->w_p_fcs_chars.fold, "fold", char2cells(0x00b7) == 1 ? 0x00b7 : '-' }, // ·
+ { &wp->w_p_fcs_chars.foldopen, "foldopen", '-' },
+ { &wp->w_p_fcs_chars.foldclosed, "foldclose", '+' },
+ { &wp->w_p_fcs_chars.foldsep, "foldsep", char2cells(0x2502) == 1 ? 0x2502 : '|' }, // │
+ { &wp->w_p_fcs_chars.diff, "diff", '-' },
+ { &wp->w_p_fcs_chars.msgsep, "msgsep", ' ' },
+ { &wp->w_p_fcs_chars.eob, "eob", '~' },
+ };
- if (State == MODE_HITRETURN || State == MODE_SETWSIZE) {
- // postpone the resizing
- State = MODE_SETWSIZE;
- return;
- }
+ struct chars_tab lcs_tab[] = {
+ { &wp->w_p_lcs_chars.eol, "eol", NUL },
+ { &wp->w_p_lcs_chars.ext, "extends", NUL },
+ { &wp->w_p_lcs_chars.nbsp, "nbsp", NUL },
+ { &wp->w_p_lcs_chars.prec, "precedes", NUL },
+ { &wp->w_p_lcs_chars.space, "space", NUL },
+ { &wp->w_p_lcs_chars.tab2, "tab", NUL },
+ { &wp->w_p_lcs_chars.lead, "lead", NUL },
+ { &wp->w_p_lcs_chars.trail, "trail", NUL },
+ { &wp->w_p_lcs_chars.conceal, "conceal", NUL },
+ };
- /* curwin->w_buffer can be NULL when we are closing a window and the
- * buffer has already been closed and removing a scrollbar causes a resize
- * event. Don't resize then, it will happen after entering another buffer.
- */
- if (curwin->w_buffer == NULL) {
- return;
+ struct chars_tab *tab;
+ int entries;
+ const char_u *value = (char_u *)(*varp);
+ if (is_listchars) {
+ tab = lcs_tab;
+ entries = ARRAY_SIZE(lcs_tab);
+ if (varp == &wp->w_p_lcs && wp->w_p_lcs[0] == NUL) {
+ value = (char_u *)p_lcs; // local value is empty, use the global value
+ }
+ } else {
+ tab = fcs_tab;
+ entries = ARRAY_SIZE(fcs_tab);
+ if (varp == &wp->w_p_fcs && wp->w_p_fcs[0] == NUL) {
+ value = (char_u *)p_fcs; // local value is empty, use the global value
+ }
}
- resizing_screen = true;
+ // first round: check for valid value, second round: assign values
+ for (int round = 0; round <= (apply ? 1 : 0); round++) {
+ if (round > 0) {
+ // After checking that the value is valid: set defaults
+ for (int i = 0; i < entries; i++) {
+ if (tab[i].cp != NULL) {
+ *(tab[i].cp) = tab[i].def;
+ }
+ }
+ if (is_listchars) {
+ wp->w_p_lcs_chars.tab1 = NUL;
+ wp->w_p_lcs_chars.tab3 = NUL;
- Rows = height;
- Columns = width;
- check_screensize();
- int max_p_ch = Rows - min_rows() + 1;
- if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) {
- p_ch = max_p_ch ? max_p_ch : 1;
- }
- height = Rows;
- width = Columns;
- p_lines = Rows;
- p_columns = Columns;
- ui_call_grid_resize(1, width, height);
+ xfree(wp->w_p_lcs_chars.multispace);
+ if (multispace_len > 0) {
+ wp->w_p_lcs_chars.multispace = xmalloc(((size_t)multispace_len + 1) * sizeof(int));
+ wp->w_p_lcs_chars.multispace[multispace_len] = NUL;
+ } else {
+ wp->w_p_lcs_chars.multispace = NULL;
+ }
- send_grid_resize = true;
+ xfree(wp->w_p_lcs_chars.leadmultispace);
+ if (lead_multispace_len > 0) {
+ wp->w_p_lcs_chars.leadmultispace
+ = xmalloc(((size_t)lead_multispace_len + 1) * sizeof(int));
+ wp->w_p_lcs_chars.leadmultispace[lead_multispace_len] = NUL;
+ } else {
+ wp->w_p_lcs_chars.leadmultispace = NULL;
+ }
+ }
+ }
+ const char_u *p = value;
+ while (*p) {
+ int i;
+ for (i = 0; i < entries; i++) {
+ const size_t len = STRLEN(tab[i].name);
+ if (STRNCMP(p, tab[i].name, len) == 0
+ && p[len] == ':'
+ && p[len + 1] != NUL) {
+ const char_u *s = p + len + 1;
+ int c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ int c2 = 0, c3 = 0;
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
+ if (*s == NUL) {
+ return e_invarg;
+ }
+ c2 = get_encoded_char_adv(&s);
+ if (c2 == 0 || char2cells(c2) > 1) {
+ return e_invarg;
+ }
+ if (!(*s == ',' || *s == NUL)) {
+ c3 = get_encoded_char_adv(&s);
+ if (c3 == 0 || char2cells(c3) > 1) {
+ return e_invarg;
+ }
+ }
+ }
+ if (*s == ',' || *s == NUL) {
+ if (round > 0) {
+ if (tab[i].cp == &wp->w_p_lcs_chars.tab2) {
+ wp->w_p_lcs_chars.tab1 = c1;
+ wp->w_p_lcs_chars.tab2 = c2;
+ wp->w_p_lcs_chars.tab3 = c3;
+ } else if (tab[i].cp != NULL) {
+ *(tab[i].cp) = c1;
+ }
+ }
+ p = s;
+ break;
+ }
+ }
+ }
- /// The window layout used to be adjusted here, but it now happens in
- /// screenalloc() (also invoked from screenclear()). That is because the
- /// recursize "resizing_screen" check above may skip this, but not screenalloc().
+ if (i == entries) {
+ const size_t len = STRLEN("multispace");
+ const size_t len2 = STRLEN("leadmultispace");
+ if (is_listchars
+ && STRNCMP(p, "multispace", len) == 0
+ && p[len] == ':'
+ && p[len + 1] != NUL) {
+ const char_u *s = p + len + 1;
+ if (round == 0) {
+ // Get length of lcs-multispace string in the first round
+ last_multispace = p;
+ multispace_len = 0;
+ while (*s != NUL && *s != ',') {
+ int c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ multispace_len++;
+ }
+ if (multispace_len == 0) {
+ // lcs-multispace cannot be an empty string
+ return e_invarg;
+ }
+ p = s;
+ } else {
+ int multispace_pos = 0;
+ while (*s != NUL && *s != ',') {
+ int c1 = get_encoded_char_adv(&s);
+ if (p == last_multispace) {
+ wp->w_p_lcs_chars.multispace[multispace_pos++] = c1;
+ }
+ }
+ p = s;
+ }
+ } else if (is_listchars
+ && STRNCMP(p, "leadmultispace", len2) == 0
+ && p[len2] == ':'
+ && p[len2 + 1] != NUL) {
+ const char_u *s = p + len2 + 1;
+ if (round == 0) {
+ // get length of lcs-leadmultispace string in first round
+ last_lmultispace = p;
+ lead_multispace_len = 0;
+ while (*s != NUL && *s != ',') {
+ int c1 = get_encoded_char_adv(&s);
+ if (c1 == 0 || char2cells(c1) > 1) {
+ return e_invarg;
+ }
+ lead_multispace_len++;
+ }
+ if (lead_multispace_len == 0) {
+ // lcs-leadmultispace cannot be an empty string
+ return e_invarg;
+ }
+ p = s;
+ } else {
+ int multispace_pos = 0;
+ while (*s != NUL && *s != ',') {
+ int c1 = get_encoded_char_adv(&s);
+ if (p == last_lmultispace) {
+ wp->w_p_lcs_chars.leadmultispace[multispace_pos++] = c1;
+ }
+ }
+ p = s;
+ }
+ } else {
+ return e_invarg;
+ }
+ }
- if (State != MODE_ASKMORE && State != MODE_EXTERNCMD && State != MODE_CONFIRM) {
- screenclear();
+ if (*p == ',') {
+ p++;
+ }
+ }
}
- if (starting != NO_SCREEN) {
- maketitle();
- changed_line_abv_curs();
- invalidate_botline();
-
- /*
- * We only redraw when it's needed:
- * - While at the more prompt or executing an external command, don't
- * redraw, but position the cursor.
- * - While editing the command line, only redraw that.
- * - in Ex mode, don't redraw anything.
- * - Otherwise, redraw right now, and position the cursor.
- * Always need to call update_screen() or screenalloc(), to make
- * sure Rows/Columns and the size of the screen is correct!
- */
- if (State == MODE_ASKMORE || State == MODE_EXTERNCMD || State == MODE_CONFIRM
- || exmode_active) {
- screenalloc();
- if (msg_grid.chars) {
- msg_grid_validate();
- }
- // TODO(bfredl): sometimes messes up the output. Implement clear+redraw
- // also for the pager? (or: what if the pager was just a modal window?)
- ui_comp_set_screen_valid(true);
- repeat_message();
- } else {
- if (curwin->w_p_scb) {
- do_check_scrollbind(true);
- }
- if (State & MODE_CMDLINE) {
- redraw_popupmenu = false;
- update_screen(NOT_VALID);
- redrawcmdline();
- if (pum_drawn()) {
- cmdline_pum_display(false);
- }
- } else {
- update_topline(curwin);
- if (pum_drawn()) {
- // TODO(bfredl): ins_compl_show_pum wants to redraw the screen first.
- // For now make sure the nested update_screen(0) won't redraw the
- // pum at the old position. Try to untangle this later.
- redraw_popupmenu = false;
- ins_compl_show_pum();
- }
- update_screen(NOT_VALID);
- if (redrawing()) {
- setcursor();
- }
- }
+ return NULL; // no error
+}
+
+/// Check all global and local values of 'listchars' and 'fillchars'.
+/// May set different defaults in case character widths change.
+///
+/// @return an untranslated error message if any of them is invalid, NULL otherwise.
+char *check_chars_options(void)
+{
+ if (set_chars_option(curwin, &p_lcs, false) != NULL) {
+ return e_conflicts_with_value_of_listchars;
+ }
+ if (set_chars_option(curwin, &p_fcs, false) != NULL) {
+ return e_conflicts_with_value_of_fillchars;
+ }
+ FOR_ALL_TAB_WINDOWS(tp, wp) {
+ if (set_chars_option(wp, &wp->w_p_lcs, true) != NULL) {
+ return e_conflicts_with_value_of_listchars;
+ }
+ if (set_chars_option(wp, &wp->w_p_fcs, true) != NULL) {
+ return e_conflicts_with_value_of_fillchars;
}
- ui_flush();
}
- resizing_screen = false;
+ return NULL;
}
/// Check if the new Nvim application "screen" dimensions are valid.
@@ -6890,13 +1555,3 @@ void check_screensize(void)
Columns = 10000;
}
}
-
-win_T *get_win_by_grid_handle(handle_T handle)
-{
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_grid_alloc.handle == handle) {
- return wp;
- }
- }
- return NULL;
-}
diff --git a/src/nvim/screen.h b/src/nvim/screen.h
index 9eda5223f1..ea1c58cd80 100644
--- a/src/nvim/screen.h
+++ b/src/nvim/screen.h
@@ -4,31 +4,10 @@
#include <stdbool.h>
#include "nvim/buffer_defs.h"
-#include "nvim/grid.h"
-#include "nvim/pos.h"
-#include "nvim/types.h"
+#include "nvim/fold.h"
+#include "nvim/grid_defs.h"
-// flags for update_screen()
-// The higher the value, the higher the priority
-#define VALID 10 // buffer not changed, or changes marked
- // with b_mod_*
-#define INVERTED 20 // redisplay inverted part that changed
-#define INVERTED_ALL 25 // redisplay whole inverted part
-#define REDRAW_TOP 30 // display first w_upd_rows screen lines
-#define SOME_VALID 35 // like NOT_VALID but may scroll
-#define NOT_VALID 40 // buffer needs complete redraw
-#define CLEAR 50 // screen messed up, clear it
-
-/// corner value flags for hsep_connected and vsep_connected
-typedef enum {
- WC_TOP_LEFT = 0,
- WC_TOP_RIGHT,
- WC_BOTTOM_LEFT,
- WC_BOTTOM_RIGHT,
-} WindowCorner;
-
-// Maximum columns for terminal highlight attributes
-#define TERM_ATTRS_MAX 1024
+EXTERN match_T screen_search_hl; // used for 'hlsearch' highlight matching
/// Array defining what should be done when tabline is clicked
EXTERN StlClickDefinition *tab_page_click_defs INIT(= NULL);
@@ -39,13 +18,6 @@ EXTERN long tab_page_click_defs_size INIT(= 0);
#define W_ENDCOL(wp) ((wp)->w_wincol + (wp)->w_width)
#define W_ENDROW(wp) ((wp)->w_winrow + (wp)->w_height)
-// While redrawing the screen this flag is set. It means the screen size
-// ('lines' and 'rows') must not be changed.
-EXTERN bool updating_screen INIT(= 0);
-
-// While resizing the screen this flag is set.
-EXTERN bool resizing_screen INIT(= 0);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "screen.h.generated.h"
#endif
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 403e2f3aa4..e995081df8 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -15,12 +15,14 @@
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/charset.h"
+#include "nvim/cmdhist.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
@@ -42,8 +44,8 @@
#include "nvim/os/input.h"
#include "nvim/os/time.h"
#include "nvim/path.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
@@ -168,7 +170,7 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc
}
if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
- mr_pattern = reverse_text(pat);
+ mr_pattern = (char_u *)reverse_text((char *)pat);
mr_pattern_alloced = true;
} else {
mr_pattern = pat;
@@ -218,7 +220,7 @@ void save_re_pat(int idx, char_u *pat, int magic)
last_idx = idx;
// If 'hlsearch' set and search pat changed: need redraw.
if (p_hls) {
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
set_no_hlsearch(false);
}
@@ -271,7 +273,7 @@ void free_search_patterns(void)
free_spat(&spats[0]);
free_spat(&spats[1]);
- memset(spats, 0, sizeof(spats));
+ CLEAR_FIELD(spats);
if (mr_pattern_alloced) {
xfree(mr_pattern);
@@ -352,10 +354,8 @@ char_u *last_search_pattern(void)
return spats[RE_SEARCH].pat;
}
-/*
- * Return TRUE when case should be ignored for search pattern "pat".
- * Uses the 'ignorecase' and 'smartcase' options.
- */
+/// Return true when case should be ignored for search pattern "pat".
+/// Uses the 'ignorecase' and 'smartcase' options.
int ignorecase(char_u *pat)
{
return ignorecase_opt(pat, p_ic, p_scs);
@@ -421,7 +421,7 @@ int last_csearch_forward(void)
int last_csearch_until(void)
{
- return last_t_cmd == TRUE;
+ return last_t_cmd == true;
}
void set_last_csearch(int c, char_u *s, int len)
@@ -431,7 +431,7 @@ void set_last_csearch(int c, char_u *s, int len)
if (len) {
memcpy(lastc_bytes, s, (size_t)len);
} else {
- memset(lastc_bytes, 0, sizeof(lastc_bytes));
+ CLEAR_FIELD(lastc_bytes);
}
}
@@ -478,8 +478,8 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
spats[idx].no_scs = false;
spats[idx].off.dir = '/';
set_vv_searchforward();
- spats[idx].off.line = FALSE;
- spats[idx].off.end = FALSE;
+ spats[idx].off.line = false;
+ spats[idx].off.end = false;
spats[idx].off.off = 0;
if (setlast) {
last_idx = idx;
@@ -496,7 +496,7 @@ void set_last_search_pat(const char_u *s, int idx, int magic, int setlast)
}
// If 'hlsearch' set and search pat changed: need redraw.
if (p_hls && idx == last_idx && !no_hlsearch) {
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
}
@@ -511,9 +511,9 @@ void last_pat_prog(regmmatch_T *regmatch)
regmatch->regprog = NULL;
return;
}
- ++emsg_off; // So it doesn't beep if bad expr
+ emsg_off++; // So it doesn't beep if bad expr
(void)search_regcomp((char_u *)"", 0, last_idx, SEARCH_KEEP, regmatch);
- --emsg_off;
+ emsg_off--;
}
/// Lowest level search function.
@@ -608,11 +608,11 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
start_pos = *pos; // remember start pos for detecting no match
found = 0; // default: not found
- at_first_line = TRUE; // default: start in first line
+ at_first_line = true; // default: start in first line
if (pos->lnum == 0) { // correct lnum for when starting in line 0
pos->lnum = 1;
pos->col = 0;
- at_first_line = FALSE; // not in first line now
+ at_first_line = false; // not in first line now
}
/*
@@ -625,14 +625,14 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
if (dir == BACKWARD && start_pos.col == 0
&& (options & SEARCH_START) == 0) {
lnum = pos->lnum - 1;
- at_first_line = FALSE;
+ at_first_line = false;
} else {
lnum = pos->lnum;
}
- for (loop = 0; loop <= 1; ++loop) { // loop twice if 'wrapscan' set
+ for (loop = 0; loop <= 1; loop++) { // loop twice if 'wrapscan' set
for (; lnum > 0 && lnum <= buf->b_ml.ml_line_count;
- lnum += dir, at_first_line = FALSE) {
+ lnum += dir, at_first_line = false) {
// Stop after checking "stop_lnum", if it's set.
if (stop_lnum != 0 && (dir == FORWARD
? lnum > stop_lnum : lnum < stop_lnum)) {
@@ -899,7 +899,7 @@ int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, Direction dir,
break; // if second loop, stop where started
}
}
- at_first_line = FALSE;
+ at_first_line = false;
// vim_regexec_multi() may clear "regprog"
if (regmatch.regprog == NULL) {
@@ -989,7 +989,7 @@ static int first_submatch(regmmatch_T *rp)
{
int submatch;
- for (submatch = 1;; ++submatch) {
+ for (submatch = 1;; submatch++) {
if (rp->startpos[submatch].lnum >= 0) {
break;
}
@@ -1004,22 +1004,22 @@ static int first_submatch(regmmatch_T *rp)
/// Highest level string search function.
/// Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc'
///
-/// Careful: If spats[0].off.line == TRUE and spats[0].off.off == 0 this
+/// Careful: If spats[0].off.line == true and spats[0].off.off == 0 this
/// makes the movement linewise without moving the match position.
///
/// @param dirc if 0: use previous dir.
/// @param pat NULL or empty : use previous string.
-/// @param options if TRUE and
-/// SEARCH_REV == TRUE : go in reverse of previous dir.
-/// SEARCH_ECHO == TRUE : echo the search command and handle options
-/// SEARCH_MSG == TRUE : may give error message
-/// SEARCH_OPT == TRUE : interpret optional flags
-/// SEARCH_HIS == TRUE : put search pattern in history
-/// SEARCH_NOOF == TRUE : don't add offset to position
-/// SEARCH_MARK == TRUE : set previous context mark
-/// SEARCH_KEEP == TRUE : keep previous search pattern
-/// SEARCH_START == TRUE : accept match at curpos itself
-/// SEARCH_PEEK == TRUE : check for typed char, cancel search
+/// @param options if true and
+/// SEARCH_REV == true : go in reverse of previous dir.
+/// SEARCH_ECHO == true : echo the search command and handle options
+/// SEARCH_MSG == true : may give error message
+/// SEARCH_OPT == true : interpret optional flags
+/// SEARCH_HIS == true : put search pattern in history
+/// SEARCH_NOOF == true : don't add offset to position
+/// SEARCH_MARK == true : set previous context mark
+/// SEARCH_KEEP == true : keep previous search pattern
+/// SEARCH_START == true : accept match at curpos itself
+/// SEARCH_PEEK == true : check for typed char, cancel search
/// @param oap can be NULL
/// @param dirc '/' or '?'
/// @param search_delim delimiter for search, e.g. '%' in s%regex%replacement
@@ -1036,7 +1036,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
char_u *p;
long c;
char_u *dircp;
- char_u *strcopy = NULL;
+ char *strcopy = NULL;
char_u *ps;
char_u *msgbuf = NULL;
size_t len;
@@ -1091,7 +1091,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
* Turn 'hlsearch' highlighting back on.
*/
if (no_hlsearch && !(options & SEARCH_KEEP)) {
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
set_no_hlsearch(false);
}
@@ -1123,20 +1123,20 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
* Find end of regular expression.
* If there is a matching '/' or '?', toss it.
*/
- ps = strcopy;
- p = skip_regexp(pat, search_delim, p_magic, &strcopy);
- if (strcopy != ps) {
+ ps = (char_u *)strcopy;
+ p = (char_u *)skip_regexp((char *)pat, search_delim, p_magic, &strcopy);
+ if (strcopy != (char *)ps) {
// made a copy of "pat" to change "\?" to "?"
searchcmdlen += (int)(STRLEN(pat) - STRLEN(strcopy));
- pat = strcopy;
- searchstr = strcopy;
+ pat = (char_u *)strcopy;
+ searchstr = (char_u *)strcopy;
}
if (*p == search_delim) {
dircp = p; // remember where we put the NUL
*p++ = NUL;
}
- spats[0].off.line = FALSE;
- spats[0].off.end = FALSE;
+ spats[0].off.line = false;
+ spats[0].off.end = false;
spats[0].off.off = 0;
// Check for a line offset or a character offset.
// For get_address (echo off) we don't check for a character
@@ -1160,9 +1160,9 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
} else { // single '+'
spats[0].off.off = 1;
}
- ++p;
+ p++;
while (ascii_isdigit(*p)) { // skip number
- ++p;
+ p++;
}
}
@@ -1261,7 +1261,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
// it would be blanked out again very soon. Show it on the
// left, but do reverse the text.
if (curwin->w_p_rl && *curwin->w_p_rlc == 's') {
- char_u *r = reverse_text(trunc != NULL ? trunc : msgbuf);
+ char_u *r = (char_u *)reverse_text(trunc != NULL ? (char *)trunc : (char *)msgbuf);
xfree(msgbuf);
msgbuf = r;
// move reversed text to beginning of buffer
@@ -1296,7 +1296,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
*/
if (!spats[0].off.line && spats[0].off.off && pos.col < MAXCOL - 2) {
if (spats[0].off.off > 0) {
- for (c = spats[0].off.off; c; --c) {
+ for (c = spats[0].off.off; c; c--) {
if (decl(&pos) == -1) {
break;
}
@@ -1306,7 +1306,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
pos.col = MAXCOL;
}
} else {
- for (c = spats[0].off.off; c; ++c) {
+ for (c = spats[0].off.off; c; c++) {
if (incl(&pos) == -1) {
break;
}
@@ -1426,14 +1426,14 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count,
emsg(_("E386: Expected '?' or '/' after ';'"));
goto end_do_search;
}
- ++pat;
+ pat++;
}
if (options & SEARCH_MARK) {
setpcmark();
}
curwin->w_cursor = pos;
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
end_do_search:
if ((options & SEARCH_KEEP) || (cmdmod.cmod_flags & CMOD_KEEPPATTERNS)) {
@@ -1497,16 +1497,15 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
// when adding lines the matching line may be empty but it is not
// ignored because we are interested in the next line -- Acevedo
- if ((compl_cont_status & CONT_ADDING)
- && !(compl_cont_status & CONT_SOL)) {
+ if (compl_status_adding() && !compl_status_sol()) {
if (mb_strcmp_ic((bool)p_ic, (const char *)p, (const char *)pat) == 0) {
return OK;
}
} else if (*p != NUL) { // Ignore empty lines.
// Expanding lines or words.
- assert(compl_length >= 0);
- if ((p_ic ? mb_strnicmp(p, pat, (size_t)compl_length)
- : STRNCMP(p, pat, compl_length)) == 0) {
+ assert(ins_compl_len() >= 0);
+ if ((p_ic ? mb_strnicmp(p, pat, (size_t)ins_compl_len())
+ : STRNCMP(p, pat, ins_compl_len())) == 0) {
return OK;
}
}
@@ -1518,12 +1517,10 @@ int search_for_exact_line(buf_T *buf, pos_T *pos, Direction dir, char_u *pat)
* Character Searches
*/
-/*
- * Search for a character in a line. If "t_cmd" is FALSE, move to the
- * position of the character, otherwise move to just before the char.
- * Do this "cap->count1" times.
- * Return FAIL or OK.
- */
+/// Search for a character in a line. If "t_cmd" is false, move to the
+/// position of the character, otherwise move to just before the char.
+/// Do this "cap->count1" times.
+/// Return FAIL or OK.
int searchc(cmdarg_T *cap, int t_cmd)
FUNC_ATTR_NONNULL_ALL
{
@@ -1695,34 +1692,34 @@ static bool find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos)
static void find_mps_values(int *initc, int *findc, bool *backwards, bool switchit)
FUNC_ATTR_NONNULL_ALL
{
- char_u *ptr = curbuf->b_p_mps;
+ char *ptr = curbuf->b_p_mps;
while (*ptr != NUL) {
- if (utf_ptr2char((char *)ptr) == *initc) {
+ if (utf_ptr2char(ptr) == *initc) {
if (switchit) {
*findc = *initc;
- *initc = utf_ptr2char((char *)ptr + utfc_ptr2len((char *)ptr) + 1);
+ *initc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1);
*backwards = true;
} else {
- *findc = utf_ptr2char((char *)ptr + utfc_ptr2len((char *)ptr) + 1);
+ *findc = utf_ptr2char(ptr + utfc_ptr2len(ptr) + 1);
*backwards = false;
}
return;
}
- char_u *prev = ptr;
- ptr += utfc_ptr2len((char *)ptr) + 1;
- if (utf_ptr2char((char *)ptr) == *initc) {
+ char *prev = ptr;
+ ptr += utfc_ptr2len(ptr) + 1;
+ if (utf_ptr2char(ptr) == *initc) {
if (switchit) {
*findc = *initc;
- *initc = utf_ptr2char((char *)prev);
+ *initc = utf_ptr2char(prev);
*backwards = false;
} else {
- *findc = utf_ptr2char((char *)prev);
+ *findc = utf_ptr2char(prev);
*backwards = true;
}
return;
}
- ptr += utfc_ptr2len((char *)ptr);
+ ptr += utfc_ptr2len(ptr);
if (*ptr == ',') {
ptr++;
}
@@ -1866,7 +1863,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
* the line.
*/
if (linep[pos.col] == NUL && pos.col) {
- --pos.col;
+ pos.col--;
}
for (;;) {
initc = utf_ptr2char((char *)linep + pos.col);
@@ -1980,7 +1977,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
// backward search: Check if this line contains a single-line comment
if ((backwards && comment_dir) || lisp) {
- comment_col = check_linecomment(linep);
+ comment_col = check_linecomment((char *)linep);
}
if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) {
lispcomm = true; // find match inside this comment
@@ -2000,7 +1997,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (pos.lnum == 1) { // start of file
break;
}
- --pos.lnum;
+ pos.lnum--;
if (maxtravel > 0 && ++traveled > maxtravel) {
break;
@@ -2013,7 +2010,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
// Check if this line contains a single-line comment
if (comment_dir || lisp) {
- comment_col = check_linecomment(linep);
+ comment_col = check_linecomment((char *)linep);
}
// skip comment
if (lisp && comment_col != MAXCOL) {
@@ -2035,7 +2032,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
|| lispcomm) {
break;
}
- ++pos.lnum;
+ pos.lnum++;
if (maxtravel && traveled++ > maxtravel) {
break;
@@ -2046,7 +2043,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
do_quotes = -1;
line_breakcheck();
if (lisp) { // find comment pos in new line
- comment_col = check_linecomment(linep);
+ comment_col = check_linecomment((char *)linep);
}
} else {
pos.col += utfc_ptr2len((char *)linep + pos.col);
@@ -2130,16 +2127,16 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
* Watch out for "\\".
*/
at_start = do_quotes;
- for (ptr = linep; *ptr; ++ptr) {
+ for (ptr = linep; *ptr; ptr++) {
if (ptr == linep + pos.col + backwards) {
at_start = (do_quotes & 1);
}
if (*ptr == '"'
&& (ptr == linep || ptr[-1] != '\'' || ptr[1] != '\'')) {
- ++do_quotes;
+ do_quotes++;
}
if (*ptr == '\\' && ptr[1] != NUL) {
- ++ptr;
+ ptr++;
}
}
do_quotes &= 1; // result is 1 with even number of quotes
@@ -2210,7 +2207,7 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
if (do_quotes) {
int col;
- for (col = pos.col - 1; col >= 0; --col) {
+ for (col = pos.col - 1; col >= 0; col--) {
if (linep[col] != '\\') {
break;
}
@@ -2304,15 +2301,15 @@ pos_T *findmatchlimit(oparg_T *oap, int initc, int flags, int64_t maxtravel)
/// Check if line[] contains a / / comment.
/// @returns MAXCOL if not, otherwise return the column.
-int check_linecomment(const char_u *line)
+int check_linecomment(const char *line)
{
- const char_u *p = line; // scan from start
+ const char *p = line; // scan from start
// skip Lispish one-line comments
if (curbuf->b_p_lisp) {
if (vim_strchr((char *)p, ';') != NULL) { // there may be comments
bool in_str = false; // inside of string
- while ((p = (char_u *)strpbrk((char *)p, "\";")) != NULL) {
+ while ((p = strpbrk((char *)p, "\";")) != NULL) {
if (*p == '"') {
if (in_str) {
if (*(p - 1) != '\\') { // skip escaped quote
@@ -2325,7 +2322,7 @@ int check_linecomment(const char_u *line)
}
} else if (!in_str && ((p - line) < 2
|| (*(p - 1) != '\\' && *(p - 2) != '#'))
- && !is_pos_in_string(line, (colnr_T)(p - line))) {
+ && !is_pos_in_string((char_u *)line, (colnr_T)(p - line))) {
break; // found!
}
p++;
@@ -2334,15 +2331,15 @@ int check_linecomment(const char_u *line)
p = NULL;
}
} else {
- while ((p = (char_u *)vim_strchr((char *)p, '/')) != NULL) {
+ while ((p = vim_strchr((char *)p, '/')) != NULL) {
// Accept a double /, unless it's preceded with * and followed by *,
// because * / / * is an end and start of a C comment. Only
// accept the position if it is not inside a string.
if (p[1] == '/' && (p == line || p[-1] != '*' || p[2] != '*')
- && !is_pos_in_string(line, (colnr_T)(p - line))) {
+ && !is_pos_in_string((char_u *)line, (colnr_T)(p - line))) {
break;
}
- ++p;
+ p++;
}
}
@@ -2375,7 +2372,7 @@ void showmatch(int c)
* Only show match for chars in the 'matchpairs' option.
*/
// 'matchpairs' is "x:y,x:y"
- for (p = curbuf->b_p_mps; *p != NUL; p++) {
+ for (p = (char_u *)curbuf->b_p_mps; *p != NUL; p++) {
if (utf_ptr2char((char *)p) == c && (curwin->w_p_rl ^ p_ri)) {
break;
}
@@ -2412,7 +2409,7 @@ void showmatch(int c)
dollar_vcol = -1;
}
curwin->w_virtcol++; // do display ')' just before "$"
- update_screen(VALID); // show the new char first
+ update_screen(UPD_VALID); // show the new char first
save_dollar_vcol = dollar_vcol;
save_state = State;
@@ -2421,7 +2418,7 @@ void showmatch(int c)
curwin->w_cursor = mpos; // move to matching char
*so = 0; // don't use 'scrolloff' here
*siso = 0; // don't use 'sidescrolloff' here
- showruler(false);
+ show_cursor_info(false);
setcursor();
ui_flush();
// Restore dollar_vcol(), because setcursor() may call curs_rows()
@@ -2447,1816 +2444,6 @@ void showmatch(int c)
}
}
-// Find the start of the next sentence, searching in the direction specified
-// by the "dir" argument. The cursor is positioned on the start of the next
-// sentence when found. If the next sentence is found, return OK. Return FAIL
-// otherwise. See ":h sentence" for the precise definition of a "sentence"
-// text object.
-int findsent(Direction dir, long count)
-{
- pos_T pos, tpos;
- int c;
- int (*func)(pos_T *);
- bool noskip = false; // do not skip blanks
-
- pos = curwin->w_cursor;
- if (dir == FORWARD) {
- func = incl;
- } else {
- func = decl;
- }
-
- while (count--) {
- const pos_T prev_pos = pos;
-
- // if on an empty line, skip up to a non-empty line
- if (gchar_pos(&pos) == NUL) {
- do {
- if ((*func)(&pos) == -1) {
- break;
- }
- } while (gchar_pos(&pos) == NUL);
- if (dir == FORWARD) {
- goto found;
- }
- // if on the start of a paragraph or a section and searching forward,
- // go to the next line
- } else if (dir == FORWARD && pos.col == 0
- && startPS(pos.lnum, NUL, false)) {
- if (pos.lnum == curbuf->b_ml.ml_line_count) {
- return FAIL;
- }
- pos.lnum++;
- goto found;
- } else if (dir == BACKWARD) {
- decl(&pos);
- }
-
- // go back to the previous non-white non-punctuation character
- bool found_dot = false;
- while (c = gchar_pos(&pos), ascii_iswhite(c)
- || vim_strchr(".!?)]\"'", c) != NULL) {
- tpos = pos;
- if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) {
- break;
- }
- if (found_dot) {
- break;
- }
- if (vim_strchr(".!?", c) != NULL) {
- found_dot = true;
- }
- if (vim_strchr(")]\"'", c) != NULL
- && vim_strchr(".!?)]\"'", gchar_pos(&tpos)) == NULL) {
- break;
- }
- decl(&pos);
- }
-
- // remember the line where the search started
- const int startlnum = pos.lnum;
- const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
-
- for (;;) { // find end of sentence
- c = gchar_pos(&pos);
- if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, FALSE))) {
- if (dir == BACKWARD && pos.lnum != startlnum) {
- ++pos.lnum;
- }
- break;
- }
- if (c == '.' || c == '!' || c == '?') {
- tpos = pos;
- do {
- if ((c = inc(&tpos)) == -1) {
- break;
- }
- } while (vim_strchr(")]\"'", c = gchar_pos(&tpos))
- != NULL);
- if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
- || (cpo_J && (c == ' ' && inc(&tpos) >= 0
- && gchar_pos(&tpos) == ' '))) {
- pos = tpos;
- if (gchar_pos(&pos) == NUL) { // skip NUL at EOL
- inc(&pos);
- }
- break;
- }
- }
- if ((*func)(&pos) == -1) {
- if (count) {
- return FAIL;
- }
- noskip = true;
- break;
- }
- }
-found:
- // skip white space
- while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) {
- if (incl(&pos) == -1) {
- break;
- }
- }
-
- if (equalpos(prev_pos, pos)) {
- // didn't actually move, advance one character and try again
- if ((*func)(&pos) == -1) {
- if (count) {
- return FAIL;
- }
- break;
- }
- count++;
- }
- }
-
- setpcmark();
- curwin->w_cursor = pos;
- return OK;
-}
-
-/// Find the next paragraph or section in direction 'dir'.
-/// Paragraphs are currently supposed to be separated by empty lines.
-/// If 'what' is NUL we go to the next paragraph.
-/// If 'what' is '{' or '}' we go to the next section.
-/// If 'both' is TRUE also stop at '}'.
-///
-/// @param pincl Return: true if last char is to be included
-///
-/// @return TRUE if the next paragraph or section was found.
-bool findpar(bool *pincl, int dir, long count, int what, int both)
-{
- linenr_T curr;
- bool did_skip; // true after separating lines have been skipped
- bool first; // true on first line
- linenr_T fold_first; // first line of a closed fold
- linenr_T fold_last; // last line of a closed fold
- bool fold_skipped; // true if a closed fold was skipped this
- // iteration
-
- curr = curwin->w_cursor.lnum;
-
- while (count--) {
- did_skip = false;
- for (first = true;; first = false) {
- if (*ml_get(curr) != NUL) {
- did_skip = true;
- }
-
- // skip folded lines
- fold_skipped = false;
- if (first && hasFolding(curr, &fold_first, &fold_last)) {
- curr = ((dir > 0) ? fold_last : fold_first) + dir;
- fold_skipped = true;
- }
-
- if (!first && did_skip && startPS(curr, what, both)) {
- break;
- }
-
- if (fold_skipped) {
- curr -= dir;
- }
- if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) {
- if (count) {
- return false;
- }
- curr -= dir;
- break;
- }
- }
- }
- setpcmark();
- if (both && *ml_get(curr) == '}') { // include line with '}'
- ++curr;
- }
- curwin->w_cursor.lnum = curr;
- if (curr == curbuf->b_ml.ml_line_count && what != '}') {
- char_u *line = ml_get(curr);
-
- // Put the cursor on the last character in the last line and make the
- // motion inclusive.
- if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) {
- curwin->w_cursor.col--;
- curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col);
- *pincl = true;
- }
- } else {
- curwin->w_cursor.col = 0;
- }
- return true;
-}
-
-/*
- * check if the string 's' is a nroff macro that is in option 'opt'
- */
-static int inmacro(char_u *opt, char_u *s)
-{
- char_u *macro;
-
- for (macro = opt; macro[0]; macro++) {
- // Accept two characters in the option being equal to two characters
- // in the line. A space in the option matches with a space in the
- // line or the line having ended.
- if ((macro[0] == s[0]
- || (macro[0] == ' '
- && (s[0] == NUL || s[0] == ' ')))
- && (macro[1] == s[1]
- || ((macro[1] == NUL || macro[1] == ' ')
- && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) {
- break;
- }
- ++macro;
- if (macro[0] == NUL) {
- break;
- }
- }
- return macro[0] != NUL;
-}
-
-/*
- * startPS: return TRUE if line 'lnum' is the start of a section or paragraph.
- * If 'para' is '{' or '}' only check for sections.
- * If 'both' is TRUE also stop at '}'
- */
-int startPS(linenr_T lnum, int para, int both)
-{
- char_u *s;
-
- s = ml_get(lnum);
- if (*s == para || *s == '\f' || (both && *s == '}')) {
- return true;
- }
- if (*s == '.' && (inmacro(p_sections, s + 1)
- || (!para && inmacro(p_para, s + 1)))) {
- return true;
- }
- return false;
-}
-
-/*
- * The following routines do the word searches performed by the 'w', 'W',
- * 'b', 'B', 'e', and 'E' commands.
- */
-
-/*
- * To perform these searches, characters are placed into one of three
- * classes, and transitions between classes determine word boundaries.
- *
- * The classes are:
- *
- * 0 - white space
- * 1 - punctuation
- * 2 or higher - keyword characters (letters, digits and underscore)
- */
-
-static int cls_bigword; // TRUE for "W", "B" or "E"
-
-/*
- * cls() - returns the class of character at curwin->w_cursor
- *
- * If a 'W', 'B', or 'E' motion is being done (cls_bigword == TRUE), chars
- * from class 2 and higher are reported as class 1 since only white space
- * boundaries are of interest.
- */
-static int cls(void)
-{
- int c;
-
- c = gchar_cursor();
- if (c == ' ' || c == '\t' || c == NUL) {
- return 0;
- }
-
- c = utf_class(c);
-
- // If cls_bigword is TRUE, report all non-blanks as class 1.
- if (c != 0 && cls_bigword) {
- return 1;
- }
- return c;
-}
-
-/// fwd_word(count, type, eol) - move forward one word
-///
-/// @return FAIL if the cursor was already at the end of the file.
-/// If eol is TRUE, last word stops at end of line (for operators).
-///
-/// @param bigword "W", "E" or "B"
-int fwd_word(long count, int bigword, int eol)
-{
- int sclass; // starting class
- int i;
- int last_line;
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- // When inside a range of folded lines, move to the last char of the
- // last line.
- if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
- coladvance(MAXCOL);
- }
- sclass = cls();
-
- /*
- * We always move at least one character, unless on the last
- * character in the buffer.
- */
- last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
- i = inc_cursor();
- if (i == -1 || (i >= 1 && last_line)) { // started at last char in file
- return FAIL;
- }
- if (i >= 1 && eol && count == 0) { // started at last char in line
- return OK;
- }
-
- /*
- * Go one char past end of current word (if any)
- */
- if (sclass != 0) {
- while (cls() == sclass) {
- i = inc_cursor();
- if (i == -1 || (i >= 1 && eol && count == 0)) {
- return OK;
- }
- }
- }
-
- /*
- * go to next non-white
- */
- while (cls() == 0) {
- /*
- * We'll stop if we land on a blank line
- */
- if (curwin->w_cursor.col == 0 && *get_cursor_line_ptr() == NUL) {
- break;
- }
-
- i = inc_cursor();
- if (i == -1 || (i >= 1 && eol && count == 0)) {
- return OK;
- }
- }
- }
- return OK;
-}
-
-/*
- * bck_word() - move backward 'count' words
- *
- * If stop is TRUE and we are already on the start of a word, move one less.
- *
- * Returns FAIL if top of the file was reached.
- */
-int bck_word(long count, int bigword, int stop)
-{
- int sclass; // starting class
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- /* When inside a range of folded lines, move to the first char of the
- * first line. */
- if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) {
- curwin->w_cursor.col = 0;
- }
- sclass = cls();
- if (dec_cursor() == -1) { // started at start of file
- return FAIL;
- }
-
- if (!stop || sclass == cls() || sclass == 0) {
- /*
- * Skip white space before the word.
- * Stop on an empty line.
- */
- while (cls() == 0) {
- if (curwin->w_cursor.col == 0
- && LINEEMPTY(curwin->w_cursor.lnum)) {
- goto finished;
- }
- if (dec_cursor() == -1) { // hit start of file, stop here
- return OK;
- }
- }
-
- /*
- * Move backward to start of this word.
- */
- if (skip_chars(cls(), BACKWARD)) {
- return OK;
- }
- }
-
- inc_cursor(); // overshot - forward one
-finished:
- stop = FALSE;
- }
- return OK;
-}
-
-/*
- * end_word() - move to the end of the word
- *
- * There is an apparent bug in the 'e' motion of the real vi. At least on the
- * System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
- * motion crosses blank lines. When the real vi crosses a blank line in an
- * 'e' motion, the cursor is placed on the FIRST character of the next
- * non-blank line. The 'E' command, however, works correctly. Since this
- * appears to be a bug, I have not duplicated it here.
- *
- * Returns FAIL if end of the file was reached.
- *
- * If stop is TRUE and we are already on the end of a word, move one less.
- * If empty is TRUE stop on an empty line.
- */
-int end_word(long count, int bigword, int stop, int empty)
-{
- int sclass; // starting class
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- /* When inside a range of folded lines, move to the last char of the
- * last line. */
- if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
- coladvance(MAXCOL);
- }
- sclass = cls();
- if (inc_cursor() == -1) {
- return FAIL;
- }
-
- /*
- * If we're in the middle of a word, we just have to move to the end
- * of it.
- */
- if (cls() == sclass && sclass != 0) {
- /*
- * Move forward to end of the current word
- */
- if (skip_chars(sclass, FORWARD)) {
- return FAIL;
- }
- } else if (!stop || sclass == 0) {
- /*
- * We were at the end of a word. Go to the end of the next word.
- * First skip white space, if 'empty' is TRUE, stop at empty line.
- */
- while (cls() == 0) {
- if (empty && curwin->w_cursor.col == 0
- && LINEEMPTY(curwin->w_cursor.lnum)) {
- goto finished;
- }
- if (inc_cursor() == -1) { // hit end of file, stop here
- return FAIL;
- }
- }
-
- /*
- * Move forward to the end of this word.
- */
- if (skip_chars(cls(), FORWARD)) {
- return FAIL;
- }
- }
- dec_cursor(); // overshot - one char backward
-finished:
- stop = FALSE; // we move only one word less
- }
- return OK;
-}
-
-/// Move back to the end of the word.
-///
-/// @param bigword TRUE for "B"
-/// @param eol if true, then stop at end of line.
-///
-/// @return FAIL if start of the file was reached.
-int bckend_word(long count, int bigword, bool eol)
-{
- int sclass; // starting class
- int i;
-
- curwin->w_cursor.coladd = 0;
- cls_bigword = bigword;
- while (--count >= 0) {
- sclass = cls();
- if ((i = dec_cursor()) == -1) {
- return FAIL;
- }
- if (eol && i == 1) {
- return OK;
- }
-
- /*
- * Move backward to before the start of this word.
- */
- if (sclass != 0) {
- while (cls() == sclass) {
- if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
- return OK;
- }
- }
- }
-
- /*
- * Move backward to end of the previous word
- */
- while (cls() == 0) {
- if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) {
- break;
- }
- if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
- return OK;
- }
- }
- }
- return OK;
-}
-
-/// Skip a row of characters of the same class.
-///
-/// @return true when end-of-file reached, false otherwise.
-static bool skip_chars(int cclass, int dir)
-{
- while (cls() == cclass) {
- if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) {
- return true;
- }
- }
- return false;
-}
-
-/*
- * Go back to the start of the word or the start of white space
- */
-static void back_in_line(void)
-{
- int sclass; // starting class
-
- sclass = cls();
- for (;;) {
- if (curwin->w_cursor.col == 0) { // stop at start of line
- break;
- }
- dec_cursor();
- if (cls() != sclass) { // stop at start of word
- inc_cursor();
- break;
- }
- }
-}
-
-static void find_first_blank(pos_T *posp)
-{
- int c;
-
- while (decl(posp) != -1) {
- c = gchar_pos(posp);
- if (!ascii_iswhite(c)) {
- incl(posp);
- break;
- }
- }
-}
-
-/// Skip count/2 sentences and count/2 separating white spaces.
-///
-/// @param at_start_sent cursor is at start of sentence
-static void findsent_forward(long count, bool at_start_sent)
-{
- while (count--) {
- findsent(FORWARD, 1L);
- if (at_start_sent) {
- find_first_blank(&curwin->w_cursor);
- }
- if (count == 0 || at_start_sent) {
- decl(&curwin->w_cursor);
- }
- at_start_sent = !at_start_sent;
- }
-}
-
-/// Find word under cursor, cursor at end.
-/// Used while an operator is pending, and in Visual mode.
-///
-/// @param include TRUE: include word and white space
-/// @param bigword FALSE == word, TRUE == WORD
-int current_word(oparg_T *oap, long count, int include, int bigword)
-{
- pos_T start_pos;
- pos_T pos;
- bool inclusive = true;
- int include_white = FALSE;
-
- cls_bigword = bigword;
- clearpos(&start_pos);
-
- // Correct cursor when 'selection' is exclusive
- if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) {
- dec_cursor();
- }
-
- /*
- * When Visual mode is not active, or when the VIsual area is only one
- * character, select the word and/or white space under the cursor.
- */
- if (!VIsual_active || equalpos(curwin->w_cursor, VIsual)) {
- /*
- * Go to start of current word or white space.
- */
- back_in_line();
- start_pos = curwin->w_cursor;
-
- /*
- * If the start is on white space, and white space should be included
- * (" word"), or start is not on white space, and white space should
- * not be included ("word"), find end of word.
- */
- if ((cls() == 0) == include) {
- if (end_word(1L, bigword, TRUE, TRUE) == FAIL) {
- return FAIL;
- }
- } else {
- /*
- * If the start is not on white space, and white space should be
- * included ("word "), or start is on white space and white
- * space should not be included (" "), find start of word.
- * If we end up in the first column of the next line (single char
- * word) back up to end of the line.
- */
- fwd_word(1L, bigword, TRUE);
- if (curwin->w_cursor.col == 0) {
- decl(&curwin->w_cursor);
- } else {
- oneleft();
- }
-
- if (include) {
- include_white = TRUE;
- }
- }
-
- if (VIsual_active) {
- // should do something when inclusive == false !
- VIsual = start_pos;
- redraw_curbuf_later(INVERTED); // update the inversion
- } else {
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- }
- --count;
- }
-
- /*
- * When count is still > 0, extend with more objects.
- */
- while (count > 0) {
- inclusive = true;
- if (VIsual_active && lt(curwin->w_cursor, VIsual)) {
- /*
- * In Visual mode, with cursor at start: move cursor back.
- */
- if (decl(&curwin->w_cursor) == -1) {
- return FAIL;
- }
- if (include != (cls() != 0)) {
- if (bck_word(1L, bigword, TRUE) == FAIL) {
- return FAIL;
- }
- } else {
- if (bckend_word(1L, bigword, true) == FAIL) {
- return FAIL;
- }
- (void)incl(&curwin->w_cursor);
- }
- } else {
- /*
- * Move cursor forward one word and/or white area.
- */
- if (incl(&curwin->w_cursor) == -1) {
- return FAIL;
- }
- if (include != (cls() == 0)) {
- if (fwd_word(1L, bigword, TRUE) == FAIL && count > 1) {
- return FAIL;
- }
- /*
- * If end is just past a new-line, we don't want to include
- * the first character on the line.
- * Put cursor on last char of white.
- */
- if (oneleft() == FAIL) {
- inclusive = false;
- }
- } else {
- if (end_word(1L, bigword, TRUE, TRUE) == FAIL) {
- return FAIL;
- }
- }
- }
- --count;
- }
-
- if (include_white && (cls() != 0
- || (curwin->w_cursor.col == 0 && !inclusive))) {
- /*
- * If we don't include white space at the end, move the start
- * to include some white space there. This makes "daw" work
- * better on the last word in a sentence (and "2daw" on last-but-one
- * word). Also when "2daw" deletes "word." at the end of the line
- * (cursor is at start of next line).
- * But don't delete white space at start of line (indent).
- */
- pos = curwin->w_cursor; // save cursor position
- curwin->w_cursor = start_pos;
- if (oneleft() == OK) {
- back_in_line();
- if (cls() == 0 && curwin->w_cursor.col > 0) {
- if (VIsual_active) {
- VIsual = curwin->w_cursor;
- } else {
- oap->start = curwin->w_cursor;
- }
- }
- }
- curwin->w_cursor = pos; // put cursor back at end
- }
-
- if (VIsual_active) {
- if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) {
- inc_cursor();
- }
- if (VIsual_mode == 'V') {
- VIsual_mode = 'v';
- redraw_cmdline = true; // show mode later
- }
- } else {
- oap->inclusive = inclusive;
- }
-
- return OK;
-}
-
-/*
- * Find sentence(s) under the cursor, cursor at end.
- * When Visual active, extend it by one or more sentences.
- */
-int current_sent(oparg_T *oap, long count, int include)
-{
- pos_T start_pos;
- pos_T pos;
- bool start_blank;
- int c;
- bool at_start_sent;
- long ncount;
-
- start_pos = curwin->w_cursor;
- pos = start_pos;
- findsent(FORWARD, 1L); // Find start of next sentence.
-
- /*
- * When the Visual area is bigger than one character: Extend it.
- */
- if (VIsual_active && !equalpos(start_pos, VIsual)) {
-extend:
- if (lt(start_pos, VIsual)) {
- /*
- * Cursor at start of Visual area.
- * Find out where we are:
- * - in the white space before a sentence
- * - in a sentence or just after it
- * - at the start of a sentence
- */
- at_start_sent = true;
- decl(&pos);
- while (lt(pos, curwin->w_cursor)) {
- c = gchar_pos(&pos);
- if (!ascii_iswhite(c)) {
- at_start_sent = false;
- break;
- }
- incl(&pos);
- }
- if (!at_start_sent) {
- findsent(BACKWARD, 1L);
- if (equalpos(curwin->w_cursor, start_pos)) {
- at_start_sent = true; // exactly at start of sentence
- } else {
- // inside a sentence, go to its end (start of next)
- findsent(FORWARD, 1L);
- }
- }
- if (include) { // "as" gets twice as much as "is"
- count *= 2;
- }
- while (count--) {
- if (at_start_sent) {
- find_first_blank(&curwin->w_cursor);
- }
- c = gchar_cursor();
- if (!at_start_sent || (!include && !ascii_iswhite(c))) {
- findsent(BACKWARD, 1L);
- }
- at_start_sent = !at_start_sent;
- }
- } else {
- /*
- * Cursor at end of Visual area.
- * Find out where we are:
- * - just before a sentence
- * - just before or in the white space before a sentence
- * - in a sentence
- */
- incl(&pos);
- at_start_sent = true;
- if (!equalpos(pos, curwin->w_cursor)) { // not just before a sentence
- at_start_sent = false;
- while (lt(pos, curwin->w_cursor)) {
- c = gchar_pos(&pos);
- if (!ascii_iswhite(c)) {
- at_start_sent = true;
- break;
- }
- incl(&pos);
- }
- if (at_start_sent) { // in the sentence
- findsent(BACKWARD, 1L);
- } else { // in/before white before a sentence
- curwin->w_cursor = start_pos;
- }
- }
-
- if (include) { // "as" gets twice as much as "is"
- count *= 2;
- }
- findsent_forward(count, at_start_sent);
- if (*p_sel == 'e') {
- ++curwin->w_cursor.col;
- }
- }
- return OK;
- }
-
- /*
- * If the cursor started on a blank, check if it is just before the start
- * of the next sentence.
- */
- while (c = gchar_pos(&pos), ascii_iswhite(c)) {
- incl(&pos);
- }
- if (equalpos(pos, curwin->w_cursor)) {
- start_blank = true;
- find_first_blank(&start_pos); // go back to first blank
- } else {
- start_blank = false;
- findsent(BACKWARD, 1L);
- start_pos = curwin->w_cursor;
- }
- if (include) {
- ncount = count * 2;
- } else {
- ncount = count;
- if (start_blank) {
- --ncount;
- }
- }
- if (ncount > 0) {
- findsent_forward(ncount, true);
- } else {
- decl(&curwin->w_cursor);
- }
-
- if (include) {
- /*
- * If the blank in front of the sentence is included, exclude the
- * blanks at the end of the sentence, go back to the first blank.
- * If there are no trailing blanks, try to include leading blanks.
- */
- if (start_blank) {
- find_first_blank(&curwin->w_cursor);
- c = gchar_pos(&curwin->w_cursor);
- if (ascii_iswhite(c)) {
- decl(&curwin->w_cursor);
- }
- } else if (c = gchar_cursor(), !ascii_iswhite(c)) {
- find_first_blank(&start_pos);
- }
- }
-
- if (VIsual_active) {
- // Avoid getting stuck with "is" on a single space before a sentence.
- if (equalpos(start_pos, curwin->w_cursor)) {
- goto extend;
- }
- if (*p_sel == 'e') {
- ++curwin->w_cursor.col;
- }
- VIsual = start_pos;
- VIsual_mode = 'v';
- redraw_cmdline = true; // show mode later
- redraw_curbuf_later(INVERTED); // update the inversion
- } else {
- // include a newline after the sentence, if there is one
- if (incl(&curwin->w_cursor) == -1) {
- oap->inclusive = true;
- } else {
- oap->inclusive = false;
- }
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- }
- return OK;
-}
-
-/// Find block under the cursor, cursor at end.
-/// "what" and "other" are two matching parenthesis/brace/etc.
-///
-/// @param include TRUE == include white space
-/// @param what '(', '{', etc.
-/// @param other ')', '}', etc.
-int current_block(oparg_T *oap, long count, int include, int what, int other)
-{
- pos_T old_pos;
- pos_T *pos = NULL;
- pos_T start_pos;
- pos_T *end_pos;
- pos_T old_start, old_end;
- char *save_cpo;
- bool sol = false; // '{' at start of line
-
- old_pos = curwin->w_cursor;
- old_end = curwin->w_cursor; // remember where we started
- old_start = old_end;
-
- /*
- * If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
- */
- if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
- setpcmark();
- if (what == '{') { // ignore indent
- while (inindent(1)) {
- if (inc_cursor() != 0) {
- break;
- }
- }
- }
- if (gchar_cursor() == what) {
- // cursor on '(' or '{', move cursor just after it
- ++curwin->w_cursor.col;
- }
- } else if (lt(VIsual, curwin->w_cursor)) {
- old_start = VIsual;
- curwin->w_cursor = VIsual; // cursor at low end of Visual
- } else {
- old_end = VIsual;
- }
-
- // Search backwards for unclosed '(', '{', etc..
- // Put this position in start_pos.
- // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
- // user wants.
- save_cpo = p_cpo;
- p_cpo = vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%";
- if ((pos = findmatch(NULL, what)) != NULL) {
- while (count-- > 0) {
- if ((pos = findmatch(NULL, what)) == NULL) {
- break;
- }
- curwin->w_cursor = *pos;
- start_pos = *pos; // the findmatch for end_pos will overwrite *pos
- }
- } else {
- while (count-- > 0) {
- if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL) {
- break;
- }
- curwin->w_cursor = *pos;
- start_pos = *pos; // the findmatch for end_pos will overwrite *pos
- }
- }
- p_cpo = save_cpo;
-
- /*
- * Search for matching ')', '}', etc.
- * Put this position in curwin->w_cursor.
- */
- if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) {
- curwin->w_cursor = old_pos;
- return FAIL;
- }
- curwin->w_cursor = *end_pos;
-
- // Try to exclude the '(', '{', ')', '}', etc. when "include" is FALSE.
- // If the ending '}', ')' or ']' is only preceded by indent, skip that
- // indent. But only if the resulting area is not smaller than what we
- // started with.
- while (!include) {
- incl(&start_pos);
- sol = (curwin->w_cursor.col == 0);
- decl(&curwin->w_cursor);
- while (inindent(1)) {
- sol = true;
- if (decl(&curwin->w_cursor) != 0) {
- break;
- }
- }
-
- // In Visual mode, when the resulting area is not bigger than what we
- // started with, extend it to the next block, and then exclude again.
- // Don't try to expand the area if the area is empty.
- if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
- && !equalpos(start_pos, curwin->w_cursor)
- && VIsual_active) {
- curwin->w_cursor = old_start;
- decl(&curwin->w_cursor);
- if ((pos = findmatch(NULL, what)) == NULL) {
- curwin->w_cursor = old_pos;
- return FAIL;
- }
- start_pos = *pos;
- curwin->w_cursor = *pos;
- if ((end_pos = findmatch(NULL, other)) == NULL) {
- curwin->w_cursor = old_pos;
- return FAIL;
- }
- curwin->w_cursor = *end_pos;
- } else {
- break;
- }
- }
-
- if (VIsual_active) {
- if (*p_sel == 'e') {
- inc(&curwin->w_cursor);
- }
- if (sol && gchar_cursor() != NUL) {
- inc(&curwin->w_cursor); // include the line break
- }
- VIsual = start_pos;
- VIsual_mode = 'v';
- redraw_curbuf_later(INVERTED); // update the inversion
- showmode();
- } else {
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- oap->inclusive = false;
- if (sol) {
- incl(&curwin->w_cursor);
- } else if (ltoreq(start_pos, curwin->w_cursor)) {
- // Include the character under the cursor.
- oap->inclusive = true;
- } else {
- // End is before the start (no text in between <>, [], etc.): don't
- // operate on any text.
- curwin->w_cursor = start_pos;
- }
- }
-
- return OK;
-}
-
-/// @param end_tag when true, return true if the cursor is on "</aaa>".
-///
-/// @return true if the cursor is on a "<aaa>" tag. Ignore "<aaa/>".
-static bool in_html_tag(bool end_tag)
-{
- char_u *line = get_cursor_line_ptr();
- char_u *p;
- int c;
- int lc = NUL;
- pos_T pos;
-
- for (p = line + curwin->w_cursor.col; p > line;) {
- if (*p == '<') { // find '<' under/before cursor
- break;
- }
- MB_PTR_BACK(line, p);
- if (*p == '>') { // find '>' before cursor
- break;
- }
- }
- if (*p != '<') {
- return false;
- }
-
- pos.lnum = curwin->w_cursor.lnum;
- pos.col = (colnr_T)(p - line);
-
- MB_PTR_ADV(p);
- if (end_tag) {
- // check that there is a '/' after the '<'
- return *p == '/';
- }
-
- // check that there is no '/' after the '<'
- if (*p == '/') {
- return false;
- }
-
- // check that the matching '>' is not preceded by '/'
- for (;;) {
- if (inc(&pos) < 0) {
- return false;
- }
- c = *ml_get_pos(&pos);
- if (c == '>') {
- break;
- }
- lc = c;
- }
- return lc != '/';
-}
-
-/// Find tag block under the cursor, cursor at end.
-///
-/// @param include true == include white space
-int current_tagblock(oparg_T *oap, long count_arg, bool include)
-{
- long count = count_arg;
- pos_T old_pos;
- pos_T start_pos;
- pos_T end_pos;
- pos_T old_start, old_end;
- char_u *p;
- char_u *cp;
- int len;
- bool do_include = include;
- bool save_p_ws = p_ws;
- int retval = FAIL;
- int is_inclusive = true;
-
- p_ws = false;
-
- old_pos = curwin->w_cursor;
- old_end = curwin->w_cursor; // remember where we started
- old_start = old_end;
- if (!VIsual_active || *p_sel == 'e') {
- decl(&old_end); // old_end is inclusive
- }
- /*
- * If we start on "<aaa>" select that block.
- */
- if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
- setpcmark();
-
- // ignore indent
- while (inindent(1)) {
- if (inc_cursor() != 0) {
- break;
- }
- }
-
- if (in_html_tag(false)) {
- // cursor on start tag, move to its '>'
- while (*get_cursor_pos_ptr() != '>') {
- if (inc_cursor() < 0) {
- break;
- }
- }
- } else if (in_html_tag(true)) {
- // cursor on end tag, move to just before it
- while (*get_cursor_pos_ptr() != '<') {
- if (dec_cursor() < 0) {
- break;
- }
- }
- dec_cursor();
- old_end = curwin->w_cursor;
- }
- } else if (lt(VIsual, curwin->w_cursor)) {
- old_start = VIsual;
- curwin->w_cursor = VIsual; // cursor at low end of Visual
- } else {
- old_end = VIsual;
- }
-
-again:
- /*
- * Search backwards for unclosed "<aaa>".
- * Put this position in start_pos.
- */
- for (long n = 0; n < count; n++) {
- if (do_searchpair("<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
- "",
- "</[^>]*>", BACKWARD, NULL, 0,
- NULL, (linenr_T)0, 0L) <= 0) {
- curwin->w_cursor = old_pos;
- goto theend;
- }
- }
- start_pos = curwin->w_cursor;
-
- /*
- * Search for matching "</aaa>". First isolate the "aaa".
- */
- inc_cursor();
- p = get_cursor_pos_ptr();
- for (cp = p;
- *cp != NUL && *cp != '>' && !ascii_iswhite(*cp);
- MB_PTR_ADV(cp)) {}
- len = (int)(cp - p);
- if (len == 0) {
- curwin->w_cursor = old_pos;
- goto theend;
- }
- const size_t spat_len = (size_t)len + 39;
- char *const spat = xmalloc(spat_len);
- const size_t epat_len = (size_t)len + 9;
- char *const epat = xmalloc(epat_len);
- snprintf(spat, spat_len,
- "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p);
- snprintf(epat, epat_len, "</%.*s>\\c", len, p);
-
- const int r = (int)do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L);
-
- xfree(spat);
- xfree(epat);
-
- if (r < 1 || lt(curwin->w_cursor, old_end)) {
- // Can't find other end or it's before the previous end. Could be a
- // HTML tag that doesn't have a matching end. Search backwards for
- // another starting tag.
- count = 1;
- curwin->w_cursor = start_pos;
- goto again;
- }
-
- if (do_include) {
- // Include up to the '>'.
- while (*get_cursor_pos_ptr() != '>') {
- if (inc_cursor() < 0) {
- break;
- }
- }
- } else {
- char_u *c = get_cursor_pos_ptr();
- // Exclude the '<' of the end tag.
- // If the closing tag is on new line, do not decrement cursor, but make
- // operation exclusive, so that the linefeed will be selected
- if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) {
- // do not decrement cursor
- is_inclusive = false;
- } else if (*c == '<') {
- dec_cursor();
- }
- }
- end_pos = curwin->w_cursor;
-
- if (!do_include) {
- // Exclude the start tag.
- curwin->w_cursor = start_pos;
- while (inc_cursor() >= 0) {
- if (*get_cursor_pos_ptr() == '>') {
- inc_cursor();
- start_pos = curwin->w_cursor;
- break;
- }
- }
- curwin->w_cursor = end_pos;
-
- // If we are in Visual mode and now have the same text as before set
- // "do_include" and try again.
- if (VIsual_active
- && equalpos(start_pos, old_start)
- && equalpos(end_pos, old_end)) {
- do_include = true;
- curwin->w_cursor = old_start;
- count = count_arg;
- goto again;
- }
- }
-
- if (VIsual_active) {
- // If the end is before the start there is no text between tags, select
- // the char under the cursor.
- if (lt(end_pos, start_pos)) {
- curwin->w_cursor = start_pos;
- } else if (*p_sel == 'e') {
- inc_cursor();
- }
- VIsual = start_pos;
- VIsual_mode = 'v';
- redraw_curbuf_later(INVERTED); // update the inversion
- showmode();
- } else {
- oap->start = start_pos;
- oap->motion_type = kMTCharWise;
- if (lt(end_pos, start_pos)) {
- // End is before the start: there is no text between tags; operate
- // on an empty area.
- curwin->w_cursor = start_pos;
- oap->inclusive = false;
- } else {
- oap->inclusive = is_inclusive;
- }
- }
- retval = OK;
-
-theend:
- p_ws = save_p_ws;
- return retval;
-}
-
-/// @param include TRUE == include white space
-/// @param type 'p' for paragraph, 'S' for section
-int current_par(oparg_T *oap, long count, int include, int type)
-{
- linenr_T start_lnum;
- linenr_T end_lnum;
- int white_in_front;
- int dir;
- int start_is_white;
- int prev_start_is_white;
- int retval = OK;
- int do_white = FALSE;
- int t;
- int i;
-
- if (type == 'S') { // not implemented yet
- return FAIL;
- }
-
- start_lnum = curwin->w_cursor.lnum;
-
- /*
- * When visual area is more than one line: extend it.
- */
- if (VIsual_active && start_lnum != VIsual.lnum) {
-extend:
- if (start_lnum < VIsual.lnum) {
- dir = BACKWARD;
- } else {
- dir = FORWARD;
- }
- for (i = (int)count; --i >= 0;) {
- if (start_lnum ==
- (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) {
- retval = FAIL;
- break;
- }
-
- prev_start_is_white = -1;
- for (t = 0; t < 2; ++t) {
- start_lnum += dir;
- start_is_white = linewhite(start_lnum);
- if (prev_start_is_white == start_is_white) {
- start_lnum -= dir;
- break;
- }
- for (;;) {
- if (start_lnum == (dir == BACKWARD
- ? 1 : curbuf->b_ml.ml_line_count)) {
- break;
- }
- if (start_is_white != linewhite(start_lnum + dir)
- || (!start_is_white
- && startPS(start_lnum + (dir > 0
- ? 1 : 0), 0, 0))) {
- break;
- }
- start_lnum += dir;
- }
- if (!include) {
- break;
- }
- if (start_lnum == (dir == BACKWARD
- ? 1 : curbuf->b_ml.ml_line_count)) {
- break;
- }
- prev_start_is_white = start_is_white;
- }
- }
- curwin->w_cursor.lnum = start_lnum;
- curwin->w_cursor.col = 0;
- return retval;
- }
-
- /*
- * First move back to the start_lnum of the paragraph or white lines
- */
- white_in_front = linewhite(start_lnum);
- while (start_lnum > 1) {
- if (white_in_front) { // stop at first white line
- if (!linewhite(start_lnum - 1)) {
- break;
- }
- } else { // stop at first non-white line of start of paragraph
- if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) {
- break;
- }
- }
- --start_lnum;
- }
-
- /*
- * Move past the end of any white lines.
- */
- end_lnum = start_lnum;
- while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) {
- ++end_lnum;
- }
-
- end_lnum--;
- i = (int)count;
- if (!include && white_in_front) {
- --i;
- }
- while (i--) {
- if (end_lnum == curbuf->b_ml.ml_line_count) {
- return FAIL;
- }
-
- if (!include) {
- do_white = linewhite(end_lnum + 1);
- }
-
- if (include || !do_white) {
- ++end_lnum;
- /*
- * skip to end of paragraph
- */
- while (end_lnum < curbuf->b_ml.ml_line_count
- && !linewhite(end_lnum + 1)
- && !startPS(end_lnum + 1, 0, 0)) {
- ++end_lnum;
- }
- }
-
- if (i == 0 && white_in_front && include) {
- break;
- }
-
- /*
- * skip to end of white lines after paragraph
- */
- if (include || do_white) {
- while (end_lnum < curbuf->b_ml.ml_line_count
- && linewhite(end_lnum + 1)) {
- ++end_lnum;
- }
- }
- }
-
- /*
- * If there are no empty lines at the end, try to find some empty lines at
- * the start (unless that has been done already).
- */
- if (!white_in_front && !linewhite(end_lnum) && include) {
- while (start_lnum > 1 && linewhite(start_lnum - 1)) {
- --start_lnum;
- }
- }
-
- if (VIsual_active) {
- // Problem: when doing "Vipipip" nothing happens in a single white
- // line, we get stuck there. Trap this here.
- if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) {
- goto extend;
- }
- if (VIsual.lnum != start_lnum) {
- VIsual.lnum = start_lnum;
- VIsual.col = 0;
- }
- VIsual_mode = 'V';
- redraw_curbuf_later(INVERTED); // update the inversion
- showmode();
- } else {
- oap->start.lnum = start_lnum;
- oap->start.col = 0;
- oap->motion_type = kMTLineWise;
- }
- curwin->w_cursor.lnum = end_lnum;
- curwin->w_cursor.col = 0;
-
- return OK;
-}
-
-/// Search quote char from string line[col].
-/// Quote character escaped by one of the characters in "escape" is not counted
-/// as a quote.
-///
-/// @param escape escape characters, can be NULL
-///
-/// @return column number of "quotechar" or -1 when not found.
-static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape)
-{
- int c;
-
- for (;;) {
- c = line[col];
- if (c == NUL) {
- return -1;
- } else if (escape != NULL && vim_strchr((char *)escape, c)) {
- col++;
- if (line[col] == NUL) {
- return -1;
- }
- } else if (c == quotechar) {
- break;
- }
- col += utfc_ptr2len((char *)line + col);
- }
- return col;
-}
-
-/// Search backwards in "line" from column "col_start" to find "quotechar".
-/// Quote character escaped by one of the characters in "escape" is not counted
-/// as a quote.
-///
-/// @param escape escape characters, can be NULL
-///
-/// @return the found column or zero.
-static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *escape)
-{
- int n;
-
- while (col_start > 0) {
- col_start--;
- col_start -= utf_head_off(line, line + col_start);
- n = 0;
- if (escape != NULL) {
- while (col_start - n > 0 && vim_strchr((char *)escape,
- line[col_start - n - 1]) != NULL) {
- ++n;
- }
- }
- if (n & 1) {
- col_start -= n; // uneven number of escape chars, skip it
- } else if (line[col_start] == quotechar) {
- break;
- }
- }
- return col_start;
-}
-
-/// Find quote under the cursor, cursor at end.
-///
-/// @param include true == include quote char
-/// @param quotechar Quote character
-///
-/// @return true if found, else false.
-bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
- FUNC_ATTR_NONNULL_ALL
-{
- char_u *line = get_cursor_line_ptr();
- int col_end;
- int col_start = curwin->w_cursor.col;
- bool inclusive = false;
- bool vis_empty = true; // Visual selection <= 1 char
- bool vis_bef_curs = false; // Visual starts before cursor
- bool did_exclusive_adj = false; // adjusted pos for 'selection'
- bool inside_quotes = false; // Looks like "i'" done before
- bool selected_quote = false; // Has quote inside selection
- int i;
- bool restore_vis_bef = false; // resotre VIsual on abort
-
- // When 'selection' is "exclusive" move the cursor to where it would be
- // with 'selection' "inclusive", so that the logic is the same for both.
- // The cursor then is moved forward after adjusting the area.
- if (VIsual_active) {
- // this only works within one line
- if (VIsual.lnum != curwin->w_cursor.lnum) {
- return false;
- }
-
- vis_bef_curs = lt(VIsual, curwin->w_cursor);
- vis_empty = equalpos(VIsual, curwin->w_cursor);
- if (*p_sel == 'e') {
- if (vis_bef_curs) {
- dec_cursor();
- did_exclusive_adj = true;
- } else if (!vis_empty) {
- dec(&VIsual);
- did_exclusive_adj = true;
- }
- vis_empty = equalpos(VIsual, curwin->w_cursor);
- if (!vis_bef_curs && !vis_empty) {
- // VIsual needs to be start of Visual selection.
- pos_T t = curwin->w_cursor;
-
- curwin->w_cursor = VIsual;
- VIsual = t;
- vis_bef_curs = true;
- restore_vis_bef = true;
- }
- }
- }
-
- if (!vis_empty) {
- // Check if the existing selection exactly spans the text inside
- // quotes.
- if (vis_bef_curs) {
- inside_quotes = VIsual.col > 0
- && line[VIsual.col - 1] == quotechar
- && line[curwin->w_cursor.col] != NUL
- && line[curwin->w_cursor.col + 1] == quotechar;
- i = VIsual.col;
- col_end = curwin->w_cursor.col;
- } else {
- inside_quotes = curwin->w_cursor.col > 0
- && line[curwin->w_cursor.col - 1] == quotechar
- && line[VIsual.col] != NUL
- && line[VIsual.col + 1] == quotechar;
- i = curwin->w_cursor.col;
- col_end = VIsual.col;
- }
-
- // Find out if we have a quote in the selection.
- while (i <= col_end) {
- // check for going over the end of the line, which can happen if
- // the line was changed after the Visual area was selected.
- if (line[i] == NUL) {
- break;
- }
- if (line[i++] == quotechar) {
- selected_quote = true;
- break;
- }
- }
- }
-
- if (!vis_empty && line[col_start] == quotechar) {
- // Already selecting something and on a quote character. Find the
- // next quoted string.
- if (vis_bef_curs) {
- // Assume we are on a closing quote: move to after the next
- // opening quote.
- col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
- if (col_start < 0) {
- goto abort_search;
- }
- col_end = find_next_quote(line, col_start + 1, quotechar,
- curbuf->b_p_qe);
- if (col_end < 0) {
- // We were on a starting quote perhaps?
- col_end = col_start;
- col_start = curwin->w_cursor.col;
- }
- } else {
- col_end = find_prev_quote(line, col_start, quotechar, NULL);
- if (line[col_end] != quotechar) {
- goto abort_search;
- }
- col_start = find_prev_quote(line, col_end, quotechar,
- curbuf->b_p_qe);
- if (line[col_start] != quotechar) {
- // We were on an ending quote perhaps?
- col_start = col_end;
- col_end = curwin->w_cursor.col;
- }
- }
- } else if (line[col_start] == quotechar || !vis_empty) {
- int first_col = col_start;
-
- if (!vis_empty) {
- if (vis_bef_curs) {
- first_col = find_next_quote(line, col_start, quotechar, NULL);
- } else {
- first_col = find_prev_quote(line, col_start, quotechar, NULL);
- }
- }
- // The cursor is on a quote, we don't know if it's the opening or
- // closing quote. Search from the start of the line to find out.
- // Also do this when there is a Visual area, a' may leave the cursor
- // in between two strings.
- col_start = 0;
- for (;;) {
- // Find open quote character.
- col_start = find_next_quote(line, col_start, quotechar, NULL);
- if (col_start < 0 || col_start > first_col) {
- goto abort_search;
- }
- // Find close quote character.
- col_end = find_next_quote(line, col_start + 1, quotechar,
- curbuf->b_p_qe);
- if (col_end < 0) {
- goto abort_search;
- }
- // If is cursor between start and end quote character, it is
- // target text object.
- if (col_start <= first_col && first_col <= col_end) {
- break;
- }
- col_start = col_end + 1;
- }
- } else {
- // Search backward for a starting quote.
- col_start = find_prev_quote(line, col_start, quotechar, curbuf->b_p_qe);
- if (line[col_start] != quotechar) {
- // No quote before the cursor, look after the cursor.
- col_start = find_next_quote(line, col_start, quotechar, NULL);
- if (col_start < 0) {
- goto abort_search;
- }
- }
-
- // Find close quote character.
- col_end = find_next_quote(line, col_start + 1, quotechar,
- curbuf->b_p_qe);
- if (col_end < 0) {
- goto abort_search;
- }
- }
-
- // When "include" is true, include spaces after closing quote or before
- // the starting quote.
- if (include) {
- if (ascii_iswhite(line[col_end + 1])) {
- while (ascii_iswhite(line[col_end + 1])) {
- ++col_end;
- }
- } else {
- while (col_start > 0 && ascii_iswhite(line[col_start - 1])) {
- --col_start;
- }
- }
- }
-
- // Set start position. After vi" another i" must include the ".
- // For v2i" include the quotes.
- if (!include && count < 2 && (vis_empty || !inside_quotes)) {
- col_start++;
- }
- curwin->w_cursor.col = col_start;
- if (VIsual_active) {
- // Set the start of the Visual area when the Visual area was empty, we
- // were just inside quotes or the Visual area didn't start at a quote
- // and didn't include a quote.
- if (vis_empty
- || (vis_bef_curs
- && !selected_quote
- && (inside_quotes
- || (line[VIsual.col] != quotechar
- && (VIsual.col == 0
- || line[VIsual.col - 1] != quotechar))))) {
- VIsual = curwin->w_cursor;
- redraw_curbuf_later(INVERTED);
- }
- } else {
- oap->start = curwin->w_cursor;
- oap->motion_type = kMTCharWise;
- }
-
- // Set end position.
- curwin->w_cursor.col = col_end;
- if ((include || count > 1
- // After vi" another i" must include the ".
- || (!vis_empty && inside_quotes)
- ) && inc_cursor() == 2) {
- inclusive = true;
- }
- if (VIsual_active) {
- if (vis_empty || vis_bef_curs) {
- // decrement cursor when 'selection' is not exclusive
- if (*p_sel != 'e') {
- dec_cursor();
- }
- } else {
- // Cursor is at start of Visual area. Set the end of the Visual
- // area when it was just inside quotes or it didn't end at a
- // quote.
- if (inside_quotes
- || (!selected_quote
- && line[VIsual.col] != quotechar
- && (line[VIsual.col] == NUL
- || line[VIsual.col + 1] != quotechar))) {
- dec_cursor();
- VIsual = curwin->w_cursor;
- }
- curwin->w_cursor.col = col_start;
- }
- if (VIsual_mode == 'V') {
- VIsual_mode = 'v';
- redraw_cmdline = true; // show mode later
- }
- } else {
- // Set inclusive and other oap's flags.
- oap->inclusive = inclusive;
- }
-
- return true;
-
-abort_search:
- if (VIsual_active && *p_sel == 'e') {
- if (did_exclusive_adj) {
- inc_cursor();
- }
- if (restore_vis_bef) {
- pos_T t = curwin->w_cursor;
-
- curwin->w_cursor = VIsual;
- VIsual = t;
- }
- }
- return false;
-}
-
/// Find next search match under cursor, cursor at end.
/// Used while an operator is pending, and in Visual mode.
///
@@ -4389,7 +2576,7 @@ int current_search(long count, bool forward)
may_start_select('c');
setmouse();
- redraw_curbuf_later(INVERTED);
+ redraw_curbuf_later(UPD_INVERTED);
showmode();
return OK;
@@ -4399,7 +2586,7 @@ int current_search(long count, bool forward)
/// If move is true, check from the beginning of the buffer,
/// else from position "cur".
/// "direction" is FORWARD or BACKWARD.
-/// Returns TRUE, FALSE or -1 for failure.
+/// Returns true, false or -1 for failure.
static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direction)
{
regmmatch_T regmatch;
@@ -4456,9 +2643,7 @@ static int is_zero_width(char_u *pattern, int move, pos_T *cur, Direction direct
return result;
}
-/*
- * return TRUE if line 'lnum' is empty or has white chars only.
- */
+/// return true if line 'lnum' is empty or has white chars only.
int linewhite(linenr_T lnum)
{
char_u *p;
@@ -4551,7 +2736,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
static buf_T *lbuf = NULL;
proftime_T start;
- memset(stat, 0, sizeof(searchstat_T));
+ CLEAR_POINTER(stat);
if (dirc == 0 && !recompute && !EMPTY_POS(lastpos)) {
stat->cur = cur;
@@ -4637,7 +2822,7 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
}
// "searchcount()" function
-void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_searchcount(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
pos_T pos = curwin->w_cursor;
char_u *pattern = NULL;
@@ -4869,7 +3054,7 @@ static int fuzzy_match_compute_score(const char_u *const str, const int strSz,
if (currIdx > 0) {
// Camel case
const char_u *p = str;
- int neighbor;
+ int neighbor = ' ';
for (uint32_t sidx = 0; sidx < currIdx; sidx++) {
neighbor = utf_ptr2char((char *)p);
@@ -5287,13 +3472,13 @@ static void do_fuzzymatch(const typval_T *const argvars, typval_T *const rettv,
}
/// "matchfuzzy()" function
-void f_matchfuzzy(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchfuzzy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_fuzzymatch(argvars, rettv, false);
}
/// "matchfuzzypos()" function
-void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_matchfuzzypos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
do_fuzzymatch(argvars, rettv, true);
}
@@ -5309,7 +3494,7 @@ static char_u *get_line_and_copy(linenr_T lnum, char_u *buf)
}
/// Find identifiers or defines in included files.
-/// If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase.
+/// If p_ic && compl_status_sol() then ptr must be in lowercase.
///
/// @param ptr pointer to search pattern
/// @param dir direction of expansion
@@ -5351,7 +3536,6 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
int i;
char_u *already = NULL;
char_u *startp = NULL;
- char_u *inc_opt = NULL;
win_T *curwin_save = NULL;
const int l_g_do_tagpreview = g_do_tagpreview;
@@ -5362,9 +3546,9 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
file_line = xmalloc(LSIZE);
if (type != CHECK_PATH && type != FIND_DEFINE
- // when CONT_SOL is set compare "ptr" with the beginning of the line
- // is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo
- && !(compl_cont_status & CONT_SOL)) {
+ // when CONT_SOL is set compare "ptr" with the beginning of the
+ // line is faster than quote_meta/regcomp/regexec "ptr" -- Acevedo
+ && !compl_status_sol()) {
pat = xmalloc(len + 5);
assert(len <= INT_MAX);
sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr);
@@ -5376,22 +3560,22 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
goto fpip_end;
}
}
- inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
+ char *inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc;
if (*inc_opt != NUL) {
- incl_regmatch.regprog = vim_regcomp((char *)inc_opt, p_magic ? RE_MAGIC : 0);
+ incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0);
if (incl_regmatch.regprog == NULL) {
goto fpip_end;
}
- incl_regmatch.rm_ic = FALSE; // don't ignore case in incl. pat.
+ incl_regmatch.rm_ic = false; // don't ignore case in incl. pat.
}
if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) {
def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
- ? (char *)p_def : (char *)curbuf->b_p_def,
+ ? p_def : curbuf->b_p_def,
p_magic ? RE_MAGIC : 0);
if (def_regmatch.regprog == NULL) {
goto fpip_end;
}
- def_regmatch.rm_ic = FALSE; // don't ignore case in define pat.
+ def_regmatch.rm_ic = false; // don't ignore case in define pat.
}
files = xcalloc((size_t)max_path_depth, sizeof(SearchedFile));
old_files = max_path_depth;
@@ -5412,7 +3596,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
char_u *p_fname = (curr_fname == (char_u *)curbuf->b_fname)
? (char_u *)curbuf->b_ffname : curr_fname;
- if (inc_opt != NULL && strstr((char *)inc_opt, "\\zs") != NULL) {
+ if (inc_opt != NULL && strstr(inc_opt, "\\zs") != NULL) {
// Use text from '\zs' to '\ze' (or end) of 'include'.
new_fname = find_file_name_in_path(incl_regmatch.startp[0],
(size_t)(incl_regmatch.endp[0]
@@ -5424,7 +3608,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
new_fname = file_name_in_line(incl_regmatch.endp[0], 0,
FNAME_EXP|FNAME_INCL|FNAME_REL, 1L, p_fname, NULL);
}
- already_searched = FALSE;
+ already_searched = false;
if (new_fname != NULL) {
// Check whether we have already searched in this file
for (i = 0;; i++) {
@@ -5467,7 +3651,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
}
did_show = true;
while (depth_displayed < depth && !got_int) {
- ++depth_displayed;
+ depth_displayed++;
for (i = 0; i < depth_displayed; i++) {
msg_puts(" ");
}
@@ -5482,14 +3666,14 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
if (new_fname != NULL) {
// using "new_fname" is more reliable, e.g., when
// 'includeexpr' is set.
- msg_outtrans_attr(new_fname, HL_ATTR(HLF_D));
+ msg_outtrans_attr((char *)new_fname, HL_ATTR(HLF_D));
} else {
/*
* Isolate the file name.
* Include the surrounding "" or <> if present.
*/
if (inc_opt != NULL
- && strstr((char *)inc_opt, "\\zs") != NULL) {
+ && strstr(inc_opt, "\\zs") != NULL) {
// pattern contains \zs, use the match
p = incl_regmatch.startp[0];
i = (int)(incl_regmatch.endp[0]
@@ -5509,16 +3693,16 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
// Avoid checking before the start of the line, can
// happen if \zs appears in the regexp.
if (p[-1] == '"' || p[-1] == '<') {
- --p;
- ++i;
+ p--;
+ i++;
}
if (p[i] == '"' || p[i] == '>') {
- ++i;
+ i++;
}
}
save_char = p[i];
p[i] = NUL;
- msg_outtrans_attr(p, HL_ATTR(HLF_D));
+ msg_outtrans_attr((char *)p, HL_ATTR(HLF_D));
p[i] = save_char;
}
@@ -5544,7 +3728,7 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
bigger[i].fp = NULL;
bigger[i].name = NULL;
bigger[i].lnum = 0;
- bigger[i].matched = FALSE;
+ bigger[i].matched = false;
}
for (i = old_files; i < max_path_depth; i++) {
bigger[i + max_path_depth] = files[i];
@@ -5561,11 +3745,11 @@ void find_pattern_in_path(char_u *ptr, Direction dir, size_t len, bool whole, bo
// Something wrong. We will forget one of our already visited files
// now.
xfree(files[old_files].name);
- ++old_files;
+ old_files++;
}
files[depth].name = curr_fname = new_fname;
files[depth].lnum = 0;
- files[depth].matched = FALSE;
+ files[depth].matched = false;
if (action == ACTION_EXPAND) {
msg_hist_off = true; // reset in msg_trunc_attr()
vim_snprintf((char *)IObuff, IOSIZE,
@@ -5604,8 +3788,7 @@ search_line:
* define and this line didn't match define_prog above.
*/
if (def_regmatch.regprog == NULL || define_matched) {
- if (define_matched
- || (compl_cont_status & CONT_SOL)) {
+ if (define_matched || compl_status_sol()) {
// compare the first "len" chars from "ptr"
startp = (char_u *)skipwhite((char *)p);
if (p_ic) {
@@ -5640,7 +3823,7 @@ search_line:
p = (char_u *)skipwhite((char *)line);
if (matched
|| (p[0] == '/' && p[1] == '*') || p[0] == '*') {
- for (p = line; *p && p < startp; ++p) {
+ for (p = line; *p && p < startp; p++) {
if (matched
&& p[0] == '/'
&& (p[1] == '*' || p[1] == '/')) {
@@ -5671,8 +3854,8 @@ search_line:
}
found = true;
aux = p = startp;
- if (compl_cont_status & CONT_ADDING) {
- p += compl_length;
+ if (compl_status_adding()) {
+ p += ins_compl_len();
if (vim_iswordp(p)) {
goto exit_matched;
}
@@ -5681,7 +3864,7 @@ search_line:
p = find_word_end(p);
i = (int)(p - aux);
- if ((compl_cont_status & CONT_ADDING) && i == compl_length) {
+ if (compl_status_adding() && i == ins_compl_len()) {
// IOSIZE > compl_length, so the STRNCPY works
STRNCPY(IObuff, aux, i);
@@ -5728,7 +3911,7 @@ search_line:
IObuff[i] = NUL;
aux = IObuff;
- if (i == compl_length) {
+ if (i == ins_compl_len()) {
goto exit_matched;
}
}
@@ -5820,14 +4003,14 @@ search_line:
}
if (action != ACTION_SHOW) {
curwin->w_cursor.col = (colnr_T)(startp - line);
- curwin->w_set_curswant = TRUE;
+ curwin->w_set_curswant = true;
}
if (l_g_do_tagpreview != 0
&& curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
break;
@@ -5838,7 +4021,7 @@ exit_matched:
// are not at the end of it already
if (def_regmatch.regprog == NULL
&& action == ACTION_EXPAND
- && !(compl_cont_status & CONT_SOL)
+ && !compl_status_sol()
&& *startp != NUL
&& *(p = startp + utfc_ptr2len((char *)startp)) != NUL) {
goto search_line;
@@ -5860,7 +4043,7 @@ exit_matched:
while (depth >= 0 && !already
&& vim_fgets(line = file_line, LSIZE, files[depth].fp)) {
fclose(files[depth].fp);
- --old_files;
+ old_files--;
files[old_files].name = files[depth].name;
files[old_files].matched = files[depth].matched;
depth--;
@@ -5948,10 +4131,10 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
if (fp != NULL) {
// We used fgets(), so get rid of newline at end
if (p >= line && *p == '\n') {
- --p;
+ p--;
}
if (p >= line && *p == '\r') {
- --p;
+ p--;
}
*(p + 1) = NUL;
}
@@ -5963,7 +4146,7 @@ static void show_pat_in_path(char_u *line, int type, bool did_show, int action,
msg_puts_attr((const char *)IObuff, HL_ATTR(HLF_N));
msg_puts(" ");
}
- msg_prt_line(line, FALSE);
+ msg_prt_line(line, false);
ui_flush(); // show one line at a time
// Definition continues until line that doesn't end with '\'
@@ -5996,7 +4179,7 @@ void get_search_pattern(SearchPattern *const pat)
void get_substitute_pattern(SearchPattern *const pat)
{
memcpy(pat, &(spats[1]), sizeof(spats[1]));
- memset(&(pat->off), 0, sizeof(pat->off));
+ CLEAR_FIELD(pat->off);
}
/// Set last search pattern
@@ -6012,7 +4195,7 @@ void set_substitute_pattern(const SearchPattern pat)
{
free_spat(&spats[1]);
memcpy(&(spats[1]), &pat, sizeof(spats[1]));
- memset(&(spats[1].off), 0, sizeof(spats[1].off));
+ CLEAR_FIELD(spats[1].off);
}
/// Set last used search pattern
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 6e80b550d8..d2f1b39ca4 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -16,11 +16,12 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
+#include "nvim/cmdhist.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/typval.h"
+#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
#include "nvim/globals.h"
@@ -1476,7 +1477,7 @@ static char *shada_filename(const char *file)
// because various expansions must have already be done by the shell.
// If shell is not performing them then they should be done in main.c
// where arguments are parsed, *not here*.
- expand_env((char_u *)file, &(NameBuff[0]), MAXPATHL);
+ expand_env((char *)file, &(NameBuff[0]), MAXPATHL);
file = (const char *)&(NameBuff[0]);
}
}
@@ -2188,7 +2189,7 @@ static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_re
k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret);
FileMarks *const filemarks = &kh_val(&wms->file_marks, k);
if (kh_ret > 0) {
- memset(filemarks, 0, sizeof(*filemarks));
+ CLEAR_POINTER(filemarks);
}
if (entry.timestamp > filemarks->greatest_timestamp) {
filemarks->greatest_timestamp = entry.timestamp;
@@ -2451,6 +2452,27 @@ static inline void find_removable_bufs(khash_t(bufset) *removable_bufs)
}
}
+/// Translate a history type number to the associated character
+static int hist_type2char(const int type)
+ FUNC_ATTR_CONST
+{
+ switch (type) {
+ case HIST_CMD:
+ return ':';
+ case HIST_SEARCH:
+ return '/';
+ case HIST_EXPR:
+ return '=';
+ case HIST_INPUT:
+ return '@';
+ case HIST_DEBUG:
+ return '>';
+ default:
+ abort();
+ }
+ return NUL;
+}
+
/// Write ShaDa file
///
/// @param[in] sd_writer Structure containing file writer definition.
@@ -2730,7 +2752,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef
k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret);
FileMarks *const filemarks = &kh_val(&wms->file_marks, k);
if (kh_ret > 0) {
- memset(filemarks, 0, sizeof(*filemarks));
+ CLEAR_POINTER(filemarks);
}
do {
fmark_T fm;
@@ -3001,7 +3023,7 @@ shada_write_file_nomerge: {}
if (tail != fname) {
const char tail_save = *tail;
*tail = NUL;
- if (!os_isdir((char_u *)fname)) {
+ if (!os_isdir(fname)) {
int ret;
char *failed_dir;
if ((ret = os_mkdir_recurse(fname, 0700, &failed_dir)) != 0) {
@@ -3456,7 +3478,7 @@ shada_read_next_item_start:
// data union are NULL so they are safe to xfree(). This is needed in case
// somebody calls goto shada_read_next_item_error before anything is set in
// the switch.
- memset(entry, 0, sizeof(*entry));
+ CLEAR_POINTER(entry);
if (sd_reader->eof) {
return kSDReadStatusFinished;
}
@@ -3974,7 +3996,7 @@ static bool shada_removable(const char *name)
bool retval = false;
char *new_name = home_replace_save(NULL, (char *)name);
- for (p = (char *)p_shada; *p;) {
+ for (p = p_shada; *p;) {
(void)copy_option_part(&p, part, ARRAY_SIZE(part), ", ");
if (part[0] == 'r') {
home_replace(NULL, part + 1, (char *)NameBuff, MAXPATHL, true);
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index 7b6b55fede..ed7f53d3ba 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -9,6 +9,7 @@
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval/funcs.h"
#include "nvim/ex_docmd.h"
@@ -16,7 +17,6 @@
#include "nvim/highlight_group.h"
#include "nvim/move.h"
#include "nvim/option.h"
-#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/syntax.h"
#include "nvim/vim.h"
@@ -203,7 +203,7 @@ static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int
// When adding first sign need to redraw the windows to create the
// column for signs.
if (buf->b_signlist == NULL) {
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
changed_line_abv_curs();
}
@@ -442,27 +442,24 @@ static linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group
/// @param max_signs the number of signs, with priority for the ones
/// with the highest Ids.
/// @return Attrs of the matching sign, or NULL
-sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int max_signs)
+SignTextAttrs *sign_get_attr(int idx, SignTextAttrs sattrs[], int max_signs)
{
- sign_attrs_T *matches[SIGN_SHOW_MAX];
- int nr_matches = 0;
+ SignTextAttrs *matches[SIGN_SHOW_MAX];
+ int sattr_matches = 0;
for (int i = 0; i < SIGN_SHOW_MAX; i++) {
- if ((type == SIGN_TEXT && sattrs[i].sat_text != NULL)
- || (type == SIGN_LINEHL && sattrs[i].sat_linehl != 0)
- || (type == SIGN_NUMHL && sattrs[i].sat_numhl != 0)) {
- matches[nr_matches] = &sattrs[i];
- nr_matches++;
+ if (sattrs[i].text != NULL) {
+ matches[sattr_matches++] = &sattrs[i];
// attr list is sorted with most important (priority, id), thus we
// may stop as soon as we have max_signs matches
- if (nr_matches >= max_signs) {
+ if (sattr_matches >= max_signs) {
break;
}
}
}
- if (nr_matches > idx) {
- return matches[nr_matches - idx - 1];
+ if (sattr_matches > idx) {
+ return matches[sattr_matches - idx - 1];
}
return NULL;
@@ -474,12 +471,12 @@ sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int m
/// @param lnum Line in which to search
/// @param sattrs Output array for attrs
/// @return Number of signs of which attrs were found
-int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
+int buf_get_signattrs(buf_T *buf, linenr_T lnum, SignTextAttrs sattrs[], HlPriAttr *num_attrs,
+ HlPriAttr *line_attrs, HlPriAttr *cul_attrs)
{
sign_entry_T *sign;
- sign_T *sp;
- int nr_matches = 0;
+ int sattr_matches = 0;
FOR_ALL_SIGNS_IN_BUF(buf, sign) {
if (sign->se_lnum > lnum) {
@@ -488,37 +485,39 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[])
break;
}
- if (sign->se_lnum == lnum) {
- sign_attrs_T sattr;
- memset(&sattr, 0, sizeof(sattr));
- sattr.sat_typenr = sign->se_typenr;
- sp = find_sign_by_typenr(sign->se_typenr);
- if (sp != NULL) {
- sattr.sat_text = sp->sn_text;
- if (sattr.sat_text != NULL && sp->sn_text_hl != 0) {
- sattr.sat_texthl = syn_id2attr(sp->sn_text_hl);
- }
- if (sp->sn_line_hl != 0) {
- sattr.sat_linehl = syn_id2attr(sp->sn_line_hl);
- }
- if (sp->sn_cul_hl != 0) {
- sattr.sat_culhl = syn_id2attr(sp->sn_cul_hl);
- }
- if (sp->sn_num_hl != 0) {
- sattr.sat_numhl = syn_id2attr(sp->sn_num_hl);
- }
- // Store the priority so we can mesh in extmark signs later
- sattr.sat_prio = sign->se_priority;
- }
+ if (sign->se_lnum < lnum) {
+ continue;
+ }
- sattrs[nr_matches] = sattr;
- nr_matches++;
- if (nr_matches == SIGN_SHOW_MAX) {
- break;
+ sign_T *sp = find_sign_by_typenr(sign->se_typenr);
+ if (sp == NULL) {
+ continue;
+ }
+
+ if (sp->sn_text != NULL && sattr_matches < SIGN_SHOW_MAX) {
+ sattrs[sattr_matches++] = (SignTextAttrs) {
+ .text = sp->sn_text,
+ .hl_attr_id = sp->sn_text_hl == 0 ? 0 : syn_id2attr(sp->sn_text_hl),
+ .priority = sign->se_priority
+ };
+ }
+
+ struct { HlPriAttr *dest; int hl; } cattrs[] = {
+ { line_attrs, sp->sn_line_hl },
+ { num_attrs, sp->sn_num_hl },
+ { cul_attrs, sp->sn_cul_hl },
+ { NULL, -1 },
+ };
+ for (int i = 0; cattrs[i].dest; i++) {
+ if (cattrs[i].hl != 0 && sign->se_priority >= cattrs[i].dest->priority) {
+ *cattrs[i].dest = (HlPriAttr) {
+ .attr_id = syn_id2attr(cattrs[i].hl),
+ .priority = sign->se_priority
+ };
}
}
}
- return nr_matches;
+ return sattr_matches;
}
/// Delete sign 'id' in group 'group' from buffer 'buf'.
@@ -577,7 +576,7 @@ static linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group)
// When deleting the last sign the cursor position may change, because the
// sign columns no longer shows. And the 'signcolumn' may be hidden.
if (buf->b_signlist == NULL) {
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
changed_line_abv_curs();
}
@@ -859,7 +858,7 @@ static void sign_define_init_icon(sign_T *sp, char_u *icon)
{
xfree(sp->sn_icon);
sp->sn_icon = vim_strsave(icon);
- backslash_halve(sp->sn_icon);
+ backslash_halve((char *)sp->sn_icon);
}
/// Initialize the text for a new sign
@@ -935,7 +934,7 @@ static int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_
// non-empty sign list.
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer->b_signlist != NULL) {
- redraw_buf_later(wp->w_buffer, NOT_VALID);
+ redraw_buf_later(wp->w_buffer, UPD_NOT_VALID);
}
}
}
@@ -1085,7 +1084,7 @@ static int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T at
}
if (sign_id == 0) {
// Delete all the signs in the specified buffer
- redraw_buf_later(buf, NOT_VALID);
+ redraw_buf_later(buf, UPD_NOT_VALID);
buf_delete_signs(buf, (char *)sign_group);
} else {
linenr_T lnum;
@@ -1354,7 +1353,7 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
if (STRNCMP(arg, "line=", 5) == 0) {
arg += 5;
*lnum = atoi((char *)arg);
- arg = skiptowhite(arg);
+ arg = (char_u *)skiptowhite((char *)arg);
lnum_arg = true;
} else if (STRNCMP(arg, "*", 1) == 0 && cmd == SIGNCMD_UNPLACE) {
if (*signid != -1) {
@@ -1362,11 +1361,11 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
return FAIL;
}
*signid = -2;
- arg = skiptowhite(arg + 1);
+ arg = (char_u *)skiptowhite((char *)arg + 1);
} else if (STRNCMP(arg, "name=", 5) == 0) {
arg += 5;
name = arg;
- arg = skiptowhite(arg);
+ arg = (char_u *)skiptowhite((char *)arg);
if (*arg != NUL) {
*arg++ = NUL;
}
@@ -1377,14 +1376,14 @@ static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *si
} else if (STRNCMP(arg, "group=", 6) == 0) {
arg += 6;
*group = arg;
- arg = skiptowhite(arg);
+ arg = (char_u *)skiptowhite((char *)arg);
if (*arg != NUL) {
*arg++ = NUL;
}
} else if (STRNCMP(arg, "priority=", 9) == 0) {
arg += 9;
*prio = atoi((char *)arg);
- arg = skiptowhite(arg);
+ arg = (char_u *)skiptowhite((char *)arg);
} else if (STRNCMP(arg, "file=", 5) == 0) {
arg += 5;
filename = arg;
@@ -1428,7 +1427,7 @@ void ex_sign(exarg_T *eap)
sign_T *sp;
// Parse the subcommand.
- p = skiptowhite(arg);
+ p = (char_u *)skiptowhite((char *)arg);
idx = sign_cmd_idx(arg, p);
if (idx == SIGNCMD_LAST) {
semsg(_("E160: Unknown sign command: %s"), arg);
@@ -1450,7 +1449,7 @@ void ex_sign(exarg_T *eap)
// Isolate the sign name. If it's a number skip leading zeroes,
// so that "099" and "99" are the same sign. But keep "0".
- p = skiptowhite(arg);
+ p = (char_u *)skiptowhite((char *)arg);
if (*p != NUL) {
*p++ = NUL;
}
@@ -1790,7 +1789,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
expand_what = EXP_SUBCMD;
xp->xp_pattern = (char *)arg;
- end_subcmd = skiptowhite(arg);
+ end_subcmd = (char_u *)skiptowhite((char *)arg);
if (*end_subcmd == NUL) {
// expand subcmd name
// :sign {subcmd}<CTRL-D>
@@ -1815,7 +1814,7 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
do {
p = (char_u *)skipwhite((char *)p);
last = p;
- p = skiptowhite(p);
+ p = (char_u *)skiptowhite((char *)p);
} while (*p != NUL);
p = (char_u *)vim_strchr((char *)last, '=');
@@ -1965,7 +1964,7 @@ static void sign_define_multiple(list_T *l, list_T *retlist)
}
/// "sign_define()" function
-void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_define(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *name;
@@ -1996,7 +1995,7 @@ void f_sign_define(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_getdefined()" function
-void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_getdefined(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *name = NULL;
@@ -2010,7 +2009,7 @@ void f_sign_getdefined(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_getplaced()" function
-void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_getplaced(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
buf_T *buf = NULL;
dict_T *dict;
@@ -2068,7 +2067,7 @@ void f_sign_getplaced(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_jump()" function
-void f_sign_jump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_jump(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int sign_id;
char *sign_group = NULL;
@@ -2226,7 +2225,7 @@ cleanup:
}
/// "sign_place()" function
-void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_place(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
dict_T *dict = NULL;
@@ -2244,7 +2243,7 @@ void f_sign_place(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_placelist()" function. Place multiple signs.
-void f_sign_placelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_placelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int sign_id;
@@ -2284,7 +2283,7 @@ static void sign_undefine_multiple(list_T *l, list_T *retlist)
}
/// "sign_undefine()" function
-void f_sign_undefine(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_undefine(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *name;
@@ -2374,7 +2373,7 @@ cleanup:
}
/// "sign_unplace()" function
-void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_unplace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
dict_T *dict = NULL;
@@ -2397,7 +2396,7 @@ void f_sign_unplace(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "sign_unplacelist()" function
-void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_sign_unplacelist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
int retval;
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index e4ece71846..a4fb325ec8 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -33,15 +33,11 @@ struct sign_entry {
};
/// Sign attributes. Used by the screen refresh routines.
-typedef struct sign_attrs_S {
- int sat_typenr;
- char_u *sat_text;
- int sat_texthl;
- int sat_linehl;
- int sat_culhl;
- int sat_numhl;
- int sat_prio; // Used for inserting extmark signs
-} sign_attrs_T;
+typedef struct {
+ char_u *text;
+ int hl_attr_id;
+ int priority;
+} SignTextAttrs;
#define SIGN_SHOW_MAX 9
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index ceb35af4b8..1259736e0e 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -56,71 +56,59 @@
// Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a
// specific word.
-// Use this to adjust the score after finding suggestions, based on the
-// suggested word sounding like the bad word. This is much faster than doing
-// it for every possible suggestion.
-// Disadvantage: When "the" is typed as "hte" it sounds quite different ("@"
-// vs "ht") and goes down in the list.
-// Used when 'spellsuggest' is set to "best".
-#define RESCORE(word_score, sound_score) ((3 * (word_score) + (sound_score)) / 4)
-
-// Do the opposite: based on a maximum end score and a known sound score,
-// compute the maximum word score that can be used.
-#define MAXSCORE(word_score, sound_score) ((4 * (word_score) - (sound_score)) / 3)
-
-#include <assert.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wctype.h>
-
-// for offsetof()
-#include <stddef.h>
-
-#include "nvim/ascii.h"
-#include "nvim/buffer.h"
-#include "nvim/change.h"
-#include "nvim/charset.h"
-#include "nvim/cursor.h"
-#include "nvim/edit.h"
-#include "nvim/eval.h"
-#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/fileio.h"
-#include "nvim/func_attr.h"
-#include "nvim/garray.h"
-#include "nvim/getchar.h"
-#include "nvim/hashtab.h"
-#include "nvim/input.h"
-#include "nvim/insexpand.h"
-#include "nvim/mark.h"
-#include "nvim/mbyte.h"
-#include "nvim/memline.h"
-#include "nvim/memory.h"
-#include "nvim/message.h"
-#include "nvim/normal.h"
-#include "nvim/option.h"
-#include "nvim/os/input.h"
-#include "nvim/os/os.h"
-#include "nvim/os_unix.h"
-#include "nvim/path.h"
-#include "nvim/regexp.h"
-#include "nvim/screen.h"
-#include "nvim/search.h"
-#include "nvim/spell.h"
-#include "nvim/spellfile.h"
-#include "nvim/strings.h"
-#include "nvim/syntax.h"
-#include "nvim/ui.h"
-#include "nvim/undo.h"
-
-// only used for su_badflags
-#define WF_MIXCAP 0x20 // mix of upper and lower case: macaRONI
-
-#define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP)
+#include <assert.h> // for assert
+#include <inttypes.h> // for uint32_t, uint16_t, uint8_t
+#include <limits.h> // for INT_MAX
+#include <stdbool.h> // for false, true, bool
+#include <stddef.h> // for NULL, size_t, ptrdiff_t
+#include <stdio.h> // for snprintf
+#include <string.h> // for memmove, strstr, memcpy, memset
+
+#include "nvim/ascii.h" // for NUL, ascii_isdigit, ascii_iswhite
+#include "nvim/autocmd.h" // for apply_autocmds
+#include "nvim/buffer.h" // for bufref_valid, set_bufref, buf_is_empty
+#include "nvim/buffer_defs.h" // for win_T, synblock_T, buf_T, w_p_...
+#include "nvim/change.h" // for changed_bytes
+#include "nvim/charset.h" // for skipwhite, getwhitecols, skipbin
+#include "nvim/cursor.h" // for get_cursor_line_ptr
+#include "nvim/drawscreen.h" // for NOT_VALID, redraw_later
+#include "nvim/eval/typval.h" // for semsg
+#include "nvim/ex_cmds.h" // for do_sub_msg
+#include "nvim/ex_cmds_defs.h" // for exarg_T
+#include "nvim/ex_docmd.h" // for do_cmdline_cmd
+#include "nvim/garray.h" // for garray_T, GA_EMPTY, GA_APPEND_...
+#include "nvim/gettext.h" // for _, N_
+#include "nvim/hashtab.h" // for hash_clear_all, hash_init, has...
+#include "nvim/highlight_defs.h" // for HLF_COUNT, hlf_T, HLF_SPB, HLF...
+#include "nvim/insexpand.h" // for ins_compl_add_infercase, ins_c...
+#include "nvim/log.h" // for ELOG
+#include "nvim/macros.h" // for MB_PTR_ADV, MB_PTR_BACK, ASCII...
+#include "nvim/mark.h" // for clearpos
+#include "nvim/mbyte.h" // for utf_ptr2char, utf_char2bytes
+#include "nvim/memline.h" // for ml_append, ml_get_buf, ml_close
+#include "nvim/memline_defs.h" // for memline_T
+#include "nvim/memory.h" // for xfree, xmalloc, xcalloc, xstrdup
+#include "nvim/message.h" // for emsg, msg_puts, give_warning
+#include "nvim/option.h" // for copy_option_part, set_option_v...
+#include "nvim/option_defs.h" // for p_ws, OPT_LOCAL, p_enc, SHM_SE...
+#include "nvim/os/fs.h" // for os_remove
+#include "nvim/os/input.h" // for line_breakcheck
+#include "nvim/os/os_defs.h" // for MAXPATHL
+#include "nvim/path.h" // for path_full_compare, path_tail...
+#include "nvim/pos.h" // for pos_T, colnr_T, linenr_T
+#include "nvim/regexp.h" // for vim_regfree, vim_regexec, vim_...
+#include "nvim/regexp_defs.h" // for regmatch_T, regprog_T
+#include "nvim/runtime.h" // for DIP_ALL, do_in_runtimepath
+#include "nvim/search.h" // for SEARCH_KEEP, for do_search
+#include "nvim/spell.h" // for FUNC_ATTR_NONNULL_ALL, FUNC_AT...
+#include "nvim/spell_defs.h" // for slang_T, langp_T, MAXWLEN, sal...
+#include "nvim/spellfile.h" // for spell_load_file
+#include "nvim/spellsuggest.h" // for spell_suggest_list
+#include "nvim/strings.h" // for vim_strchr, vim_snprintf, conc...
+#include "nvim/syntax.h" // for syn_get_id, syntax_present
+#include "nvim/types.h" // for char_u
+#include "nvim/undo.h" // for u_save_cursor
+#include "nvim/vim.h" // for curwin, STRLEN, STRLCPY, STRNCMP
// Result values. Lower number is accepted over higher one.
#define SP_BANNED (-1)
@@ -136,104 +124,6 @@ slang_T *first_lang = NULL;
// file used for "zG" and "zW"
char_u *int_wordlist = NULL;
-typedef struct wordcount_S {
- uint16_t wc_count; // nr of times word was seen
- char_u wc_word[1]; // word, actually longer
-} wordcount_T;
-
-#define WC_KEY_OFF offsetof(wordcount_T, wc_word)
-#define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF))
-#define MAXWORDCOUNT 0xffff
-
-// Information used when looking for suggestions.
-typedef struct suginfo_S {
- garray_T su_ga; // suggestions, contains "suggest_T"
- int su_maxcount; // max. number of suggestions displayed
- int su_maxscore; // maximum score for adding to su_ga
- int su_sfmaxscore; // idem, for when doing soundfold words
- garray_T su_sga; // like su_ga, sound-folded scoring
- char_u *su_badptr; // start of bad word in line
- int su_badlen; // length of detected bad word in line
- int su_badflags; // caps flags for bad word
- char_u su_badword[MAXWLEN]; // bad word truncated at su_badlen
- char_u su_fbadword[MAXWLEN]; // su_badword case-folded
- char_u su_sal_badword[MAXWLEN]; // su_badword soundfolded
- hashtab_T su_banned; // table with banned words
- slang_T *su_sallang; // default language for sound folding
-} suginfo_T;
-
-// One word suggestion. Used in "si_ga".
-typedef struct {
- char_u *st_word; // suggested word, allocated string
- int st_wordlen; // STRLEN(st_word)
- int st_orglen; // length of replaced text
- int st_score; // lower is better
- int st_altscore; // used when st_score compares equal
- bool st_salscore; // st_score is for soundalike
- bool st_had_bonus; // bonus already included in score
- slang_T *st_slang; // language used for sound folding
-} suggest_T;
-
-#define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i])
-
-// True if a word appears in the list of banned words.
-#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&(su)->su_banned, word)))
-
-// Number of suggestions kept when cleaning up. We need to keep more than
-// what is displayed, because when rescore_suggestions() is called the score
-// may change and wrong suggestions may be removed later.
-#define SUG_CLEAN_COUNT(su) ((su)->su_maxcount < \
- 130 ? 150 : (su)->su_maxcount + 20)
-
-// Threshold for sorting and cleaning up suggestions. Don't want to keep lots
-// of suggestions that are not going to be displayed.
-#define SUG_MAX_COUNT(su) (SUG_CLEAN_COUNT(su) + 50)
-
-// score for various changes
-#define SCORE_SPLIT 149 // split bad word
-#define SCORE_SPLIT_NO 249 // split bad word with NOSPLITSUGS
-#define SCORE_ICASE 52 // slightly different case
-#define SCORE_REGION 200 // word is for different region
-#define SCORE_RARE 180 // rare word
-#define SCORE_SWAP 75 // swap two characters
-#define SCORE_SWAP3 110 // swap two characters in three
-#define SCORE_REP 65 // REP replacement
-#define SCORE_SUBST 93 // substitute a character
-#define SCORE_SIMILAR 33 // substitute a similar character
-#define SCORE_SUBCOMP 33 // substitute a composing character
-#define SCORE_DEL 94 // delete a character
-#define SCORE_DELDUP 66 // delete a duplicated character
-#define SCORE_DELCOMP 28 // delete a composing character
-#define SCORE_INS 96 // insert a character
-#define SCORE_INSDUP 67 // insert a duplicate character
-#define SCORE_INSCOMP 30 // insert a composing character
-#define SCORE_NONWORD 103 // change non-word to word char
-
-#define SCORE_FILE 30 // suggestion from a file
-#define SCORE_MAXINIT 350 // Initial maximum score: higher == slower.
- // 350 allows for about three changes.
-
-#define SCORE_COMMON1 30 // subtracted for words seen before
-#define SCORE_COMMON2 40 // subtracted for words often seen
-#define SCORE_COMMON3 50 // subtracted for words very often seen
-#define SCORE_THRES2 10 // word count threshold for COMMON2
-#define SCORE_THRES3 100 // word count threshold for COMMON3
-
-// When trying changed soundfold words it becomes slow when trying more than
-// two changes. With less than two changes it's slightly faster but we miss a
-// few good suggestions. In rare cases we need to try three of four changes.
-#define SCORE_SFMAX1 200 // maximum score for first try
-#define SCORE_SFMAX2 300 // maximum score for second try
-#define SCORE_SFMAX3 400 // maximum score for third try
-
-#define SCORE_BIG (SCORE_INS * 3) // big difference
-#define SCORE_MAXMAX 999999 // accept any score
-#define SCORE_LIMITMAX 350 // for spell_edit_score_limit()
-
-// for spell_edit_score_limit() we need to know the minimum value of
-// SCORE_ICASE, SCORE_SWAP, SCORE_DEL, SCORE_SIMILAR and SCORE_INS
-#define SCORE_EDIT_MIN SCORE_SIMILAR
-
// Structure to store info for word matching.
typedef struct matchinf_S {
langp_T *mi_lp; // info for language and region
@@ -289,38 +179,10 @@ typedef struct syl_item_S {
spelltab_T spelltab;
int did_set_spelltab;
-// structure used to store soundfolded words that add_sound_suggest() has
-// handled already.
-typedef struct {
- int16_t sft_score; // lowest score used
- char_u sft_word[1]; // soundfolded word, actually longer
-} sftword_T;
-
-typedef struct {
- int badi;
- int goodi;
- int score;
-} limitscore_T;
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "spell.c.generated.h"
#endif
-// values for ts_isdiff
-#define DIFF_NONE 0 // no different byte (yet)
-#define DIFF_YES 1 // different byte found
-#define DIFF_INSERT 2 // inserting character
-
-// values for ts_flags
-#define TSF_PREFIXOK 1 // already checked that prefix is OK
-#define TSF_DIDSPLIT 2 // tried split at this point
-#define TSF_DIDDEL 4 // did a delete, "ts_delidx" has index
-
-// special values ts_prefixdepth
-#define PFD_NOPREFIX 0xff // not using prefixes
-#define PFD_PREFIXTREE 0xfe // walking through the prefix tree
-#define PFD_NOTSPECIAL 0xfd // highest value that's not special
-
// mode values for find_word
#define FIND_FOLDWORD 0 // find word case-folded
#define FIND_KEEPWORD 1 // find keep-case word
@@ -331,8 +193,8 @@ typedef struct {
char *e_format = N_("E759: Format error in spell file");
// Remember what "z?" replaced.
-static char_u *repl_from = NULL;
-static char_u *repl_to = NULL;
+char_u *repl_from = NULL;
+char_u *repl_to = NULL;
/// Main spell-checking function.
/// "ptr" points to a character that could be the start of a word.
@@ -356,9 +218,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
matchinf_T mi; // Most things are put in "mi" so that it can
// be passed to functions quickly.
size_t nrlen = 0; // found a number first
- int c;
size_t wrongcaplen = 0;
- int lpi;
bool count_word = docount;
bool use_camel_case = *wp->w_s->b_p_spo != NUL;
bool camel_case = false;
@@ -374,7 +234,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
return 1;
}
- memset(&mi, 0, sizeof(matchinf_T));
+ CLEAR_FIELD(mi);
// A number is always OK. Also skip hexadecimal numbers 0xFF99 and
// 0X99FF. But always do check spelling to find "3GPP" and "11
@@ -383,7 +243,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B')) {
mi.mi_end = (char_u *)skipbin((char *)ptr + 2);
} else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) {
- mi.mi_end = skiphex(ptr + 2);
+ mi.mi_end = (char_u *)skiphex((char *)ptr + 2);
} else {
mi.mi_end = (char_u *)skipdigits((char *)ptr);
}
@@ -397,7 +257,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
bool this_upper = false; // init for gcc
if (use_camel_case) {
- c = utf_ptr2char((char *)mi.mi_fend);
+ int c = utf_ptr2char((char *)mi.mi_fend);
this_upper = SPELL_ISUPPER(c);
}
@@ -405,7 +265,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
MB_PTR_ADV(mi.mi_fend);
if (use_camel_case) {
const bool prev_upper = this_upper;
- c = utf_ptr2char((char *)mi.mi_fend);
+ int c = utf_ptr2char((char *)mi.mi_fend);
this_upper = SPELL_ISUPPER(c);
camel_case = !prev_upper && this_upper;
}
@@ -414,7 +274,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) {
// Check word starting with capital letter.
- c = utf_ptr2char((char *)ptr);
+ int c = utf_ptr2char((char *)ptr);
if (!SPELL_ISUPPER(c)) {
wrongcaplen = (size_t)(mi.mi_fend - ptr);
}
@@ -455,7 +315,7 @@ size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docou
// Loop over the languages specified in 'spelllang'.
// We check them all, because a word may be matched longer in another
// language.
- for (lpi = 0; lpi < wp->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < wp->w_s->b_langp.ga_len; lpi++) {
mi.mi_lp = LANGP_ENTRY(wp->w_s->b_langp, lpi);
// If reloading fails the language is still in the list but everything
@@ -615,7 +475,6 @@ static void find_word(matchinf_T *mip, int mode)
int endlen[MAXWLEN]; // length at possible word endings
idx_T endidx[MAXWLEN]; // possible word endings
int endidxcnt = 0;
- int len;
int c;
// Repeat advancing in the tree until:
@@ -627,7 +486,7 @@ static void find_word(matchinf_T *mip, int mode)
flen = fold_more(mip);
}
- len = byts[arridx++];
+ int len = byts[arridx++];
// If the first possible byte is a zero the word could end here.
// Remember this index, we first check for the longest word.
@@ -639,13 +498,13 @@ static void find_word(matchinf_T *mip, int mode)
}
endlen[endidxcnt] = wlen;
endidx[endidxcnt++] = arridx++;
- --len;
+ len--;
// Skip over the zeros, there can be several flag/region
// combinations.
while (len > 0 && byts[arridx] == 0) {
- ++arridx;
- --len;
+ arridx++;
+ len--;
}
if (len == 0) {
break; // no children, word must end here
@@ -683,8 +542,8 @@ static void find_word(matchinf_T *mip, int mode)
// Continue at the child (if there is one).
arridx = idxs[lo];
- ++wlen;
- --flen;
+ wlen++;
+ flen--;
// One space in the good word may stand for several spaces in the
// checked word.
@@ -696,8 +555,8 @@ static void find_word(matchinf_T *mip, int mode)
if (ptr[wlen] != ' ' && ptr[wlen] != TAB) {
break;
}
- ++wlen;
- --flen;
+ wlen++;
+ flen--;
}
}
}
@@ -708,7 +567,7 @@ static void find_word(matchinf_T *mip, int mode)
// Verify that one of the possible endings is valid. Try the longest
// first.
while (endidxcnt > 0) {
- --endidxcnt;
+ endidxcnt--;
arridx = endidx[endidxcnt];
wlen = endlen[endidxcnt];
@@ -744,7 +603,7 @@ static void find_word(matchinf_T *mip, int mode)
// prefix ID.
// Repeat this if there are more flags/region alternatives until there
// is a match.
- for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; len--, arridx++) {
+ for (int len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; len--, arridx++) {
uint32_t flags = (uint32_t)idxs[arridx];
// For the fold-case tree check that the case of the checked word
@@ -763,11 +622,10 @@ static void find_word(matchinf_T *mip, int mode)
|| !spell_valid_case(mip->mi_capflags, (int)flags)) {
continue;
}
- }
- // When mode is FIND_PREFIX the word must support the prefix:
- // check the prefix ID and the condition. Do that for the list at
- // mip->mi_prefarridx that find_prefix() filled.
- else if (mode == FIND_PREFIX && !prefix_found) {
+ } else if (mode == FIND_PREFIX && !prefix_found) {
+ // When mode is FIND_PREFIX the word must support the prefix:
+ // check the prefix ID and the condition. Do that for the list at
+ // mip->mi_prefarridx that find_prefix() filled.
c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx,
(int)flags,
mip->mi_word + mip->mi_cprefixlen, slang,
@@ -899,9 +757,8 @@ static void find_word(matchinf_T *mip, int mode)
// COMPOUNDRULE, discard the compounded word.
continue;
}
- }
- // Check NEEDCOMPOUND: can't use word without compounding.
- else if (flags & WF_NEEDCOMP) {
+ } else if (flags & WF_NEEDCOMP) {
+ // skip if word is only valid in a compound
continue;
}
@@ -939,14 +796,14 @@ static void find_word(matchinf_T *mip, int mode)
#if 0
c = mip->mi_compoff;
#endif
- ++mip->mi_complen;
+ mip->mi_complen++;
if (flags & WF_COMPROOT) {
- ++mip->mi_compextra;
+ mip->mi_compextra++;
}
// For NOBREAK we need to try all NOBREAK languages, at least
// to find the ".add" file(s).
- for (int lpi = 0; lpi < mip->mi_win->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < mip->mi_win->w_s->b_langp.ga_len; lpi++) {
if (slang->sl_nobreak) {
mip->mi_lp = LANGP_ENTRY(mip->mi_win->w_s->b_langp, lpi);
if (mip->mi_lp->lp_slang->sl_fidxs == NULL
@@ -980,9 +837,9 @@ static void find_word(matchinf_T *mip, int mode)
break;
}
}
- --mip->mi_complen;
+ mip->mi_complen--;
if (flags & WF_COMPROOT) {
- --mip->mi_compextra;
+ mip->mi_compextra--;
}
mip->mi_lp = save_lp;
@@ -1050,18 +907,15 @@ static void find_word(matchinf_T *mip, int mode)
/// end of ptr[wlen] and the second part matches after it.
///
/// @param gap &sl_comppat
-static bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
+bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
{
- char_u *p;
- int len;
-
for (int i = 0; i + 1 < gap->ga_len; i += 2) {
- p = ((char_u **)gap->ga_data)[i + 1];
+ char_u *p = ((char_u **)gap->ga_data)[i + 1];
if (STRNCMP(ptr + wlen, p, STRLEN(p)) == 0) {
// Second part matches at start of following compound word, now
// check if first part matches at end of previous word.
p = ((char_u **)gap->ga_data)[i];
- len = (int)STRLEN(p);
+ int len = (int)STRLEN(p);
if (len <= wlen && STRNCMP(ptr + wlen - len, p, len) == 0) {
return true;
}
@@ -1072,7 +926,7 @@ static bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap)
// Returns true if "flags" is a valid sequence of compound flags and "word"
// does not have too many syllables.
-static bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
+bool can_compound(slang_T *slang, const char_u *word, const char_u *flags)
FUNC_ATTR_NONNULL_ALL
{
char_u uflags[MAXWLEN * 2] = { 0 };
@@ -1101,48 +955,18 @@ static bool can_compound(slang_T *slang, const char_u *word, const char_u *flags
return true;
}
-// Returns true when the sequence of flags in "compflags" plus "flag" can
-// possibly form a valid compounded word. This also checks the COMPOUNDRULE
-// lines if they don't contain wildcards.
-static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, int flag)
-{
- // If the flag doesn't appear in sl_compstartflags or sl_compallflags
- // then it can't possibly compound.
- if (!byte_in_str(sp->ts_complen == sp->ts_compsplit
- ? slang->sl_compstartflags : slang->sl_compallflags, flag)) {
- return false;
- }
-
- // If there are no wildcards, we can check if the flags collected so far
- // possibly can form a match with COMPOUNDRULE patterns. This only
- // makes sense when we have two or more words.
- if (slang->sl_comprules != NULL && sp->ts_complen > sp->ts_compsplit) {
- compflags[sp->ts_complen] = (char_u)flag;
- compflags[sp->ts_complen + 1] = NUL;
- bool v = match_compoundrule(slang, compflags + sp->ts_compsplit);
- compflags[sp->ts_complen] = NUL;
- return v;
- }
-
- return true;
-}
-
// Returns true if the compound flags in compflags[] match the start of any
// compound rule. This is used to stop trying a compound if the flags
// collected so far can't possibly match any compound rule.
// Caller must check that slang->sl_comprules is not NULL.
-static bool match_compoundrule(slang_T *slang, char_u *compflags)
+bool match_compoundrule(slang_T *slang, char_u *compflags)
{
- char_u *p;
- int i;
- int c;
-
// loop over all the COMPOUNDRULE entries
- for (p = slang->sl_comprules; *p != NUL; ++p) {
+ for (char_u *p = slang->sl_comprules; *p != NUL; p++) {
// loop over the flags in the compound word we have made, match
// them against the current rule entry
- for (i = 0;; ++i) {
- c = compflags[i];
+ for (int i = 0;; i++) {
+ int c = compflags[i];
if (c == NUL) {
// found a rule that matches for the flags we have so far
return true;
@@ -1154,7 +978,7 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags)
bool match = false;
// compare against all the flags in []
- ++p;
+ p++;
while (*p != ']' && *p != NUL) {
if (*p++ == c) {
match = true;
@@ -1166,7 +990,7 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags)
} else if (*p != c) {
break; // flag of word doesn't match flag in pattern
}
- ++p;
+ p++;
}
// Skip to the next "/", where the next pattern starts.
@@ -1188,15 +1012,12 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags)
/// @param totprefcnt nr of prefix IDs
/// @param arridx idx in sl_pidxs[]
/// @param cond_req only use prefixes with a condition
-static int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang,
- bool cond_req)
+int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang,
+ bool cond_req)
{
- int prefcnt;
- int pidx;
-
int prefid = (int)((unsigned)flags >> 24);
- for (prefcnt = totprefcnt - 1; prefcnt >= 0; prefcnt--) {
- pidx = slang->sl_pidxs[arridx + prefcnt];
+ for (int prefcnt = totprefcnt - 1; prefcnt >= 0; prefcnt--) {
+ int pidx = slang->sl_pidxs[arridx + prefcnt];
// Check the prefix ID.
if (prefid != (pidx & 0xff)) {
@@ -1236,30 +1057,23 @@ static int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word
static void find_prefix(matchinf_T *mip, int mode)
{
idx_T arridx = 0;
- int len;
int wlen = 0;
- int flen;
- int c;
- char_u *ptr;
- idx_T lo, hi, m;
slang_T *slang = mip->mi_lp->lp_slang;
- char_u *byts;
- idx_T *idxs;
- byts = slang->sl_pbyts;
+ char_u *byts = slang->sl_pbyts;
if (byts == NULL) {
return; // array is empty
}
// We use the case-folded word here, since prefixes are always
// case-folded.
- ptr = mip->mi_fword;
- flen = mip->mi_fwordlen; // available case-folded bytes
+ char_u *ptr = mip->mi_fword;
+ int flen = mip->mi_fwordlen; // available case-folded bytes
if (mode == FIND_COMPOUND) {
// Skip over the previously found word(s).
ptr += mip->mi_compoff;
flen -= mip->mi_compoff;
}
- idxs = slang->sl_pidxs;
+ idx_T *idxs = slang->sl_pidxs;
// Repeat advancing in the tree until:
// - there is a byte that doesn't match,
@@ -1270,7 +1084,7 @@ static void find_prefix(matchinf_T *mip, int mode)
flen = fold_more(mip);
}
- len = byts[arridx++];
+ int len = byts[arridx++];
// If the first possible byte is a zero the prefix could end here.
// Check if the following word matches and supports the prefix.
@@ -1282,8 +1096,8 @@ static void find_prefix(matchinf_T *mip, int mode)
mip->mi_prefarridx = arridx;
mip->mi_prefcnt = len;
while (len > 0 && byts[arridx] == 0) {
- ++arridx;
- --len;
+ arridx++;
+ len--;
}
mip->mi_prefcnt -= len;
@@ -1310,11 +1124,11 @@ static void find_prefix(matchinf_T *mip, int mode)
}
// Perform a binary search in the list of accepted bytes.
- c = ptr[wlen];
- lo = arridx;
- hi = arridx + len - 1;
+ int c = ptr[wlen];
+ idx_T lo = arridx;
+ idx_T hi = arridx + len - 1;
while (lo < hi) {
- m = (lo + hi) / 2;
+ idx_T m = (lo + hi) / 2;
if (byts[m] > c) {
hi = m - 1;
} else if (byts[m] < c) {
@@ -1332,8 +1146,8 @@ static void find_prefix(matchinf_T *mip, int mode)
// Continue at the child (if there is one).
arridx = idxs[lo];
- ++wlen;
- --flen;
+ wlen++;
+ flen--;
}
}
@@ -1342,10 +1156,7 @@ static void find_prefix(matchinf_T *mip, int mode)
// Return the length of the folded chars in bytes.
static int fold_more(matchinf_T *mip)
{
- int flen;
- char_u *p;
-
- p = mip->mi_fend;
+ char_u *p = mip->mi_fend;
do {
MB_PTR_ADV(mip->mi_fend);
} while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_win));
@@ -1358,7 +1169,7 @@ static int fold_more(matchinf_T *mip)
(void)spell_casefold(mip->mi_win, p, (int)(mip->mi_fend - p),
mip->mi_fword + mip->mi_fwordlen,
MAXWLEN - mip->mi_fwordlen);
- flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen);
+ int flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen);
mip->mi_fwordlen += flen;
return flen;
}
@@ -1368,7 +1179,7 @@ static int fold_more(matchinf_T *mip)
///
/// @param wordflags Flags for the checked word.
/// @param treeflags Flags for the word in the spell tree.
-static bool spell_valid_case(int wordflags, int treeflags)
+bool spell_valid_case(int wordflags, int treeflags)
{
return (wordflags == WF_ALLCAP && (treeflags & WF_FIXCAP) == 0)
|| ((treeflags & (WF_ALLCAP | WF_KEEPCAP)) == 0
@@ -1377,7 +1188,7 @@ static bool spell_valid_case(int wordflags, int treeflags)
}
// Returns true if spell checking is not enabled.
-static bool no_spell_checking(win_T *wp)
+bool no_spell_checking(win_T *wp)
{
if (!wp->w_p_spell || *wp->w_s->b_p_spl == NUL
|| GA_EMPTY(&wp->w_s->b_langp)) {
@@ -1400,17 +1211,12 @@ static bool no_spell_checking(win_T *wp)
/// @return 0 if not found, length of the badly spelled word otherwise.
size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *attrp)
{
- linenr_T lnum;
pos_T found_pos;
size_t found_len = 0;
- char_u *line;
- char_u *p;
- char_u *endp;
hlf_T attr = HLF_COUNT;
size_t len;
int has_syntax = syntax_present(wp);
int col;
- bool can_spell;
char_u *buf = NULL;
size_t buflen = 0;
int skip = 0;
@@ -1431,11 +1237,11 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
// We concatenate the start of the next line, so that wrapped words work
// (e.g. "et<line-break>cetera"). Doesn't work when searching backwards
// though...
- lnum = wp->w_cursor.lnum;
+ linenr_T lnum = wp->w_cursor.lnum;
clearpos(&found_pos);
while (!got_int) {
- line = ml_get_buf(wp->w_buffer, lnum, false);
+ char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
len = STRLEN(line);
if (buflen < len + MAXWLEN + 2) {
@@ -1452,10 +1258,10 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
// For checking first word with a capital skip white space.
if (capcol == 0) {
- capcol = (int)getwhitecols(line);
+ capcol = (int)getwhitecols((char *)line);
} else if (curline && wp == curwin) {
// For spellbadword(): check if first word needs a capital.
- col = (int)getwhitecols(line);
+ col = (int)getwhitecols((char *)line);
if (check_need_cap(lnum, col)) {
capcol = col;
}
@@ -1475,8 +1281,8 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
ml_get_buf(wp->w_buffer, lnum + 1, false),
MAXWLEN);
}
- p = buf + skip;
- endp = buf + len;
+ char_u *p = buf + skip;
+ char_u *endp = buf + len;
while (p < endp) {
// When searching backward don't search after the cursor. Unless
// we wrapped around the end of the buffer.
@@ -1502,10 +1308,11 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
|| ((colnr_T)(curline
? p - buf + (ptrdiff_t)len
: p - buf) > wp->w_cursor.col)) {
+ bool can_spell;
if (has_syntax) {
col = (int)(p - buf);
(void)syn_get_id(wp, lnum, (colnr_T)col,
- FALSE, &can_spell, FALSE);
+ false, &can_spell, false);
if (!can_spell) {
attr = HLF_COUNT;
}
@@ -1515,9 +1322,11 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
if (can_spell) {
found_one = true;
- found_pos.lnum = lnum;
- found_pos.col = (int)(p - buf);
- found_pos.coladd = 0;
+ found_pos = (pos_T) {
+ .lnum = lnum,
+ .col = (int)(p - buf),
+ .coladd = 0
+ };
if (dir == FORWARD) {
// No need to search further.
wp->w_cursor = found_pos;
@@ -1581,7 +1390,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
capcol = -1;
} else {
if (lnum < wp->w_buffer->b_ml.ml_line_count) {
- ++lnum;
+ lnum++;
} else if (!p_ws) {
break; // at first line and 'nowrapscan'
} else {
@@ -1609,7 +1418,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
}
// Capcol skips over the inserted space.
- --capcol;
+ capcol--;
// But after empty line check first word in next line
if (empty_line) {
@@ -1630,10 +1439,7 @@ size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *att
// to skip those bytes if the word was OK.
void spell_cat_line(char_u *buf, char_u *line, int maxlen)
{
- char_u *p;
- int n;
-
- p = (char_u *)skipwhite((char *)line);
+ char_u *p = (char_u *)skipwhite((char *)line);
while (vim_strchr("*#/\"\t", *p) != NULL) {
p = (char_u *)skipwhite((char *)p + 1);
}
@@ -1641,7 +1447,7 @@ void spell_cat_line(char_u *buf, char_u *line, int maxlen)
if (*p != NUL) {
// Only worth concatenating if there is something else than spaces to
// concatenate.
- n = (int)(p - line) + 1;
+ int n = (int)(p - line) + 1;
if (n < maxlen - 1) {
memset(buf, ' ', (size_t)n);
STRLCPY(buf + n, p, maxlen - n);
@@ -1656,7 +1462,6 @@ static void spell_load_lang(char_u *lang)
char fname_enc[85];
int r;
spelload_T sl;
- int round;
// Copy the language name to pass it to spell_load_cb() as a cookie.
// It's truncated when an error is detected.
@@ -1666,7 +1471,7 @@ static void spell_load_lang(char_u *lang)
// We may retry when no spell file is found for the language, an
// autocommand may load it then.
- for (round = 1; round <= 2; ++round) {
+ for (int round = 1; round <= 2; round++) {
// Find the first spell file for "lang" in 'runtimepath' and load it.
vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5,
"spell/%s.%s.spl", lang, spell_enc());
@@ -1713,7 +1518,7 @@ static void spell_load_lang(char_u *lang)
char_u *spell_enc(void)
{
if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) {
- return p_enc;
+ return (char_u *)p_enc;
}
return (char_u *)"latin1";
}
@@ -1797,7 +1602,7 @@ void slang_clear(slang_T *lp)
GA_DEEP_CLEAR(gap, salitem_T, free_salitem);
}
- for (int i = 0; i < lp->sl_prefixcnt; ++i) {
+ for (int i = 0; i < lp->sl_prefixcnt; i++) {
vim_regfree(lp->sl_prefprog[i]);
}
lp->sl_prefixcnt = 0;
@@ -1846,9 +1651,7 @@ void slang_clear_sug(slang_T *lp)
static void spell_load_cb(char *fname, void *cookie)
{
spelload_T *slp = (spelload_T *)cookie;
- slang_T *slang;
-
- slang = spell_load_file((char_u *)fname, slp->sl_lang, NULL, false);
+ slang_T *slang = spell_load_file((char_u *)fname, slp->sl_lang, NULL, false);
if (slang != NULL) {
// When a previously loaded file has NOBREAK also use it for the
// ".add" files.
@@ -1871,9 +1674,6 @@ static void spell_load_cb(char *fname, void *cookie)
/// @param[in] count 1 to count once, 10 to init
void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count)
{
- hash_T hash;
- hashitem_T *hi;
- wordcount_T *wc;
char_u buf[MAXWLEN];
char_u *p;
@@ -1886,9 +1686,10 @@ void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count)
p = buf;
}
- hash = hash_hash(p);
+ wordcount_T *wc;
+ hash_T hash = hash_hash(p);
const size_t p_len = STRLEN(p);
- hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash);
+ hashitem_T *hi = hash_lookup(&lp->sl_wordcount, (const char *)p, p_len, hash);
if (HASHITEM_EMPTY(hi)) {
wc = xmalloc(sizeof(wordcount_T) + p_len);
memcpy(wc->wc_word, p, p_len + 1);
@@ -1903,45 +1704,11 @@ void count_common_word(slang_T *lp, char_u *word, int len, uint8_t count)
}
}
-/// Adjust the score of common words.
-///
-/// @param split word was split, less bonus
-static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool split)
-{
- wordcount_T *wc;
- int bonus;
- int newscore;
-
- hashitem_T *hi = hash_find(&slang->sl_wordcount, (char *)word);
- if (!HASHITEM_EMPTY(hi)) {
- wc = HI2WC(hi);
- if (wc->wc_count < SCORE_THRES2) {
- bonus = SCORE_COMMON1;
- } else if (wc->wc_count < SCORE_THRES3) {
- bonus = SCORE_COMMON2;
- } else {
- bonus = SCORE_COMMON3;
- }
- if (split) {
- newscore = score - bonus / 2;
- } else {
- newscore = score - bonus;
- }
- if (newscore < 0) {
- return 0;
- }
- return newscore;
- }
- return score;
-}
-
// Returns true if byte "n" appears in "str".
// Like strchr() but independent of locale.
bool byte_in_str(char_u *str, int n)
{
- char_u *p;
-
- for (p = str; *p != NUL; ++p) {
+ for (char_u *p = str; *p != NUL; p++) {
if (*p == n) {
return true;
}
@@ -1953,19 +1720,16 @@ bool byte_in_str(char_u *str, int n)
// in "slang->sl_syl_items".
int init_syl_tab(slang_T *slang)
{
- char_u *p;
- char_u *s;
- int l;
-
ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4);
- p = (char_u *)vim_strchr((char *)slang->sl_syllable, '/');
+ char_u *p = (char_u *)vim_strchr((char *)slang->sl_syllable, '/');
while (p != NULL) {
*p++ = NUL;
if (*p == NUL) { // trailing slash
break;
}
- s = p;
+ char_u *s = p;
p = (char_u *)vim_strchr((char *)p, '/');
+ int l;
if (p == NULL) {
l = (int)STRLEN(s);
} else {
@@ -1991,8 +1755,6 @@ static int count_syllables(slang_T *slang, const char_u *word)
int cnt = 0;
bool skip = false;
int len;
- syl_item_T *syl;
- int c;
if (slang->sl_syllable == NULL) {
return 0;
@@ -2008,24 +1770,24 @@ static int count_syllables(slang_T *slang, const char_u *word)
// Find longest match of syllable items.
len = 0;
- for (int i = 0; i < slang->sl_syl_items.ga_len; ++i) {
- syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i;
+ for (int i = 0; i < slang->sl_syl_items.ga_len; i++) {
+ syl_item_T *syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i;
if (syl->sy_len > len
&& STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) {
len = syl->sy_len;
}
}
if (len != 0) { // found a match, count syllable
- ++cnt;
+ cnt++;
skip = false;
} else {
// No recognized syllable item, at least a syllable char then?
- c = utf_ptr2char((char *)p);
+ int c = utf_ptr2char((char *)p);
len = utfc_ptr2len((char *)p);
if (vim_strchr((char *)slang->sl_syllable, c) == NULL) {
skip = false; // No, search for next syllable
} else if (!skip) {
- ++cnt; // Yes, count it
+ cnt++; // Yes, count it
skip = true; // don't count following syllable chars
}
}
@@ -2033,12 +1795,12 @@ static int count_syllables(slang_T *slang, const char_u *word)
return cnt;
}
-// Parse 'spelllang' and set w_s->b_langp accordingly.
-// Returns NULL if it's OK, an error message otherwise.
+/// Parse 'spelllang' and set w_s->b_langp accordingly.
+/// @return NULL if it's OK, an untranslated error message otherwise.
char *did_set_spelllang(win_T *wp)
{
garray_T ga;
- char_u *splp;
+ char *splp;
char_u *region;
char_u region_cp[3];
bool filename;
@@ -2050,7 +1812,7 @@ char *did_set_spelllang(win_T *wp)
int len;
char_u *p;
int round;
- char_u *spf;
+ char *spf;
char_u *use_region = NULL;
bool dont_use_region = false;
bool nobreak = false;
@@ -2075,18 +1837,18 @@ char *did_set_spelllang(win_T *wp)
// Make a copy of 'spelllang', the SpellFileMissing autocommands may change
// it under our fingers.
- spl_copy = vim_strsave(wp->w_s->b_p_spl);
+ spl_copy = vim_strsave((char_u *)wp->w_s->b_p_spl);
wp->w_s->b_cjk = 0;
// Loop over comma separated language names.
- for (splp = spl_copy; *splp != NUL;) {
+ for (splp = (char *)spl_copy; *splp != NUL;) {
// Get one language name.
- copy_option_part((char **)&splp, (char *)lang, MAXWLEN, ",");
+ copy_option_part(&splp, (char *)lang, MAXWLEN, ",");
region = NULL;
len = (int)STRLEN(lang);
- if (!valid_spelllang(lang)) {
+ if (!valid_spelllang((char *)lang)) {
continue;
}
@@ -2114,7 +1876,7 @@ char *did_set_spelllang(win_T *wp)
// Check if we loaded this language before.
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare((char *)lang, (char *)slang->sl_fname, false, true)
+ if (path_full_compare((char *)lang, slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -2163,7 +1925,7 @@ char *did_set_spelllang(win_T *wp)
// Loop over the languages, there can be several files for "lang".
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
if (filename
- ? path_full_compare((char *)lang, (char *)slang->sl_fname, false, true) == kEqualFiles
+ ? path_full_compare((char *)lang, slang->sl_fname, false, true) == kEqualFiles
: STRICMP(lang, slang->sl_name) == 0) {
region_mask = REGION_ALL;
if (!filename && region != NULL) {
@@ -2205,7 +1967,7 @@ char *did_set_spelllang(win_T *wp)
// round 2: load second name in 'spellfile.
// etc.
spf = curwin->w_s->b_p_spf;
- for (round = 0; round == 0 || *spf != NUL; ++round) {
+ for (round = 0; round == 0 || *spf != NUL; round++) {
if (round == 0) {
// Internal wordlist, if there is one.
if (int_wordlist == NULL) {
@@ -2214,12 +1976,12 @@ char *did_set_spelllang(win_T *wp)
int_wordlist_spl(spf_name);
} else {
// One entry in 'spellfile'.
- copy_option_part((char **)&spf, (char *)spf_name, MAXPATHL - 5, ",");
+ copy_option_part(&spf, (char *)spf_name, MAXPATHL - 5, ",");
STRCAT(spf_name, ".spl");
// If it was already found above then skip it.
- for (c = 0; c < ga.ga_len; ++c) {
- p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
+ for (c = 0; c < ga.ga_len; c++) {
+ p = (char_u *)LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
if (p != NULL
&& path_full_compare((char *)spf_name, (char *)p, false, true) == kEqualFiles) {
break;
@@ -2232,7 +1994,7 @@ char *did_set_spelllang(win_T *wp)
// Check if it was loaded already.
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare((char *)spf_name, (char *)slang->sl_fname, false, true)
+ if (path_full_compare((char *)spf_name, slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
@@ -2290,7 +2052,7 @@ char *did_set_spelllang(win_T *wp)
// For each language figure out what language to use for sound folding and
// REP items. If the language doesn't support it itself use another one
// with the same name. E.g. for "en-math" use "en".
- for (int i = 0; i < ga.ga_len; ++i) {
+ for (int i = 0; i < ga.ga_len; i++) {
lp = LANGP_ENTRY(ga, i);
// sound folding
@@ -2299,7 +2061,7 @@ char *did_set_spelllang(win_T *wp)
lp->lp_sallang = lp->lp_slang;
} else {
// find first similar language that does sound folding
- for (int j = 0; j < ga.ga_len; ++j) {
+ for (int j = 0; j < ga.ga_len; j++) {
lp2 = LANGP_ENTRY(ga, j);
if (!GA_EMPTY(&lp2->lp_slang->sl_sal)
&& STRNCMP(lp->lp_slang->sl_name,
@@ -2316,7 +2078,7 @@ char *did_set_spelllang(win_T *wp)
lp->lp_replang = lp->lp_slang;
} else {
// find first similar language that has REP items
- for (int j = 0; j < ga.ga_len; ++j) {
+ for (int j = 0; j < ga.ga_len; j++) {
lp2 = LANGP_ENTRY(ga, j);
if (!GA_EMPTY(&lp2->lp_slang->sl_rep)
&& STRNCMP(lp->lp_slang->sl_name,
@@ -2327,7 +2089,7 @@ char *did_set_spelllang(win_T *wp)
}
}
}
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
theend:
xfree(spl_copy);
@@ -2338,7 +2100,7 @@ theend:
// Clear the midword characters for buffer "buf".
static void clear_midword(win_T *wp)
{
- memset(wp->w_s->b_spell_ismw, 0, 256);
+ CLEAR_FIELD(wp->w_s->b_spell_ismw);
XFREE_CLEAR(wp->w_s->b_spell_ismw_mb);
}
@@ -2358,13 +2120,13 @@ static void use_midword(slang_T *lp, win_T *wp)
wp->w_s->b_spell_ismw[c] = true;
} else if (wp->w_s->b_spell_ismw_mb == NULL) {
// First multi-byte char in "b_spell_ismw_mb".
- wp->w_s->b_spell_ismw_mb = vim_strnsave(p, (size_t)l);
+ wp->w_s->b_spell_ismw_mb = (char *)vim_strnsave(p, (size_t)l);
} else {
// Append multi-byte chars to "b_spell_ismw_mb".
const int n = (int)STRLEN(wp->w_s->b_spell_ismw_mb);
- char_u *bp = vim_strnsave(wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l);
+ char_u *bp = vim_strnsave((char_u *)wp->w_s->b_spell_ismw_mb, (size_t)n + (size_t)l);
xfree(wp->w_s->b_spell_ismw_mb);
- wp->w_s->b_spell_ismw_mb = bp;
+ wp->w_s->b_spell_ismw_mb = (char *)bp;
STRLCPY(bp + n, p, l + 1);
}
p += l;
@@ -2403,9 +2165,6 @@ int captype(char_u *word, char_u *end)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *p;
- int firstcap;
- bool allcap;
- bool past_second = false; // past second word char
// find first letter
for (p = word; !spell_iswordp_nmw(p, curwin); MB_PTR_ADV(p)) {
@@ -2414,7 +2173,9 @@ int captype(char_u *word, char_u *end)
}
}
int c = mb_ptr2char_adv((const char_u **)&p);
- firstcap = allcap = SPELL_ISUPPER(c);
+ bool allcap;
+ bool firstcap = allcap = SPELL_ISUPPER(c);
+ bool past_second = false; // past second word char
// Need to check all letters to find a word with mixed upper/lower.
// But a word with an upper char only at start is a ONECAP.
@@ -2444,57 +2205,11 @@ int captype(char_u *word, char_u *end)
return 0;
}
-// Like captype() but for a KEEPCAP word add ONECAP if the word starts with a
-// capital. So that make_case_word() can turn WOrd into Word.
-// Add ALLCAP for "WOrD".
-static int badword_captype(char_u *word, char_u *end)
- FUNC_ATTR_NONNULL_ALL
-{
- int flags = captype(word, end);
- int c;
- int l, u;
- bool first;
- char_u *p;
-
- if (flags & WF_KEEPCAP) {
- // Count the number of UPPER and lower case letters.
- l = u = 0;
- first = false;
- for (p = word; p < end; MB_PTR_ADV(p)) {
- c = utf_ptr2char((char *)p);
- if (SPELL_ISUPPER(c)) {
- ++u;
- if (p == word) {
- first = true;
- }
- } else {
- ++l;
- }
- }
-
- // If there are more UPPER than lower case letters suggest an
- // ALLCAP word. Otherwise, if the first letter is UPPER then
- // suggest ONECAP. Exception: "ALl" most likely should be "All",
- // require three upper case letters.
- if (u > l && u > 2) {
- flags |= WF_ALLCAP;
- } else if (first) {
- flags |= WF_ONECAP;
- }
-
- if (u >= 2 && l >= 2) { // maCARONI maCAroni
- flags |= WF_MIXCAP;
- }
- }
- return flags;
-}
-
// Delete the internal wordlist and its .spl file.
void spell_delete_wordlist(void)
{
- char_u fname[MAXPATHL] = { 0 };
-
if (int_wordlist != NULL) {
+ char_u fname[MAXPATHL] = { 0 };
os_remove((char *)int_wordlist);
int_wordlist_spl(fname);
os_remove((char *)fname);
@@ -2505,15 +2220,13 @@ void spell_delete_wordlist(void)
// Free all languages.
void spell_free_all(void)
{
- slang_T *slang;
-
// Go through all buffers and handle 'spelllang'. <VN>
FOR_ALL_BUFFERS(buf) {
ga_clear(&buf->b_s.b_langp);
}
while (first_lang != NULL) {
- slang = first_lang;
+ slang_T *slang = first_lang;
first_lang = slang->sl_next;
slang_free(slang);
}
@@ -2547,36 +2260,6 @@ void spell_reload(void)
}
}
-// Opposite of offset2bytes().
-// "pp" points to the bytes and is advanced over it.
-// Returns the offset.
-static int bytes2offset(char_u **pp)
-{
- char_u *p = *pp;
- int nr;
- int c;
-
- c = *p++;
- if ((c & 0x80) == 0x00) { // 1 byte
- nr = c - 1;
- } else if ((c & 0xc0) == 0x80) { // 2 bytes
- nr = (c & 0x3f) - 1;
- nr = nr * 255 + (*p++ - 1);
- } else if ((c & 0xe0) == 0xc0) { // 3 bytes
- nr = (c & 0x1f) - 1;
- nr = nr * 255 + (*p++ - 1);
- nr = nr * 255 + (*p++ - 1);
- } else { // 4 bytes
- nr = (c & 0x0f) - 1;
- nr = nr * 255 + (*p++ - 1);
- nr = nr * 255 + (*p++ - 1);
- nr = nr * 255 + (*p++ - 1);
- }
-
- *pp = p;
- return nr;
-}
-
// Open a spell buffer. This is a nameless buffer that is not in the buffer
// list and only contains text lines. Can use a swapfile to reduce memory
// use.
@@ -2600,7 +2283,7 @@ buf_T *open_spellbuf(void)
void close_spellbuf(buf_T *buf)
{
if (buf != NULL) {
- ml_close(buf, TRUE);
+ ml_close(buf, true);
xfree(buf);
}
}
@@ -2608,28 +2291,26 @@ void close_spellbuf(buf_T *buf)
// Init the chartab used for spelling for ASCII.
void clear_spell_chartab(spelltab_T *sp)
{
- int i;
+ // Init everything to false (zero).
+ CLEAR_FIELD(sp->st_isw);
+ CLEAR_FIELD(sp->st_isu);
- // Init everything to false.
- memset(sp->st_isw, false, sizeof(sp->st_isw));
- memset(sp->st_isu, false, sizeof(sp->st_isu));
-
- for (i = 0; i < 256; i++) {
+ for (int i = 0; i < 256; i++) {
sp->st_fold[i] = (char_u)i;
sp->st_upper[i] = (char_u)i;
}
// We include digits. A word shouldn't start with a digit, but handling
// that is done separately.
- for (i = '0'; i <= '9'; ++i) {
+ for (int i = '0'; i <= '9'; i++) {
sp->st_isw[i] = true;
}
- for (i = 'A'; i <= 'Z'; ++i) {
+ for (int i = 'A'; i <= 'Z'; i++) {
sp->st_isw[i] = true;
sp->st_isu[i] = true;
sp->st_fold[i] = (char_u)(i + 0x20);
}
- for (i = 'a'; i <= 'z'; ++i) {
+ for (int i = 'a'; i <= 'z'; i++) {
sp->st_isw[i] = true;
sp->st_upper[i] = (char_u)(i - 0x20);
}
@@ -2641,11 +2322,9 @@ void clear_spell_chartab(spelltab_T *sp)
// locale. For utf-8 we don't use isalpha() but our own functions.
void init_spell_chartab(void)
{
- int i;
-
did_set_spelltab = false;
clear_spell_chartab(&spelltab);
- for (i = 128; i < 256; i++) {
+ for (int i = 128; i < 256; i++) {
int f = utf_fold(i);
int u = mb_toupper(i);
@@ -2665,11 +2344,9 @@ void init_spell_chartab(void)
/// Thus this only works properly when past the first character of the word.
///
/// @param wp Buffer used.
-static bool spell_iswordp(const char_u *p, const win_T *wp)
+bool spell_iswordp(const char_u *p, const win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- int c;
-
const int l = utfc_ptr2len((char *)p);
const char_u *s = p;
if (l == 1) {
@@ -2678,16 +2355,16 @@ static bool spell_iswordp(const char_u *p, const win_T *wp)
s = p + 1; // skip a mid-word character
}
} else {
- c = utf_ptr2char((char *)p);
+ int c = utf_ptr2char((char *)p);
if (c < 256
? wp->w_s->b_spell_ismw[c]
: (wp->w_s->b_spell_ismw_mb != NULL
- && vim_strchr((char *)wp->w_s->b_spell_ismw_mb, c) != NULL)) {
+ && vim_strchr(wp->w_s->b_spell_ismw_mb, c) != NULL)) {
s = p + l;
}
}
- c = utf_ptr2char((char *)s);
+ int c = utf_ptr2char((char *)s);
if (c > 255) {
return spell_mb_isword_class(mb_get_class(s), wp);
}
@@ -2728,7 +2405,7 @@ static bool spell_iswordp_w(const int *p, const win_T *wp)
if (*p <
256 ? wp->w_s->b_spell_ismw[*p] : (wp->w_s->b_spell_ismw_mb != NULL
- && vim_strchr((char *)wp->w_s->b_spell_ismw_mb,
+ && vim_strchr(wp->w_s->b_spell_ismw_mb,
*p) != NULL)) {
s = p + 1;
} else {
@@ -2783,313 +2460,20 @@ int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int bufle
return OK;
}
-// values for sps_flags
-#define SPS_BEST 1
-#define SPS_FAST 2
-#define SPS_DOUBLE 4
-
-static int sps_flags = SPS_BEST; // flags from 'spellsuggest'
-static int sps_limit = 9999; // max nr of suggestions given
-
-// Check the 'spellsuggest' option. Return FAIL if it's wrong.
-// Sets "sps_flags" and "sps_limit".
-int spell_check_sps(void)
-{
- char_u *p;
- char_u *s;
- char_u buf[MAXPATHL];
- int f;
-
- sps_flags = 0;
- sps_limit = 9999;
-
- for (p = p_sps; *p != NUL;) {
- copy_option_part((char **)&p, (char *)buf, MAXPATHL, ",");
-
- f = 0;
- if (ascii_isdigit(*buf)) {
- s = buf;
- sps_limit = getdigits_int((char **)&s, true, 0);
- if (*s != NUL && !ascii_isdigit(*s)) {
- f = -1;
- }
- } else if (STRCMP(buf, "best") == 0) {
- f = SPS_BEST;
- } else if (STRCMP(buf, "fast") == 0) {
- f = SPS_FAST;
- } else if (STRCMP(buf, "double") == 0) {
- f = SPS_DOUBLE;
- } else if (STRNCMP(buf, "expr:", 5) != 0
- && STRNCMP(buf, "file:", 5) != 0) {
- f = -1;
- }
-
- if (f == -1 || (sps_flags != 0 && f != 0)) {
- sps_flags = SPS_BEST;
- sps_limit = 9999;
- return FAIL;
- }
- if (f != 0) {
- sps_flags = f;
- }
- }
-
- if (sps_flags == 0) {
- sps_flags = SPS_BEST;
- }
-
- return OK;
-}
-
-// "z=": Find badly spelled word under or after the cursor.
-// Give suggestions for the properly spelled word.
-// In Visual mode use the highlighted word as the bad word.
-// When "count" is non-zero use that suggestion.
-void spell_suggest(int count)
-{
- char_u *line;
- pos_T prev_cursor = curwin->w_cursor;
- char_u wcopy[MAXWLEN + 2];
- char_u *p;
- int c;
- suginfo_T sug;
- suggest_T *stp;
- int mouse_used;
- int need_cap;
- int limit;
- int selected = count;
- int badlen = 0;
- int msg_scroll_save = msg_scroll;
- const int wo_spell_save = curwin->w_p_spell;
-
- if (!curwin->w_p_spell) {
- did_set_spelllang(curwin);
- curwin->w_p_spell = true;
- }
-
- if (*curwin->w_s->b_p_spl == NUL) {
- emsg(_(e_no_spell));
- return;
- }
-
- if (VIsual_active) {
- // Use the Visually selected text as the bad word. But reject
- // a multi-line selection.
- if (curwin->w_cursor.lnum != VIsual.lnum) {
- vim_beep(BO_SPELL);
- return;
- }
- badlen = (int)curwin->w_cursor.col - (int)VIsual.col;
- if (badlen < 0) {
- badlen = -badlen;
- } else {
- curwin->w_cursor.col = VIsual.col;
- }
- badlen++;
- end_visual_mode();
- } else
- // Find the start of the badly spelled word.
- if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0
- || curwin->w_cursor.col > prev_cursor.col) {
- // No bad word or it starts after the cursor: use the word under the
- // cursor.
- curwin->w_cursor = prev_cursor;
- line = get_cursor_line_ptr();
- p = line + curwin->w_cursor.col;
- // Backup to before start of word.
- while (p > line && spell_iswordp_nmw(p, curwin)) {
- MB_PTR_BACK(line, p);
- }
- // Forward to start of word.
- while (*p != NUL && !spell_iswordp_nmw(p, curwin)) {
- MB_PTR_ADV(p);
- }
-
- if (!spell_iswordp_nmw(p, curwin)) { // No word found.
- beep_flush();
- return;
- }
- curwin->w_cursor.col = (colnr_T)(p - line);
- }
-
- // Get the word and its length.
-
- // Figure out if the word should be capitalised.
- need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col);
-
- // Make a copy of current line since autocommands may free the line.
- line = vim_strsave(get_cursor_line_ptr());
-
- // Get the list of suggestions. Limit to 'lines' - 2 or the number in
- // 'spellsuggest', whatever is smaller.
- if (sps_limit > Rows - 2) {
- limit = Rows - 2;
- } else {
- limit = sps_limit;
- }
- spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
- true, need_cap, true);
-
- if (GA_EMPTY(&sug.su_ga)) {
- msg(_("Sorry, no suggestions"));
- } else if (count > 0) {
- if (count > sug.su_ga.ga_len) {
- smsg(_("Sorry, only %" PRId64 " suggestions"),
- (int64_t)sug.su_ga.ga_len);
- }
- } else {
- // When 'rightleft' is set the list is drawn right-left.
- cmdmsg_rl = curwin->w_p_rl;
- if (cmdmsg_rl) {
- msg_col = Columns - 1;
- }
-
- // List the suggestions.
- msg_start();
- msg_row = Rows - 1; // for when 'cmdheight' > 1
- lines_left = Rows; // avoid more prompt
- vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
- sug.su_badlen, sug.su_badptr);
- if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) {
- // And now the rabbit from the high hat: Avoid showing the
- // untranslated message rightleft.
- vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC",
- sug.su_badlen, sug.su_badptr);
- }
- msg_puts((const char *)IObuff);
- msg_clr_eos();
- msg_putchar('\n');
-
- msg_scroll = TRUE;
- for (int i = 0; i < sug.su_ga.ga_len; ++i) {
- stp = &SUG(sug.su_ga, i);
-
- // The suggested word may replace only part of the bad word, add
- // the not replaced part. But only when it's not getting too long.
- STRLCPY(wcopy, stp->st_word, MAXWLEN + 1);
- int el = sug.su_badlen - stp->st_orglen;
- if (el > 0 && stp->st_wordlen + el <= MAXWLEN) {
- STRLCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, el + 1);
- }
- vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1);
- if (cmdmsg_rl) {
- rl_mirror(IObuff);
- }
- msg_puts((const char *)IObuff);
-
- vim_snprintf((char *)IObuff, IOSIZE, " \"%s\"", wcopy);
- msg_puts((const char *)IObuff);
-
- // The word may replace more than "su_badlen".
- if (sug.su_badlen < stp->st_orglen) {
- vim_snprintf((char *)IObuff, IOSIZE, _(" < \"%.*s\""),
- stp->st_orglen, sug.su_badptr);
- msg_puts((const char *)IObuff);
- }
-
- if (p_verbose > 0) {
- // Add the score.
- if (sps_flags & (SPS_DOUBLE | SPS_BEST)) {
- vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)",
- stp->st_salscore ? "s " : "",
- stp->st_score, stp->st_altscore);
- } else {
- vim_snprintf((char *)IObuff, IOSIZE, " (%d)",
- stp->st_score);
- }
- if (cmdmsg_rl) {
- // Mirror the numbers, but keep the leading space.
- rl_mirror(IObuff + 1);
- }
- msg_advance(30);
- msg_puts((const char *)IObuff);
- }
- msg_putchar('\n');
- }
-
- cmdmsg_rl = FALSE;
- msg_col = 0;
- // Ask for choice.
- selected = prompt_for_number(&mouse_used);
-
- if (ui_has(kUIMessages)) {
- ui_call_msg_clear();
- }
-
- if (mouse_used) {
- selected -= lines_left;
- }
- lines_left = Rows; // avoid more prompt
- // don't delay for 'smd' in normal_cmd()
- msg_scroll = msg_scroll_save;
- }
-
- if (selected > 0 && selected <= sug.su_ga.ga_len && u_save_cursor() == OK) {
- // Save the from and to text for :spellrepall.
- XFREE_CLEAR(repl_from);
- XFREE_CLEAR(repl_to);
-
- stp = &SUG(sug.su_ga, selected - 1);
- if (sug.su_badlen > stp->st_orglen) {
- // Replacing less than "su_badlen", append the remainder to
- // repl_to.
- repl_from = vim_strnsave(sug.su_badptr, (size_t)sug.su_badlen);
- vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word,
- sug.su_badlen - stp->st_orglen,
- sug.su_badptr + stp->st_orglen);
- repl_to = vim_strsave(IObuff);
- } else {
- // Replacing su_badlen or more, use the whole word.
- repl_from = vim_strnsave(sug.su_badptr, (size_t)stp->st_orglen);
- repl_to = vim_strsave(stp->st_word);
- }
-
- // Replace the word.
- p = xmalloc(STRLEN(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1);
- c = (int)(sug.su_badptr - line);
- memmove(p, line, (size_t)c);
- STRCPY(p + c, stp->st_word);
- STRCAT(p, sug.su_badptr + stp->st_orglen);
-
- // For redo we use a change-word command.
- ResetRedobuff();
- AppendToRedobuff("ciw");
- AppendToRedobuffLit((char *)p + c,
- stp->st_wordlen + sug.su_badlen - stp->st_orglen);
- AppendCharToRedobuff(ESC);
-
- // "p" may be freed here
- ml_replace(curwin->w_cursor.lnum, (char *)p, false);
- curwin->w_cursor.col = c;
-
- inserted_bytes(curwin->w_cursor.lnum, c, stp->st_orglen, stp->st_wordlen);
- } else {
- curwin->w_cursor = prev_cursor;
- }
-
- spell_find_cleanup(&sug);
- xfree(line);
- curwin->w_p_spell = wo_spell_save;
-}
-
// Check if the word at line "lnum" column "col" is required to start with a
// capital. This uses 'spellcapcheck' of the current buffer.
-static bool check_need_cap(linenr_T lnum, colnr_T col)
+bool check_need_cap(linenr_T lnum, colnr_T col)
{
bool need_cap = false;
- char_u *line;
- char_u *line_copy = NULL;
- char_u *p;
- colnr_T endcol;
- regmatch_T regmatch;
if (curwin->w_s->b_cap_prog == NULL) {
return false;
}
- line = get_cursor_line_ptr();
- endcol = 0;
- if (getwhitecols(line) >= (int)col) {
+ char_u *line = get_cursor_line_ptr();
+ char_u *line_copy = NULL;
+ colnr_T endcol = 0;
+ if (getwhitecols((char *)line) >= (int)col) {
// At start of line, check if previous line is empty or sentence
// ends there.
if (lnum == 1) {
@@ -3100,7 +2484,7 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
need_cap = true;
} else {
// Append a space in place of the line break.
- line_copy = concat_str(line, (char_u *)" ");
+ line_copy = (char_u *)concat_str((char *)line, " ");
line = line_copy;
endcol = (colnr_T)STRLEN(line);
}
@@ -3111,9 +2495,11 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
if (endcol > 0) {
// Check if sentence ends before the bad word.
- regmatch.regprog = curwin->w_s->b_cap_prog;
- regmatch.rm_ic = FALSE;
- p = line + endcol;
+ regmatch_T regmatch = {
+ .regprog = curwin->w_s->b_cap_prog,
+ .rm_ic = false
+ };
+ char_u *p = line + endcol;
for (;;) {
MB_PTR_BACK(line, p);
if (p == line || spell_iswordp_nmw(p, curwin)) {
@@ -3137,10 +2523,6 @@ static bool check_need_cap(linenr_T lnum, colnr_T col)
void ex_spellrepall(exarg_T *eap)
{
pos_T pos = curwin->w_cursor;
- char_u *frompat;
- int addlen;
- char_u *line;
- char_u *p;
bool save_ws = p_ws;
linenr_T prev_lnum = 0;
@@ -3148,10 +2530,11 @@ void ex_spellrepall(exarg_T *eap)
emsg(_("E752: No previous spell replacement"));
return;
}
- addlen = (int)(STRLEN(repl_to) - STRLEN(repl_from));
+ int addlen = (int)(STRLEN(repl_to) - STRLEN(repl_from));
- frompat = xmalloc(STRLEN(repl_from) + 7);
- sprintf((char *)frompat, "\\V\\<%s\\>", repl_from);
+ size_t frompatlen = STRLEN(repl_from) + 7;
+ char_u *frompat = xmalloc(frompatlen);
+ snprintf((char *)frompat, frompatlen, "\\V\\<%s\\>", repl_from);
p_ws = false;
sub_nsubs = 0;
@@ -3165,10 +2548,10 @@ void ex_spellrepall(exarg_T *eap)
// Only replace when the right word isn't there yet. This happens
// when changing "etc" to "etc.".
- line = get_cursor_line_ptr();
+ char_u *line = get_cursor_line_ptr();
if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col,
repl_to, STRLEN(repl_to)) != 0) {
- p = xmalloc(STRLEN(line) + (size_t)addlen + 1);
+ char_u *p = xmalloc(STRLEN(line) + (size_t)addlen + 1);
memmove(p, line, (size_t)curwin->w_cursor.col);
STRCPY(p + curwin->w_cursor.col, repl_to);
STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from));
@@ -3176,10 +2559,10 @@ void ex_spellrepall(exarg_T *eap)
changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col);
if (curwin->w_cursor.lnum != prev_lnum) {
- ++sub_nlines;
+ sub_nlines++;
prev_lnum = curwin->w_cursor.lnum;
}
- ++sub_nsubs;
+ sub_nsubs++;
}
curwin->w_cursor.col += (colnr_T)STRLEN(repl_to);
}
@@ -3195,336 +2578,6 @@ void ex_spellrepall(exarg_T *eap)
}
}
-/// Find spell suggestions for "word". Return them in the growarray "*gap" as
-/// a list of allocated strings.
-///
-/// @param maxcount maximum nr of suggestions
-/// @param need_cap 'spellcapcheck' matched
-void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap, bool interactive)
-{
- suginfo_T sug;
- suggest_T *stp;
- char_u *wcopy;
-
- spell_find_suggest(word, 0, &sug, maxcount, false, need_cap, interactive);
-
- // Make room in "gap".
- ga_init(gap, sizeof(char_u *), sug.su_ga.ga_len + 1);
- ga_grow(gap, sug.su_ga.ga_len);
- for (int i = 0; i < sug.su_ga.ga_len; ++i) {
- stp = &SUG(sug.su_ga, i);
-
- // The suggested word may replace only part of "word", add the not
- // replaced part.
- wcopy = xmalloc((size_t)stp->st_wordlen + STRLEN(sug.su_badptr + stp->st_orglen) + 1);
- STRCPY(wcopy, stp->st_word);
- STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen);
- ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy;
- }
-
- spell_find_cleanup(&sug);
-}
-
-/// Find spell suggestions for the word at the start of "badptr".
-/// Return the suggestions in "su->su_ga".
-/// The maximum number of suggestions is "maxcount".
-/// Note: does use info for the current window.
-/// This is based on the mechanisms of Aspell, but completely reimplemented.
-///
-/// @param badlen length of bad word or 0 if unknown
-/// @param banbadword don't include badword in suggestions
-/// @param need_cap word should start with capital
-static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int maxcount,
- bool banbadword, bool need_cap, bool interactive)
-{
- hlf_T attr = HLF_COUNT;
- char_u buf[MAXPATHL];
- char_u *p;
- bool do_combine = false;
- char_u *sps_copy;
- static bool expr_busy = false;
- int c;
- langp_T *lp;
- bool did_intern = false;
-
- // Set the info in "*su".
- memset(su, 0, sizeof(suginfo_T));
- ga_init(&su->su_ga, (int)sizeof(suggest_T), 10);
- ga_init(&su->su_sga, (int)sizeof(suggest_T), 10);
- if (*badptr == NUL) {
- return;
- }
- hash_init(&su->su_banned);
-
- su->su_badptr = badptr;
- if (badlen != 0) {
- su->su_badlen = badlen;
- } else {
- size_t tmplen = spell_check(curwin, su->su_badptr, &attr, NULL, false);
- assert(tmplen <= INT_MAX);
- su->su_badlen = (int)tmplen;
- }
- su->su_maxcount = maxcount;
- su->su_maxscore = SCORE_MAXINIT;
-
- if (su->su_badlen >= MAXWLEN) {
- su->su_badlen = MAXWLEN - 1; // just in case
- }
- STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
- (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword,
- MAXWLEN);
-
- // TODO(vim): make this work if the case-folded text is longer than the
- // original text. Currently an illegal byte causes wrong pointer
- // computations.
- su->su_fbadword[su->su_badlen] = NUL;
-
- // get caps flags for bad word
- su->su_badflags = badword_captype(su->su_badptr,
- su->su_badptr + su->su_badlen);
- if (need_cap) {
- su->su_badflags |= WF_ONECAP;
- }
-
- // Find the default language for sound folding. We simply use the first
- // one in 'spelllang' that supports sound folding. That's good for when
- // using multiple files for one language, it's not that bad when mixing
- // languages (e.g., "pl,en").
- for (int i = 0; i < curbuf->b_s.b_langp.ga_len; ++i) {
- lp = LANGP_ENTRY(curbuf->b_s.b_langp, i);
- if (lp->lp_sallang != NULL) {
- su->su_sallang = lp->lp_sallang;
- break;
- }
- }
-
- // Soundfold the bad word with the default sound folding, so that we don't
- // have to do this many times.
- if (su->su_sallang != NULL) {
- spell_soundfold(su->su_sallang, su->su_fbadword, true,
- su->su_sal_badword);
- }
-
- // If the word is not capitalised and spell_check() doesn't consider the
- // word to be bad then it might need to be capitalised. Add a suggestion
- // for that.
- c = utf_ptr2char((char *)su->su_badptr);
- if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) {
- make_case_word(su->su_badword, buf, WF_ONECAP);
- add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE,
- 0, true, su->su_sallang, false);
- }
-
- // Ban the bad word itself. It may appear in another region.
- if (banbadword) {
- add_banned(su, su->su_badword);
- }
-
- // Make a copy of 'spellsuggest', because the expression may change it.
- sps_copy = vim_strsave(p_sps);
-
- // Loop over the items in 'spellsuggest'.
- for (p = sps_copy; *p != NUL;) {
- copy_option_part((char **)&p, (char *)buf, MAXPATHL, ",");
-
- if (STRNCMP(buf, "expr:", 5) == 0) {
- // Evaluate an expression. Skip this when called recursively,
- // when using spellsuggest() in the expression.
- if (!expr_busy) {
- expr_busy = true;
- spell_suggest_expr(su, buf + 5);
- expr_busy = false;
- }
- } else if (STRNCMP(buf, "file:", 5) == 0) {
- // Use list of suggestions in a file.
- spell_suggest_file(su, buf + 5);
- } else if (!did_intern) {
- // Use internal method once.
- spell_suggest_intern(su, interactive);
- if (sps_flags & SPS_DOUBLE) {
- do_combine = true;
- }
- did_intern = true;
- }
- }
-
- xfree(sps_copy);
-
- if (do_combine) {
- // Combine the two list of suggestions. This must be done last,
- // because sorting changes the order again.
- score_combine(su);
- }
-}
-
-// Find suggestions by evaluating expression "expr".
-static void spell_suggest_expr(suginfo_T *su, char_u *expr)
-{
- int score;
- const char *p;
-
- // The work is split up in a few parts to avoid having to export
- // suginfo_T.
- // First evaluate the expression and get the resulting list.
- list_T *const list = eval_spell_expr((char *)su->su_badword, (char *)expr);
- if (list != NULL) {
- // Loop over the items in the list.
- TV_LIST_ITER(list, li, {
- if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
- // Get the word and the score from the items.
- score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p);
- if (score >= 0 && score <= su->su_maxscore) {
- add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen,
- score, 0, true, su->su_sallang, false);
- }
- }
- });
- tv_list_unref(list);
- }
-
- // Remove bogus suggestions, sort and truncate at "maxcount".
- check_suggestions(su, &su->su_ga);
- (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
-}
-
-// Find suggestions in file "fname". Used for "file:" in 'spellsuggest'.
-static void spell_suggest_file(suginfo_T *su, char_u *fname)
-{
- FILE *fd;
- char_u line[MAXWLEN * 2];
- char_u *p;
- int len;
- char_u cword[MAXWLEN];
-
- // Open the file.
- fd = os_fopen((char *)fname, "r");
- if (fd == NULL) {
- semsg(_(e_notopen), fname);
- return;
- }
-
- // Read it line by line.
- while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int) {
- line_breakcheck();
-
- p = (char_u *)vim_strchr((char *)line, '/');
- if (p == NULL) {
- continue; // No Tab found, just skip the line.
- }
- *p++ = NUL;
- if (STRICMP(su->su_badword, line) == 0) {
- // Match! Isolate the good word, until CR or NL.
- for (len = 0; p[len] >= ' '; len++) {}
- p[len] = NUL;
-
- // If the suggestion doesn't have specific case duplicate the case
- // of the bad word.
- if (captype(p, NULL) == 0) {
- make_case_word(p, cword, su->su_badflags);
- p = cword;
- }
-
- add_suggestion(su, &su->su_ga, p, su->su_badlen,
- SCORE_FILE, 0, true, su->su_sallang, false);
- }
- }
-
- fclose(fd);
-
- // Remove bogus suggestions, sort and truncate at "maxcount".
- check_suggestions(su, &su->su_ga);
- (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
-}
-
-// Find suggestions for the internal method indicated by "sps_flags".
-static void spell_suggest_intern(suginfo_T *su, bool interactive)
-{
- // Load the .sug file(s) that are available and not done yet.
- suggest_load_files();
-
- // 1. Try special cases, such as repeating a word: "the the" -> "the".
- //
- // Set a maximum score to limit the combination of operations that is
- // tried.
- suggest_try_special(su);
-
- // 2. Try inserting/deleting/swapping/changing a letter, use REP entries
- // from the .aff file and inserting a space (split the word).
- suggest_try_change(su);
-
- // For the resulting top-scorers compute the sound-a-like score.
- if (sps_flags & SPS_DOUBLE) {
- score_comp_sal(su);
- }
-
- // 3. Try finding sound-a-like words.
- if ((sps_flags & SPS_FAST) == 0) {
- if (sps_flags & SPS_BEST) {
- // Adjust the word score for the suggestions found so far for how
- // they sounds like.
- rescore_suggestions(su);
- }
-
- // While going through the soundfold tree "su_maxscore" is the score
- // for the soundfold word, limits the changes that are being tried,
- // and "su_sfmaxscore" the rescored score, which is set by
- // cleanup_suggestions().
- // First find words with a small edit distance, because this is much
- // faster and often already finds the top-N suggestions. If we didn't
- // find many suggestions try again with a higher edit distance.
- // "sl_sounddone" is used to avoid doing the same word twice.
- suggest_try_soundalike_prep();
- su->su_maxscore = SCORE_SFMAX1;
- su->su_sfmaxscore = SCORE_MAXINIT * 3;
- suggest_try_soundalike(su);
- if (su->su_ga.ga_len < SUG_CLEAN_COUNT(su)) {
- // We didn't find enough matches, try again, allowing more
- // changes to the soundfold word.
- su->su_maxscore = SCORE_SFMAX2;
- suggest_try_soundalike(su);
- if (su->su_ga.ga_len < SUG_CLEAN_COUNT(su)) {
- // Still didn't find enough matches, try again, allowing even
- // more changes to the soundfold word.
- su->su_maxscore = SCORE_SFMAX3;
- suggest_try_soundalike(su);
- }
- }
- su->su_maxscore = su->su_sfmaxscore;
- suggest_try_soundalike_finish();
- }
-
- // When CTRL-C was hit while searching do show the results. Only clear
- // got_int when using a command, not for spellsuggest().
- os_breakcheck();
- if (interactive && got_int) {
- (void)vgetc();
- got_int = FALSE;
- }
-
- if ((sps_flags & SPS_DOUBLE) == 0 && su->su_ga.ga_len != 0) {
- if (sps_flags & SPS_BEST) {
- // Adjust the word score for how it sounds like.
- rescore_suggestions(su);
- }
-
- // Remove bogus suggestions, sort and truncate at "maxcount".
- check_suggestions(su, &su->su_ga);
- (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
- }
-}
-
-// Free the info put in "*su" by spell_find_suggest().
-static void spell_find_cleanup(suginfo_T *su)
-{
-#define FREE_SUG_WORD(sug) xfree((sug)->st_word)
- // Free the suggestions.
- GA_DEEP_CLEAR(&su->su_ga, suggest_T, FREE_SUG_WORD);
- GA_DEEP_CLEAR(&su->su_sga, suggest_T, FREE_SUG_WORD);
-
- // Free the banned words.
- hash_clear_all(&su->su_banned, 0);
-}
-
/// Make a copy of "word", with the first letter upper or lower cased, to
/// "wcopy[MAXWLEN]". "word" must not be empty.
/// The result is NUL terminated.
@@ -3547,7 +2600,7 @@ void onecap_copy(char_u *word, char_u *wcopy, bool upper)
// Make a copy of "word" with all the letters upper cased into
// "wcopy[MAXWLEN]". The result is NUL terminated.
-static void allcap_copy(char_u *word, char_u *wcopy)
+void allcap_copy(char_u *word, char_u *wcopy)
{
char_u *d = wcopy;
for (char_u *s = word; *s != NUL;) {
@@ -3571,1383 +2624,9 @@ static void allcap_copy(char_u *word, char_u *wcopy)
*d = NUL;
}
-// Try finding suggestions by recognizing specific situations.
-static void suggest_try_special(suginfo_T *su)
-{
- int c;
- char_u word[MAXWLEN];
-
- // Recognize a word that is repeated: "the the".
- char_u *p = skiptowhite(su->su_fbadword);
- size_t len = (size_t)(p - su->su_fbadword);
- p = (char_u *)skipwhite((char *)p);
- if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) {
- // Include badflags: if the badword is onecap or allcap
- // use that for the goodword too: "The the" -> "The".
- c = su->su_fbadword[len];
- su->su_fbadword[len] = NUL;
- make_case_word(su->su_fbadword, word, su->su_badflags);
- su->su_fbadword[len] = (char_u)c;
-
- // Give a soundalike score of 0, compute the score as if deleting one
- // character.
- add_suggestion(su, &su->su_ga, word, su->su_badlen,
- RESCORE(SCORE_REP, 0), 0, true, su->su_sallang, false);
- }
-}
-
-// Measure how much time is spent in each state.
-// Output is dumped in "suggestprof".
-
-#ifdef SUGGEST_PROFILE
-proftime_T current;
-proftime_T total;
-proftime_T times[STATE_FINAL + 1];
-long counts[STATE_FINAL + 1];
-
-static void prof_init(void)
-{
- for (int i = 0; i <= STATE_FINAL; i++) {
- profile_zero(&times[i]);
- counts[i] = 0;
- }
- profile_start(&current);
- profile_start(&total);
-}
-
-// call before changing state
-static void prof_store(state_T state)
-{
- profile_end(&current);
- profile_add(&times[state], &current);
- counts[state]++;
- profile_start(&current);
-}
-# define PROF_STORE(state) prof_store(state);
-
-static void prof_report(char *name)
-{
- FILE *fd = fopen("suggestprof", "a");
-
- profile_end(&total);
- fprintf(fd, "-----------------------\n");
- fprintf(fd, "%s: %s\n", name, profile_msg(&total));
- for (int i = 0; i <= STATE_FINAL; i++) {
- fprintf(fd, "%d: %s ("%" PRId64)\n", i, profile_msg(&times[i]), counts[i]);
- }
- fclose(fd);
-}
-#else
-# define PROF_STORE(state)
-#endif
-
-// Try finding suggestions by adding/removing/swapping letters.
-
-static void suggest_try_change(suginfo_T *su)
-{
- char_u fword[MAXWLEN]; // copy of the bad word, case-folded
- int n;
- char_u *p;
- langp_T *lp;
-
- // We make a copy of the case-folded bad word, so that we can modify it
- // to find matches (esp. REP items). Append some more text, changing
- // chars after the bad word may help.
- STRCPY(fword, su->su_fbadword);
- n = (int)STRLEN(fword);
- p = su->su_badptr + su->su_badlen;
- (void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n);
-
- // Make sure the resulting text is not longer than the original text.
- n = (int)STRLEN(su->su_badptr);
- if (n < MAXWLEN) {
- fword[n] = NUL;
- }
-
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
- lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
-
- // If reloading a spell file fails it's still in the list but
- // everything has been cleared.
- if (lp->lp_slang->sl_fbyts == NULL) {
- continue;
- }
-
- // Try it for this language. Will add possible suggestions.
- //
-#ifdef SUGGEST_PROFILE
- prof_init();
-#endif
- suggest_trie_walk(su, lp, fword, false);
-#ifdef SUGGEST_PROFILE
- prof_report("try_change");
-#endif
- }
-}
-
-// Check the maximum score, if we go over it we won't try this change.
-#define TRY_DEEPER(su, stack, depth, add) \
- ((depth) < MAXWLEN - 1 && (stack)[depth].ts_score + (add) < (su)->su_maxscore)
-
-// Try finding suggestions by adding/removing/swapping letters.
-//
-// This uses a state machine. At each node in the tree we try various
-// operations. When trying if an operation works "depth" is increased and the
-// stack[] is used to store info. This allows combinations, thus insert one
-// character, replace one and delete another. The number of changes is
-// limited by su->su_maxscore.
-//
-// After implementing this I noticed an article by Kemal Oflazer that
-// describes something similar: "Error-tolerant Finite State Recognition with
-// Applications to Morphological Analysis and Spelling Correction" (1996).
-// The implementation in the article is simplified and requires a stack of
-// unknown depth. The implementation here only needs a stack depth equal to
-// the length of the word.
-//
-// This is also used for the sound-folded word, "soundfold" is true then.
-// The mechanism is the same, but we find a match with a sound-folded word
-// that comes from one or more original words. Each of these words may be
-// added, this is done by add_sound_suggest().
-// Don't use:
-// the prefix tree or the keep-case tree
-// "su->su_badlen"
-// anything to do with upper and lower case
-// anything to do with word or non-word characters ("spell_iswordp()")
-// banned words
-// word flags (rare, region, compounding)
-// word splitting for now
-// "similar_chars()"
-// use "slang->sl_repsal" instead of "lp->lp_replang->sl_rep"
-static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool soundfold)
-{
- char_u tword[MAXWLEN]; // good word collected so far
- trystate_T stack[MAXWLEN];
- char_u preword[MAXWLEN * 3] = { 0 }; // word found with proper case;
- // concatenation of prefix compound
- // words and split word. NUL terminated
- // when going deeper but not when coming
- // back.
- char_u compflags[MAXWLEN]; // compound flags, one for each word
- trystate_T *sp;
- int newscore;
- int score;
- char_u *byts, *fbyts, *pbyts;
- idx_T *idxs, *fidxs, *pidxs;
- int depth;
- int c, c2, c3;
- int n = 0;
- int flags;
- garray_T *gap;
- idx_T arridx;
- int len;
- char_u *p;
- fromto_T *ftp;
- int fl = 0, tl;
- int repextra = 0; // extra bytes in fword[] from REP item
- slang_T *slang = lp->lp_slang;
- int fword_ends;
- bool goodword_ends;
-#ifdef DEBUG_TRIEWALK
- // Stores the name of the change made at each level.
- char_u changename[MAXWLEN][80];
-#endif
- int breakcheckcount = 1000;
- bool compound_ok;
-
- // Go through the whole case-fold tree, try changes at each node.
- // "tword[]" contains the word collected from nodes in the tree.
- // "fword[]" the word we are trying to match with (initially the bad
- // word).
- depth = 0;
- sp = &stack[0];
- memset(sp, 0, sizeof(trystate_T)); // -V512
- sp->ts_curi = 1;
-
- if (soundfold) {
- // Going through the soundfold tree.
- byts = fbyts = slang->sl_sbyts;
- idxs = fidxs = slang->sl_sidxs;
- pbyts = NULL;
- pidxs = NULL;
- sp->ts_prefixdepth = PFD_NOPREFIX;
- sp->ts_state = STATE_START;
- } else {
- // When there are postponed prefixes we need to use these first. At
- // the end of the prefix we continue in the case-fold tree.
- fbyts = slang->sl_fbyts;
- fidxs = slang->sl_fidxs;
- pbyts = slang->sl_pbyts;
- pidxs = slang->sl_pidxs;
- if (pbyts != NULL) {
- byts = pbyts;
- idxs = pidxs;
- sp->ts_prefixdepth = PFD_PREFIXTREE;
- sp->ts_state = STATE_NOPREFIX; // try without prefix first
- } else {
- byts = fbyts;
- idxs = fidxs;
- sp->ts_prefixdepth = PFD_NOPREFIX;
- sp->ts_state = STATE_START;
- }
- }
-
- // The loop may take an indefinite amount of time. Break out after five
- // sectonds. TODO(vim): add an option for the time limit.
- proftime_T time_limit = profile_setlimit(5000);
-
- // Loop to find all suggestions. At each round we either:
- // - For the current state try one operation, advance "ts_curi",
- // increase "depth".
- // - When a state is done go to the next, set "ts_state".
- // - When all states are tried decrease "depth".
- while (depth >= 0 && !got_int) {
- sp = &stack[depth];
- switch (sp->ts_state) {
- case STATE_START:
- case STATE_NOPREFIX:
- // Start of node: Deal with NUL bytes, which means
- // tword[] may end here.
- arridx = sp->ts_arridx; // current node in the tree
- len = byts[arridx]; // bytes in this node
- arridx += sp->ts_curi; // index of current byte
-
- if (sp->ts_prefixdepth == PFD_PREFIXTREE) {
- // Skip over the NUL bytes, we use them later.
- for (n = 0; n < len && byts[arridx + n] == 0; n++) {}
- sp->ts_curi = (int16_t)(sp->ts_curi + n);
-
- // Always past NUL bytes now.
- n = (int)sp->ts_state;
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_ENDNUL;
- sp->ts_save_badflags = (char_u)su->su_badflags;
-
- // At end of a prefix or at start of prefixtree: check for
- // following word.
- if (depth < MAXWLEN - 1 && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
- // Set su->su_badflags to the caps type at this position.
- // Use the caps type until here for the prefix itself.
- n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
- flags = badword_captype(su->su_badptr, su->su_badptr + n);
- su->su_badflags = badword_captype(su->su_badptr + n,
- su->su_badptr + su->su_badlen);
-#ifdef DEBUG_TRIEWALK
- sprintf(changename[depth], "prefix");
-#endif
- go_deeper(stack, depth, 0);
- ++depth;
- sp = &stack[depth];
- sp->ts_prefixdepth = (char_u)(depth - 1);
- byts = fbyts;
- idxs = fidxs;
- sp->ts_arridx = 0;
-
- // Move the prefix to preword[] with the right case
- // and make find_keepcap_word() works.
- tword[sp->ts_twordlen] = NUL;
- make_case_word(tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen, flags);
- sp->ts_prewordlen = (char_u)STRLEN(preword);
- sp->ts_splitoff = sp->ts_twordlen;
- }
- break;
- }
-
- if (sp->ts_curi > len || byts[arridx] != 0) {
- // Past bytes in node and/or past NUL bytes.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_ENDNUL;
- sp->ts_save_badflags = (char_u)su->su_badflags;
- break;
- }
-
- // End of word in tree.
- ++sp->ts_curi; // eat one NUL byte
-
- flags = (int)idxs[arridx];
-
- // Skip words with the NOSUGGEST flag.
- if (flags & WF_NOSUGGEST) {
- break;
- }
-
- fword_ends = (fword[sp->ts_fidx] == NUL
- || (soundfold
- ? ascii_iswhite(fword[sp->ts_fidx])
- : !spell_iswordp(fword + sp->ts_fidx, curwin)));
- tword[sp->ts_twordlen] = NUL;
-
- if (sp->ts_prefixdepth <= PFD_NOTSPECIAL
- && (sp->ts_flags & TSF_PREFIXOK) == 0
- && pbyts != NULL) {
- // There was a prefix before the word. Check that the prefix
- // can be used with this word.
- // Count the length of the NULs in the prefix. If there are
- // none this must be the first try without a prefix.
- n = stack[sp->ts_prefixdepth].ts_arridx;
- len = pbyts[n++];
- for (c = 0; c < len && pbyts[n + c] == 0; c++) {}
- if (c > 0) {
- c = valid_word_prefix(c, n, flags,
- tword + sp->ts_splitoff, slang, false);
- if (c == 0) {
- break;
- }
-
- // Use the WF_RARE flag for a rare prefix.
- if (c & WF_RAREPFX) {
- flags |= WF_RARE;
- }
-
- // Tricky: when checking for both prefix and compounding
- // we run into the prefix flag first.
- // Remember that it's OK, so that we accept the prefix
- // when arriving at a compound flag.
- sp->ts_flags |= TSF_PREFIXOK;
- }
- }
-
- // Check NEEDCOMPOUND: can't use word without compounding. Do try
- // appending another compound word below.
- if (sp->ts_complen == sp->ts_compsplit && fword_ends
- && (flags & WF_NEEDCOMP)) {
- goodword_ends = false;
- } else {
- goodword_ends = true;
- }
-
- p = NULL;
- compound_ok = true;
- if (sp->ts_complen > sp->ts_compsplit) {
- if (slang->sl_nobreak) {
- // There was a word before this word. When there was no
- // change in this word (it was correct) add the first word
- // as a suggestion. If this word was corrected too, we
- // need to check if a correct word follows.
- if (sp->ts_fidx - sp->ts_splitfidx
- == sp->ts_twordlen - sp->ts_splitoff
- && STRNCMP(fword + sp->ts_splitfidx,
- tword + sp->ts_splitoff,
- sp->ts_fidx - sp->ts_splitfidx) == 0) {
- preword[sp->ts_prewordlen] = NUL;
- newscore = score_wordcount_adj(slang, sp->ts_score,
- preword + sp->ts_prewordlen,
- sp->ts_prewordlen > 0);
- // Add the suggestion if the score isn't too bad.
- if (newscore <= su->su_maxscore) {
- add_suggestion(su, &su->su_ga, preword,
- sp->ts_splitfidx - repextra,
- newscore, 0, false,
- lp->lp_sallang, false);
- }
- break;
- }
- } else {
- // There was a compound word before this word. If this
- // word does not support compounding then give up
- // (splitting is tried for the word without compound
- // flag).
- if (((unsigned)flags >> 24) == 0
- || sp->ts_twordlen - sp->ts_splitoff
- < slang->sl_compminlen) {
- break;
- }
- // For multi-byte chars check character length against
- // COMPOUNDMIN.
- if (slang->sl_compminlen > 0
- && mb_charlen(tword + sp->ts_splitoff)
- < slang->sl_compminlen) {
- break;
- }
-
- compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24);
- compflags[sp->ts_complen + 1] = NUL;
- STRLCPY(preword + sp->ts_prewordlen,
- tword + sp->ts_splitoff,
- sp->ts_twordlen - sp->ts_splitoff + 1);
-
- // Verify CHECKCOMPOUNDPATTERN rules.
- if (match_checkcompoundpattern(preword, sp->ts_prewordlen,
- &slang->sl_comppat)) {
- compound_ok = false;
- }
-
- if (compound_ok) {
- p = preword;
- while (*skiptowhite(p) != NUL) {
- p = (char_u *)skipwhite((char *)skiptowhite(p));
- }
- if (fword_ends && !can_compound(slang, p,
- compflags + sp->ts_compsplit)) {
- // Compound is not allowed. But it may still be
- // possible if we add another (short) word.
- compound_ok = false;
- }
- }
-
- // Get pointer to last char of previous word.
- p = preword + sp->ts_prewordlen;
- MB_PTR_BACK(preword, p);
- }
- }
-
- // Form the word with proper case in preword.
- // If there is a word from a previous split, append.
- // For the soundfold tree don't change the case, simply append.
- if (soundfold) {
- STRCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff);
- } else if (flags & WF_KEEPCAP) {
- // Must find the word in the keep-case tree.
- find_keepcap_word(slang, tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen);
- } else {
- // Include badflags: If the badword is onecap or allcap
- // use that for the goodword too. But if the badword is
- // allcap and it's only one char long use onecap.
- c = su->su_badflags;
- if ((c & WF_ALLCAP)
- && su->su_badlen ==
- utfc_ptr2len((char *)su->su_badptr)) {
- c = WF_ONECAP;
- }
- c |= flags;
-
- // When appending a compound word after a word character don't
- // use Onecap.
- if (p != NULL && spell_iswordp_nmw(p, curwin)) {
- c &= ~WF_ONECAP;
- }
- make_case_word(tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen, c);
- }
-
- if (!soundfold) {
- // Don't use a banned word. It may appear again as a good
- // word, thus remember it.
- if (flags & WF_BANNED) {
- add_banned(su, preword + sp->ts_prewordlen);
- break;
- }
- if ((sp->ts_complen == sp->ts_compsplit
- && WAS_BANNED(su, (char *)preword + sp->ts_prewordlen))
- || WAS_BANNED(su, (char *)preword)) {
- if (slang->sl_compprog == NULL) {
- break;
- }
- // the word so far was banned but we may try compounding
- goodword_ends = false;
- }
- }
-
- newscore = 0;
- if (!soundfold) { // soundfold words don't have flags
- if ((flags & WF_REGION)
- && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) {
- newscore += SCORE_REGION;
- }
- if (flags & WF_RARE) {
- newscore += SCORE_RARE;
- }
-
- if (!spell_valid_case(su->su_badflags,
- captype(preword + sp->ts_prewordlen, NULL))) {
- newscore += SCORE_ICASE;
- }
- }
-
- // TODO: how about splitting in the soundfold tree?
- if (fword_ends
- && goodword_ends
- && sp->ts_fidx >= sp->ts_fidxtry
- && compound_ok) {
- // The badword also ends: add suggestions.
-#ifdef DEBUG_TRIEWALK
- if (soundfold && STRCMP(preword, "smwrd") == 0) {
- int j;
-
- // print the stack of changes that brought us here
- smsg("------ %s -------", fword);
- for (j = 0; j < depth; ++j) {
- smsg("%s", changename[j]);
- }
- }
-#endif
- if (soundfold) {
- // For soundfolded words we need to find the original
- // words, the edit distance and then add them.
- add_sound_suggest(su, preword, sp->ts_score, lp);
- } else if (sp->ts_fidx > 0) {
- // Give a penalty when changing non-word char to word
- // char, e.g., "thes," -> "these".
- p = fword + sp->ts_fidx;
- MB_PTR_BACK(fword, p);
- if (!spell_iswordp(p, curwin) && *preword != NUL) {
- p = preword + STRLEN(preword);
- MB_PTR_BACK(preword, p);
- if (spell_iswordp(p, curwin)) {
- newscore += SCORE_NONWORD;
- }
- }
-
- // Give a bonus to words seen before.
- score = score_wordcount_adj(slang,
- sp->ts_score + newscore,
- preword + sp->ts_prewordlen,
- sp->ts_prewordlen > 0);
-
- // Add the suggestion if the score isn't too bad.
- if (score <= su->su_maxscore) {
- add_suggestion(su, &su->su_ga, preword,
- sp->ts_fidx - repextra,
- score, 0, false, lp->lp_sallang, false);
-
- if (su->su_badflags & WF_MIXCAP) {
- // We really don't know if the word should be
- // upper or lower case, add both.
- c = captype(preword, NULL);
- if (c == 0 || c == WF_ALLCAP) {
- make_case_word(tword + sp->ts_splitoff,
- preword + sp->ts_prewordlen,
- c == 0 ? WF_ALLCAP : 0);
-
- add_suggestion(su, &su->su_ga, preword,
- sp->ts_fidx - repextra,
- score + SCORE_ICASE, 0, false,
- lp->lp_sallang, false);
- }
- }
- }
- }
- }
-
- // Try word split and/or compounding.
- if ((sp->ts_fidx >= sp->ts_fidxtry || fword_ends)
- // Don't split in the middle of a character
- && (sp->ts_tcharlen == 0)) {
- bool try_compound;
- int try_split;
-
- // If past the end of the bad word don't try a split.
- // Otherwise try changing the next word. E.g., find
- // suggestions for "the the" where the second "the" is
- // different. It's done like a split.
- // TODO: word split for soundfold words
- try_split = (sp->ts_fidx - repextra < su->su_badlen)
- && !soundfold;
-
- // Get here in several situations:
- // 1. The word in the tree ends:
- // If the word allows compounding try that. Otherwise try
- // a split by inserting a space. For both check that a
- // valid words starts at fword[sp->ts_fidx].
- // For NOBREAK do like compounding to be able to check if
- // the next word is valid.
- // 2. The badword does end, but it was due to a change (e.g.,
- // a swap). No need to split, but do check that the
- // following word is valid.
- // 3. The badword and the word in the tree end. It may still
- // be possible to compound another (short) word.
- try_compound = false;
- if (!soundfold
- && !slang->sl_nocompoundsugs
- && slang->sl_compprog != NULL
- && ((unsigned)flags >> 24) != 0
- && sp->ts_twordlen - sp->ts_splitoff
- >= slang->sl_compminlen
- && (slang->sl_compminlen == 0
- || mb_charlen(tword + sp->ts_splitoff)
- >= slang->sl_compminlen)
- && (slang->sl_compsylmax < MAXWLEN
- || sp->ts_complen + 1 - sp->ts_compsplit
- < slang->sl_compmax)
- && (can_be_compound(sp, slang, compflags, (int)((unsigned)flags >> 24)))) {
- try_compound = true;
- compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24);
- compflags[sp->ts_complen + 1] = NUL;
- }
-
- // For NOBREAK we never try splitting, it won't make any word
- // valid.
- if (slang->sl_nobreak && !slang->sl_nocompoundsugs) {
- try_compound = true;
- } else if (!fword_ends
- && try_compound
- && (sp->ts_flags & TSF_DIDSPLIT) == 0) {
- // If we could add a compound word, and it's also possible to
- // split at this point, do the split first and set
- // TSF_DIDSPLIT to avoid doing it again.
- try_compound = false;
- sp->ts_flags |= TSF_DIDSPLIT;
- --sp->ts_curi; // do the same NUL again
- compflags[sp->ts_complen] = NUL;
- } else {
- sp->ts_flags &= (char_u) ~TSF_DIDSPLIT;
- }
-
- if (try_split || try_compound) {
- if (!try_compound && (!fword_ends || !goodword_ends)) {
- // If we're going to split need to check that the
- // words so far are valid for compounding. If there
- // is only one word it must not have the NEEDCOMPOUND
- // flag.
- if (sp->ts_complen == sp->ts_compsplit
- && (flags & WF_NEEDCOMP)) {
- break;
- }
- p = preword;
- while (*skiptowhite(p) != NUL) {
- p = (char_u *)skipwhite((char *)skiptowhite(p));
- }
- if (sp->ts_complen > sp->ts_compsplit
- && !can_compound(slang, p,
- compflags + sp->ts_compsplit)) {
- break;
- }
-
- if (slang->sl_nosplitsugs) {
- newscore += SCORE_SPLIT_NO;
- } else {
- newscore += SCORE_SPLIT;
- }
-
- // Give a bonus to words seen before.
- newscore = score_wordcount_adj(slang, newscore,
- preword + sp->ts_prewordlen, true);
- }
-
- if (TRY_DEEPER(su, stack, depth, newscore)) {
- go_deeper(stack, depth, newscore);
-#ifdef DEBUG_TRIEWALK
- if (!try_compound && !fword_ends) {
- sprintf(changename[depth], "%.*s-%s: split",
- sp->ts_twordlen, tword, fword + sp->ts_fidx);
- } else {
- sprintf(changename[depth], "%.*s-%s: compound",
- sp->ts_twordlen, tword, fword + sp->ts_fidx);
- }
-#endif
- // Save things to be restored at STATE_SPLITUNDO.
- sp->ts_save_badflags = (char_u)su->su_badflags;
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_SPLITUNDO;
-
- ++depth;
- sp = &stack[depth];
-
- // Append a space to preword when splitting.
- if (!try_compound && !fword_ends) {
- STRCAT(preword, " ");
- }
- sp->ts_prewordlen = (char_u)STRLEN(preword);
- sp->ts_splitoff = sp->ts_twordlen;
- sp->ts_splitfidx = sp->ts_fidx;
-
- // If the badword has a non-word character at this
- // position skip it. That means replacing the
- // non-word character with a space. Always skip a
- // character when the word ends. But only when the
- // good word can end.
- if (((!try_compound && !spell_iswordp_nmw(fword
- + sp->ts_fidx,
- curwin))
- || fword_ends)
- && fword[sp->ts_fidx] != NUL
- && goodword_ends) {
- int l;
-
- l = utfc_ptr2len((char *)fword + sp->ts_fidx);
- if (fword_ends) {
- // Copy the skipped character to preword.
- memmove(preword + sp->ts_prewordlen, fword + sp->ts_fidx, (size_t)l);
- sp->ts_prewordlen = (char_u)(sp->ts_prewordlen + l);
- preword[sp->ts_prewordlen] = NUL;
- } else {
- sp->ts_score -= SCORE_SPLIT - SCORE_SUBST;
- }
- sp->ts_fidx = (char_u)(sp->ts_fidx + l);
- }
-
- // When compounding include compound flag in
- // compflags[] (already set above). When splitting we
- // may start compounding over again.
- if (try_compound) {
- ++sp->ts_complen;
- } else {
- sp->ts_compsplit = sp->ts_complen;
- }
- sp->ts_prefixdepth = PFD_NOPREFIX;
-
- // set su->su_badflags to the caps type at this
- // position
- n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
- su->su_badflags = badword_captype(su->su_badptr + n,
- su->su_badptr + su->su_badlen);
-
- // Restart at top of the tree.
- sp->ts_arridx = 0;
-
- // If there are postponed prefixes, try these too.
- if (pbyts != NULL) {
- byts = pbyts;
- idxs = pidxs;
- sp->ts_prefixdepth = PFD_PREFIXTREE;
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_NOPREFIX;
- }
- }
- }
- }
- break;
-
- case STATE_SPLITUNDO:
- // Undo the changes done for word split or compound word.
- su->su_badflags = sp->ts_save_badflags;
-
- // Continue looking for NUL bytes.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_START;
-
- // In case we went into the prefix tree.
- byts = fbyts;
- idxs = fidxs;
- break;
-
- case STATE_ENDNUL:
- // Past the NUL bytes in the node.
- su->su_badflags = sp->ts_save_badflags;
- if (fword[sp->ts_fidx] == NUL
- && sp->ts_tcharlen == 0) {
- // The badword ends, can't use STATE_PLAIN.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_DEL;
- break;
- }
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_PLAIN;
- FALLTHROUGH;
-
- case STATE_PLAIN:
- // Go over all possible bytes at this node, add each to tword[]
- // and use child node. "ts_curi" is the index.
- arridx = sp->ts_arridx;
- if (sp->ts_curi > byts[arridx]) {
- // Done all bytes at this node, do next state. When still at
- // already changed bytes skip the other tricks.
- PROF_STORE(sp->ts_state)
- if (sp->ts_fidx >= sp->ts_fidxtry) {
- sp->ts_state = STATE_DEL;
- } else {
- sp->ts_state = STATE_FINAL;
- }
- } else {
- arridx += sp->ts_curi++;
- c = byts[arridx];
-
- // Normal byte, go one level deeper. If it's not equal to the
- // byte in the bad word adjust the score. But don't even try
- // when the byte was already changed. And don't try when we
- // just deleted this byte, accepting it is always cheaper than
- // delete + substitute.
- if (c == fword[sp->ts_fidx]
- || (sp->ts_tcharlen > 0
- && sp->ts_isdiff != DIFF_NONE)) {
- newscore = 0;
- } else {
- newscore = SCORE_SUBST;
- }
- if ((newscore == 0
- || (sp->ts_fidx >= sp->ts_fidxtry
- && ((sp->ts_flags & TSF_DIDDEL) == 0
- || c != fword[sp->ts_delidx])))
- && TRY_DEEPER(su, stack, depth, newscore)) {
- go_deeper(stack, depth, newscore);
-#ifdef DEBUG_TRIEWALK
- if (newscore > 0) {
- sprintf(changename[depth], "%.*s-%s: subst %c to %c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- fword[sp->ts_fidx], c);
- } else {
- sprintf(changename[depth], "%.*s-%s: accept %c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- fword[sp->ts_fidx]);
- }
-#endif
- ++depth;
- sp = &stack[depth];
- if (fword[sp->ts_fidx] != NUL) {
- sp->ts_fidx++;
- }
- tword[sp->ts_twordlen++] = (char_u)c;
- sp->ts_arridx = idxs[arridx];
- if (newscore == SCORE_SUBST) {
- sp->ts_isdiff = DIFF_YES;
- }
- // Multi-byte characters are a bit complicated to
- // handle: They differ when any of the bytes differ
- // and then their length may also differ.
- if (sp->ts_tcharlen == 0) {
- // First byte.
- sp->ts_tcharidx = 0;
- sp->ts_tcharlen = MB_BYTE2LEN(c);
- sp->ts_fcharstart = (char_u)(sp->ts_fidx - 1);
- sp->ts_isdiff = (newscore != 0)
- ? DIFF_YES : DIFF_NONE;
- } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_fidx > 0) {
- // When inserting trail bytes don't advance in the
- // bad word.
- sp->ts_fidx--;
- }
- if (++sp->ts_tcharidx == sp->ts_tcharlen) {
- // Last byte of character.
- if (sp->ts_isdiff == DIFF_YES) {
- // Correct ts_fidx for the byte length of the
- // character (we didn't check that before).
- sp->ts_fidx = (char_u)(sp->ts_fcharstart
- + utfc_ptr2len((char *)fword + sp->ts_fcharstart));
-
- // For changing a composing character adjust
- // the score from SCORE_SUBST to
- // SCORE_SUBCOMP.
- if (utf_iscomposing(utf_ptr2char((char *)tword + sp->ts_twordlen
- - sp->ts_tcharlen))
- && utf_iscomposing(utf_ptr2char((char *)fword
- + sp->ts_fcharstart))) {
- sp->ts_score -= SCORE_SUBST - SCORE_SUBCOMP;
- } else if (!soundfold
- && slang->sl_has_map
- && similar_chars(slang,
- utf_ptr2char((char *)tword + sp->ts_twordlen -
- sp->ts_tcharlen),
- utf_ptr2char((char *)fword + sp->ts_fcharstart))) {
- // For a similar character adjust score from
- // SCORE_SUBST to SCORE_SIMILAR.
- sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR;
- }
- } else if (sp->ts_isdiff == DIFF_INSERT
- && sp->ts_twordlen > sp->ts_tcharlen) {
- p = tword + sp->ts_twordlen - sp->ts_tcharlen;
- c = utf_ptr2char((char *)p);
- if (utf_iscomposing(c)) {
- // Inserting a composing char doesn't
- // count that much.
- sp->ts_score -= SCORE_INS - SCORE_INSCOMP;
- } else {
- // If the previous character was the same,
- // thus doubling a character, give a bonus
- // to the score. Also for the soundfold
- // tree (might seem illogical but does
- // give better scores).
- MB_PTR_BACK(tword, p);
- if (c == utf_ptr2char((char *)p)) {
- sp->ts_score -= SCORE_INS - SCORE_INSDUP;
- }
- }
- }
-
- // Starting a new char, reset the length.
- sp->ts_tcharlen = 0;
- }
- }
- }
- break;
-
- case STATE_DEL:
- // When past the first byte of a multi-byte char don't try
- // delete/insert/swap a character.
- if (sp->ts_tcharlen > 0) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_FINAL;
- break;
- }
- // Try skipping one character in the bad word (delete it).
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_INS_PREP;
- sp->ts_curi = 1;
- if (soundfold && sp->ts_fidx == 0 && fword[sp->ts_fidx] == '*') {
- // Deleting a vowel at the start of a word counts less, see
- // soundalike_score().
- newscore = 2 * SCORE_DEL / 3;
- } else {
- newscore = SCORE_DEL;
- }
- if (fword[sp->ts_fidx] != NUL
- && TRY_DEEPER(su, stack, depth, newscore)) {
- go_deeper(stack, depth, newscore);
-#ifdef DEBUG_TRIEWALK
- sprintf(changename[depth], "%.*s-%s: delete %c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- fword[sp->ts_fidx]);
-#endif
- ++depth;
-
- // Remember what character we deleted, so that we can avoid
- // inserting it again.
- stack[depth].ts_flags |= TSF_DIDDEL;
- stack[depth].ts_delidx = sp->ts_fidx;
-
- // Advance over the character in fword[]. Give a bonus to the
- // score if the same character is following "nn" -> "n". It's
- // a bit illogical for soundfold tree but it does give better
- // results.
- c = utf_ptr2char((char *)fword + sp->ts_fidx);
- stack[depth].ts_fidx =
- (char_u)(stack[depth].ts_fidx + utfc_ptr2len((char *)fword + sp->ts_fidx));
- if (utf_iscomposing(c)) {
- stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP;
- } else if (c == utf_ptr2char((char *)fword + stack[depth].ts_fidx)) {
- stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP;
- }
-
- break;
- }
- FALLTHROUGH;
-
- case STATE_INS_PREP:
- if (sp->ts_flags & TSF_DIDDEL) {
- // If we just deleted a byte then inserting won't make sense,
- // a substitute is always cheaper.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_SWAP;
- break;
- }
-
- // skip over NUL bytes
- n = sp->ts_arridx;
- for (;;) {
- if (sp->ts_curi > byts[n]) {
- // Only NUL bytes at this node, go to next state.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_SWAP;
- break;
- }
- if (byts[n + sp->ts_curi] != NUL) {
- // Found a byte to insert.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_INS;
- break;
- }
- ++sp->ts_curi;
- }
- break;
-
- case STATE_INS:
- // Insert one byte. Repeat this for each possible byte at this
- // node.
- n = sp->ts_arridx;
- if (sp->ts_curi > byts[n]) {
- // Done all bytes at this node, go to next state.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_SWAP;
- break;
- }
-
- // Do one more byte at this node, but:
- // - Skip NUL bytes.
- // - Skip the byte if it's equal to the byte in the word,
- // accepting that byte is always better.
- n += sp->ts_curi++;
- c = byts[n];
- if (soundfold && sp->ts_twordlen == 0 && c == '*') {
- // Inserting a vowel at the start of a word counts less,
- // see soundalike_score().
- newscore = 2 * SCORE_INS / 3;
- } else {
- newscore = SCORE_INS;
- }
- if (c != fword[sp->ts_fidx]
- && TRY_DEEPER(su, stack, depth, newscore)) {
- go_deeper(stack, depth, newscore);
-#ifdef DEBUG_TRIEWALK
- sprintf(changename[depth], "%.*s-%s: insert %c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- c);
-#endif
- ++depth;
- sp = &stack[depth];
- tword[sp->ts_twordlen++] = (char_u)c;
- sp->ts_arridx = idxs[n];
- fl = MB_BYTE2LEN(c);
- if (fl > 1) {
- // There are following bytes for the same character.
- // We must find all bytes before trying
- // delete/insert/swap/etc.
- sp->ts_tcharlen = (char_u)fl;
- sp->ts_tcharidx = 1;
- sp->ts_isdiff = DIFF_INSERT;
- }
- if (fl == 1) {
- // If the previous character was the same, thus doubling a
- // character, give a bonus to the score. Also for
- // soundfold words (illogical but does give a better
- // score).
- if (sp->ts_twordlen >= 2
- && tword[sp->ts_twordlen - 2] == c) {
- sp->ts_score -= SCORE_INS - SCORE_INSDUP;
- }
- }
- }
- break;
-
- case STATE_SWAP:
- // Swap two bytes in the bad word: "12" -> "21".
- // We change "fword" here, it's changed back afterwards at
- // STATE_UNSWAP.
- p = fword + sp->ts_fidx;
- c = *p;
- if (c == NUL) {
- // End of word, can't swap or replace.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_FINAL;
- break;
- }
-
- // Don't swap if the first character is not a word character.
- // SWAP3 etc. also don't make sense then.
- if (!soundfold && !spell_iswordp(p, curwin)) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- break;
- }
-
- n = utf_ptr2len((char *)p);
- c = utf_ptr2char((char *)p);
- if (p[n] == NUL) {
- c2 = NUL;
- } else if (!soundfold && !spell_iswordp(p + n, curwin)) {
- c2 = c; // don't swap non-word char
- } else {
- c2 = utf_ptr2char((char *)p + n);
- }
-
- // When the second character is NUL we can't swap.
- if (c2 == NUL) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- break;
- }
-
- // When characters are identical, swap won't do anything.
- // Also get here if the second char is not a word character.
- if (c == c2) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_SWAP3;
- break;
- }
- if (TRY_DEEPER(su, stack, depth, SCORE_SWAP)) {
- go_deeper(stack, depth, SCORE_SWAP);
-#ifdef DEBUG_TRIEWALK
- snprintf(changename[depth], sizeof(changename[0]),
- "%.*s-%s: swap %c and %c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- c, c2);
-#endif
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_UNSWAP;
- depth++;
- fl = utf_char2len(c2);
- memmove(p, p + n, (size_t)fl);
- utf_char2bytes(c, (char *)p + fl);
- stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl);
- } else {
- // If this swap doesn't work then SWAP3 won't either.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- }
- break;
-
- case STATE_UNSWAP:
- // Undo the STATE_SWAP swap: "21" -> "12".
- p = fword + sp->ts_fidx;
- n = utfc_ptr2len((char *)p);
- c = utf_ptr2char((char *)p + n);
- memmove(p + utfc_ptr2len((char *)p + n), p, (size_t)n);
- utf_char2bytes(c, (char *)p);
-
- FALLTHROUGH;
-
- case STATE_SWAP3:
- // Swap two bytes, skipping one: "123" -> "321". We change
- // "fword" here, it's changed back afterwards at STATE_UNSWAP3.
- p = fword + sp->ts_fidx;
- n = utf_ptr2len((char *)p);
- c = utf_ptr2char((char *)p);
- fl = utf_ptr2len((char *)p + n);
- c2 = utf_ptr2char((char *)p + n);
- if (!soundfold && !spell_iswordp(p + n + fl, curwin)) {
- c3 = c; // don't swap non-word char
- } else {
- c3 = utf_ptr2char((char *)p + n + fl);
- }
-
- // When characters are identical: "121" then SWAP3 result is
- // identical, ROT3L result is same as SWAP: "211", ROT3L result is
- // same as SWAP on next char: "112". Thus skip all swapping.
- // Also skip when c3 is NUL.
- // Also get here when the third character is not a word character.
- // Second character may any char: "a.b" -> "b.a"
- if (c == c3 || c3 == NUL) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- break;
- }
- if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) {
- go_deeper(stack, depth, SCORE_SWAP3);
-#ifdef DEBUG_TRIEWALK
- sprintf(changename[depth], "%.*s-%s: swap3 %c and %c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- c, c3);
-#endif
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_UNSWAP3;
- depth++;
- tl = utf_char2len(c3);
- memmove(p, p + n + fl, (size_t)tl);
- utf_char2bytes(c2, (char *)p + tl);
- utf_char2bytes(c, (char *)p + fl + tl);
- stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl + tl);
- } else {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- }
- break;
-
- case STATE_UNSWAP3:
- // Undo STATE_SWAP3: "321" -> "123"
- p = fword + sp->ts_fidx;
- n = utfc_ptr2len((char *)p);
- c2 = utf_ptr2char((char *)p + n);
- fl = utfc_ptr2len((char *)p + n);
- c = utf_ptr2char((char *)p + n + fl);
- tl = utfc_ptr2len((char *)p + n + fl);
- memmove(p + fl + tl, p, (size_t)n);
- utf_char2bytes(c, (char *)p);
- utf_char2bytes(c2, (char *)p + tl);
- p = p + tl;
-
- if (!soundfold && !spell_iswordp(p, curwin)) {
- // Middle char is not a word char, skip the rotate. First and
- // third char were already checked at swap and swap3.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- break;
- }
-
- // Rotate three characters left: "123" -> "231". We change
- // "fword" here, it's changed back afterwards at STATE_UNROT3L.
- if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) {
- go_deeper(stack, depth, SCORE_SWAP3);
-#ifdef DEBUG_TRIEWALK
- p = fword + sp->ts_fidx;
- sprintf(changename[depth], "%.*s-%s: rotate left %c%c%c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- p[0], p[1], p[2]);
-#endif
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_UNROT3L;
- ++depth;
- p = fword + sp->ts_fidx;
- n = utf_ptr2len((char *)p);
- c = utf_ptr2char((char *)p);
- fl = utf_ptr2len((char *)p + n);
- fl += utf_ptr2len((char *)p + n + fl);
- memmove(p, p + n, (size_t)fl);
- utf_char2bytes(c, (char *)p + fl);
- stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl);
- } else {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- }
- break;
-
- case STATE_UNROT3L:
- // Undo ROT3L: "231" -> "123"
- p = fword + sp->ts_fidx;
- n = utfc_ptr2len((char *)p);
- n += utfc_ptr2len((char *)p + n);
- c = utf_ptr2char((char *)p + n);
- tl = utfc_ptr2len((char *)p + n);
- memmove(p + tl, p, (size_t)n);
- utf_char2bytes(c, (char *)p);
-
- // Rotate three bytes right: "123" -> "312". We change "fword"
- // here, it's changed back afterwards at STATE_UNROT3R.
- if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) {
- go_deeper(stack, depth, SCORE_SWAP3);
-#ifdef DEBUG_TRIEWALK
- p = fword + sp->ts_fidx;
- sprintf(changename[depth], "%.*s-%s: rotate right %c%c%c",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- p[0], p[1], p[2]);
-#endif
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_UNROT3R;
- ++depth;
- p = fword + sp->ts_fidx;
- n = utf_ptr2len((char *)p);
- n += utf_ptr2len((char *)p + n);
- c = utf_ptr2char((char *)p + n);
- tl = utf_ptr2len((char *)p + n);
- memmove(p + tl, p, (size_t)n);
- utf_char2bytes(c, (char *)p);
- stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + tl);
- } else {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_INI;
- }
- break;
-
- case STATE_UNROT3R:
- // Undo ROT3R: "312" -> "123"
- p = fword + sp->ts_fidx;
- c = utf_ptr2char((char *)p);
- tl = utfc_ptr2len((char *)p);
- n = utfc_ptr2len((char *)p + tl);
- n += utfc_ptr2len((char *)p + tl + n);
- memmove(p, p + tl, (size_t)n);
- utf_char2bytes(c, (char *)p + n);
-
- FALLTHROUGH;
-
- case STATE_REP_INI:
- // Check if matching with REP items from the .aff file would work.
- // Quickly skip if:
- // - there are no REP items and we are not in the soundfold trie
- // - the score is going to be too high anyway
- // - already applied a REP item or swapped here
- if ((lp->lp_replang == NULL && !soundfold)
- || sp->ts_score + SCORE_REP >= su->su_maxscore
- || sp->ts_fidx < sp->ts_fidxtry) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_FINAL;
- break;
- }
-
- // Use the first byte to quickly find the first entry that may
- // match. If the index is -1 there is none.
- if (soundfold) {
- sp->ts_curi = slang->sl_repsal_first[fword[sp->ts_fidx]];
- } else {
- sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]];
- }
-
- if (sp->ts_curi < 0) {
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_FINAL;
- break;
- }
-
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP;
- FALLTHROUGH;
-
- case STATE_REP:
- // Try matching with REP items from the .aff file. For each match
- // replace the characters and check if the resulting word is
- // valid.
- p = fword + sp->ts_fidx;
-
- if (soundfold) {
- gap = &slang->sl_repsal;
- } else {
- gap = &lp->lp_replang->sl_rep;
- }
- while (sp->ts_curi < gap->ga_len) {
- ftp = (fromto_T *)gap->ga_data + sp->ts_curi++;
- if (*ftp->ft_from != *p) {
- // past possible matching entries
- sp->ts_curi = (char_u)gap->ga_len;
- break;
- }
- if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0
- && TRY_DEEPER(su, stack, depth, SCORE_REP)) {
- go_deeper(stack, depth, SCORE_REP);
-#ifdef DEBUG_TRIEWALK
- sprintf(changename[depth], "%.*s-%s: replace %s with %s",
- sp->ts_twordlen, tword, fword + sp->ts_fidx,
- ftp->ft_from, ftp->ft_to);
-#endif
- // Need to undo this afterwards.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP_UNDO;
-
- // Change the "from" to the "to" string.
- ++depth;
- fl = (int)STRLEN(ftp->ft_from);
- tl = (int)STRLEN(ftp->ft_to);
- if (fl != tl) {
- STRMOVE(p + tl, p + fl);
- repextra += tl - fl;
- }
- memmove(p, ftp->ft_to, (size_t)tl);
- stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + tl);
- stack[depth].ts_tcharlen = 0;
- break;
- }
- }
-
- if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) {
- // No (more) matches.
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_FINAL;
- }
-
- break;
-
- case STATE_REP_UNDO:
- // Undo a REP replacement and continue with the next one.
- if (soundfold) {
- gap = &slang->sl_repsal;
- } else {
- gap = &lp->lp_replang->sl_rep;
- }
- ftp = (fromto_T *)gap->ga_data + sp->ts_curi - 1;
- fl = (int)STRLEN(ftp->ft_from);
- tl = (int)STRLEN(ftp->ft_to);
- p = fword + sp->ts_fidx;
- if (fl != tl) {
- STRMOVE(p + fl, p + tl);
- repextra -= tl - fl;
- }
- memmove(p, ftp->ft_from, (size_t)fl);
- PROF_STORE(sp->ts_state)
- sp->ts_state = STATE_REP;
- break;
-
- default:
- // Did all possible states at this level, go up one level.
- --depth;
-
- if (depth >= 0 && stack[depth].ts_prefixdepth == PFD_PREFIXTREE) {
- // Continue in or go back to the prefix tree.
- byts = pbyts;
- idxs = pidxs;
- }
-
- // Don't check for CTRL-C too often, it takes time.
- if (--breakcheckcount == 0) {
- os_breakcheck();
- breakcheckcount = 1000;
- if (profile_passed_limit(time_limit)) {
- got_int = true;
- }
- }
- }
- }
-}
-
-// Go one level deeper in the tree.
-static void go_deeper(trystate_T *stack, int depth, int score_add)
-{
- stack[depth + 1] = stack[depth];
- stack[depth + 1].ts_state = STATE_START;
- stack[depth + 1].ts_score = stack[depth].ts_score + score_add;
- stack[depth + 1].ts_curi = 1; // start just after length byte
- stack[depth + 1].ts_flags = 0;
-}
-
// Case-folding may change the number of bytes: Count nr of chars in
// fword[flen] and return the byte length of that many chars in "word".
-static int nofold_len(char_u *fword, int flen, char_u *word)
+int nofold_len(char_u *fword, int flen, char_u *word)
{
char_u *p;
int i = 0;
@@ -4961,677 +2640,8 @@ static int nofold_len(char_u *fword, int flen, char_u *word)
return (int)(p - word);
}
-// "fword" is a good word with case folded. Find the matching keep-case
-// words and put it in "kword".
-// Theoretically there could be several keep-case words that result in the
-// same case-folded word, but we only find one...
-static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
-{
- char_u uword[MAXWLEN]; // "fword" in upper-case
- int depth;
- idx_T tryidx;
-
- // The following arrays are used at each depth in the tree.
- idx_T arridx[MAXWLEN];
- int round[MAXWLEN];
- int fwordidx[MAXWLEN];
- int uwordidx[MAXWLEN];
- int kwordlen[MAXWLEN];
-
- int flen, ulen;
- int l;
- int len;
- int c;
- idx_T lo, hi, m;
- char_u *p;
- char_u *byts = slang->sl_kbyts; // array with bytes of the words
- idx_T *idxs = slang->sl_kidxs; // array with indexes
-
- if (byts == NULL) {
- // array is empty: "cannot happen"
- *kword = NUL;
- return;
- }
-
- // Make an all-cap version of "fword".
- allcap_copy(fword, uword);
-
- // Each character needs to be tried both case-folded and upper-case.
- // All this gets very complicated if we keep in mind that changing case
- // may change the byte length of a multi-byte character...
- depth = 0;
- arridx[0] = 0;
- round[0] = 0;
- fwordidx[0] = 0;
- uwordidx[0] = 0;
- kwordlen[0] = 0;
- while (depth >= 0) {
- if (fword[fwordidx[depth]] == NUL) {
- // We are at the end of "fword". If the tree allows a word to end
- // here we have found a match.
- if (byts[arridx[depth] + 1] == 0) {
- kword[kwordlen[depth]] = NUL;
- return;
- }
-
- // kword is getting too long, continue one level up
- --depth;
- } else if (++round[depth] > 2) {
- // tried both fold-case and upper-case character, continue one
- // level up
- --depth;
- } else {
- // round[depth] == 1: Try using the folded-case character.
- // round[depth] == 2: Try using the upper-case character.
- flen = utf_ptr2len((char *)fword + fwordidx[depth]);
- ulen = utf_ptr2len((char *)uword + uwordidx[depth]);
- if (round[depth] == 1) {
- p = fword + fwordidx[depth];
- l = flen;
- } else {
- p = uword + uwordidx[depth];
- l = ulen;
- }
-
- for (tryidx = arridx[depth]; l > 0; --l) {
- // Perform a binary search in the list of accepted bytes.
- len = byts[tryidx++];
- c = *p++;
- lo = tryidx;
- hi = tryidx + len - 1;
- while (lo < hi) {
- m = (lo + hi) / 2;
- if (byts[m] > c) {
- hi = m - 1;
- } else if (byts[m] < c) {
- lo = m + 1;
- } else {
- lo = hi = m;
- break;
- }
- }
-
- // Stop if there is no matching byte.
- if (hi < lo || byts[lo] != c) {
- break;
- }
-
- // Continue at the child (if there is one).
- tryidx = idxs[lo];
- }
-
- if (l == 0) {
- // Found the matching char. Copy it to "kword" and go a
- // level deeper.
- if (round[depth] == 1) {
- STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth],
- flen);
- kwordlen[depth + 1] = kwordlen[depth] + flen;
- } else {
- STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth],
- ulen);
- kwordlen[depth + 1] = kwordlen[depth] + ulen;
- }
- fwordidx[depth + 1] = fwordidx[depth] + flen;
- uwordidx[depth + 1] = uwordidx[depth] + ulen;
-
- ++depth;
- arridx[depth] = tryidx;
- round[depth] = 0;
- }
- }
- }
-
- // Didn't find it: "cannot happen".
- *kword = NUL;
-}
-
-// Compute the sound-a-like score for suggestions in su->su_ga and add them to
-// su->su_sga.
-static void score_comp_sal(suginfo_T *su)
-{
- langp_T *lp;
- char_u badsound[MAXWLEN];
- int i;
- suggest_T *stp;
- suggest_T *sstp;
- int score;
-
- ga_grow(&su->su_sga, su->su_ga.ga_len);
-
- // Use the sound-folding of the first language that supports it.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
- lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
- if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
- // soundfold the bad word
- spell_soundfold(lp->lp_slang, su->su_fbadword, true, badsound);
-
- for (i = 0; i < su->su_ga.ga_len; ++i) {
- stp = &SUG(su->su_ga, i);
-
- // Case-fold the suggested word, sound-fold it and compute the
- // sound-a-like score.
- score = stp_sal_score(stp, su, lp->lp_slang, badsound);
- if (score < SCORE_MAXMAX) {
- // Add the suggestion.
- sstp = &SUG(su->su_sga, su->su_sga.ga_len);
- sstp->st_word = vim_strsave(stp->st_word);
- sstp->st_wordlen = stp->st_wordlen;
- sstp->st_score = score;
- sstp->st_altscore = 0;
- sstp->st_orglen = stp->st_orglen;
- ++su->su_sga.ga_len;
- }
- }
- break;
- }
- }
-}
-
-// Combine the list of suggestions in su->su_ga and su->su_sga.
-// They are entwined.
-static void score_combine(suginfo_T *su)
-{
- garray_T ga;
- garray_T *gap;
- langp_T *lp;
- suggest_T *stp;
- char_u *p;
- char_u badsound[MAXWLEN];
- int round;
- slang_T *slang = NULL;
-
- // Add the alternate score to su_ga.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
- lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
- if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
- // soundfold the bad word
- slang = lp->lp_slang;
- spell_soundfold(slang, su->su_fbadword, true, badsound);
-
- for (int i = 0; i < su->su_ga.ga_len; ++i) {
- stp = &SUG(su->su_ga, i);
- stp->st_altscore = stp_sal_score(stp, su, slang, badsound);
- if (stp->st_altscore == SCORE_MAXMAX) {
- stp->st_score = (stp->st_score * 3 + SCORE_BIG) / 4;
- } else {
- stp->st_score = (stp->st_score * 3
- + stp->st_altscore) / 4;
- }
- stp->st_salscore = false;
- }
- break;
- }
- }
-
- if (slang == NULL) { // Using "double" without sound folding.
- (void)cleanup_suggestions(&su->su_ga, su->su_maxscore,
- su->su_maxcount);
- return;
- }
-
- // Add the alternate score to su_sga.
- for (int i = 0; i < su->su_sga.ga_len; ++i) {
- stp = &SUG(su->su_sga, i);
- stp->st_altscore = spell_edit_score(slang,
- su->su_badword, stp->st_word);
- if (stp->st_score == SCORE_MAXMAX) {
- stp->st_score = (SCORE_BIG * 7 + stp->st_altscore) / 8;
- } else {
- stp->st_score = (stp->st_score * 7 + stp->st_altscore) / 8;
- }
- stp->st_salscore = true;
- }
-
- // Remove bad suggestions, sort the suggestions and truncate at "maxcount"
- // for both lists.
- check_suggestions(su, &su->su_ga);
- (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
- check_suggestions(su, &su->su_sga);
- (void)cleanup_suggestions(&su->su_sga, su->su_maxscore, su->su_maxcount);
-
- ga_init(&ga, (int)sizeof(suginfo_T), 1);
- ga_grow(&ga, su->su_ga.ga_len + su->su_sga.ga_len);
-
- stp = &SUG(ga, 0);
- for (int i = 0; i < su->su_ga.ga_len || i < su->su_sga.ga_len; ++i) {
- // round 1: get a suggestion from su_ga
- // round 2: get a suggestion from su_sga
- for (round = 1; round <= 2; ++round) {
- gap = round == 1 ? &su->su_ga : &su->su_sga;
- if (i < gap->ga_len) {
- // Don't add a word if it's already there.
- p = SUG(*gap, i).st_word;
- int j;
- for (j = 0; j < ga.ga_len; ++j) {
- if (STRCMP(stp[j].st_word, p) == 0) {
- break;
- }
- }
- if (j == ga.ga_len) {
- stp[ga.ga_len++] = SUG(*gap, i);
- } else {
- xfree(p);
- }
- }
- }
- }
-
- ga_clear(&su->su_ga);
- ga_clear(&su->su_sga);
-
- // Truncate the list to the number of suggestions that will be displayed.
- if (ga.ga_len > su->su_maxcount) {
- for (int i = su->su_maxcount; i < ga.ga_len; ++i) {
- xfree(stp[i].st_word);
- }
- ga.ga_len = su->su_maxcount;
- }
-
- su->su_ga = ga;
-}
-
-/// For the goodword in "stp" compute the soundalike score compared to the
-/// badword.
-///
-/// @param badsound sound-folded badword
-static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *badsound)
-{
- char_u *p;
- char_u *pbad;
- char_u *pgood;
- char_u badsound2[MAXWLEN];
- char_u fword[MAXWLEN];
- char_u goodsound[MAXWLEN];
- char_u goodword[MAXWLEN];
- int lendiff;
-
- lendiff = su->su_badlen - stp->st_orglen;
- if (lendiff >= 0) {
- pbad = badsound;
- } else {
- // soundfold the bad word with more characters following
- (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN);
-
- // When joining two words the sound often changes a lot. E.g., "t he"
- // sounds like "t h" while "the" sounds like "@". Avoid that by
- // removing the space. Don't do it when the good word also contains a
- // space.
- if (ascii_iswhite(su->su_badptr[su->su_badlen])
- && *skiptowhite(stp->st_word) == NUL) {
- for (p = fword; *(p = skiptowhite(p)) != NUL;) {
- STRMOVE(p, p + 1);
- }
- }
-
- spell_soundfold(slang, fword, true, badsound2);
- pbad = badsound2;
- }
-
- if (lendiff > 0 && stp->st_wordlen + lendiff < MAXWLEN) {
- // Add part of the bad word to the good word, so that we soundfold
- // what replaces the bad word.
- STRCPY(goodword, stp->st_word);
- STRLCPY(goodword + stp->st_wordlen,
- su->su_badptr + su->su_badlen - lendiff, lendiff + 1);
- pgood = goodword;
- } else {
- pgood = stp->st_word;
- }
-
- // Sound-fold the word and compute the score for the difference.
- spell_soundfold(slang, pgood, false, goodsound);
-
- return soundalike_score(goodsound, pbad);
-}
-
-static sftword_T dumsft;
-#define HIKEY2SFT(p) ((sftword_T *)((p) - (dumsft.sft_word - (char_u *)&dumsft)))
-#define HI2SFT(hi) HIKEY2SFT((hi)->hi_key)
-
-// Prepare for calling suggest_try_soundalike().
-static void suggest_try_soundalike_prep(void)
-{
- langp_T *lp;
- slang_T *slang;
-
- // Do this for all languages that support sound folding and for which a
- // .sug file has been loaded.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
- lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
- slang = lp->lp_slang;
- if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
- // prepare the hashtable used by add_sound_suggest()
- hash_init(&slang->sl_sounddone);
- }
- }
-}
-
-// Find suggestions by comparing the word in a sound-a-like form.
-// Note: This doesn't support postponed prefixes.
-static void suggest_try_soundalike(suginfo_T *su)
-{
- char_u salword[MAXWLEN];
- langp_T *lp;
- slang_T *slang;
-
- // Do this for all languages that support sound folding and for which a
- // .sug file has been loaded.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
- lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
- slang = lp->lp_slang;
- if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
- // soundfold the bad word
- spell_soundfold(slang, su->su_fbadword, true, salword);
-
- // try all kinds of inserts/deletes/swaps/etc.
- // TODO: also soundfold the next words, so that we can try joining
- // and splitting
-#ifdef SUGGEST_PROFILE
- prof_init();
-#endif
- suggest_trie_walk(su, lp, salword, true);
-#ifdef SUGGEST_PROFILE
- prof_report("soundalike");
-#endif
- }
- }
-}
-
-// Finish up after calling suggest_try_soundalike().
-static void suggest_try_soundalike_finish(void)
-{
- langp_T *lp;
- slang_T *slang;
- int todo;
- hashitem_T *hi;
-
- // Do this for all languages that support sound folding and for which a
- // .sug file has been loaded.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
- lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
- slang = lp->lp_slang;
- if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
- // Free the info about handled words.
- todo = (int)slang->sl_sounddone.ht_used;
- for (hi = slang->sl_sounddone.ht_array; todo > 0; ++hi) {
- if (!HASHITEM_EMPTY(hi)) {
- xfree(HI2SFT(hi));
- --todo;
- }
- }
-
- // Clear the hashtable, it may also be used by another region.
- hash_clear(&slang->sl_sounddone);
- hash_init(&slang->sl_sounddone);
- }
- }
-}
-
-/// A match with a soundfolded word is found. Add the good word(s) that
-/// produce this soundfolded word.
-///
-/// @param score soundfold score
-static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_T *lp)
-{
- slang_T *slang = lp->lp_slang; // language for sound folding
- int sfwordnr;
- char_u *nrline;
- int orgnr;
- char_u theword[MAXWLEN];
- int i;
- int wlen;
- char_u *byts;
- idx_T *idxs;
- int n;
- int wordcount;
- int wc;
- int goodscore;
- hash_T hash;
- hashitem_T *hi;
- sftword_T *sft;
- int bc, gc;
- int limit;
-
- // It's very well possible that the same soundfold word is found several
- // times with different scores. Since the following is quite slow only do
- // the words that have a better score than before. Use a hashtable to
- // remember the words that have been done.
- hash = hash_hash(goodword);
- const size_t goodword_len = STRLEN(goodword);
- hi = hash_lookup(&slang->sl_sounddone, (const char *)goodword, goodword_len,
- hash);
- if (HASHITEM_EMPTY(hi)) {
- sft = xmalloc(sizeof(sftword_T) + goodword_len);
- sft->sft_score = (int16_t)score;
- memcpy(sft->sft_word, goodword, goodword_len + 1);
- hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash);
- } else {
- sft = HI2SFT(hi);
- if (score >= sft->sft_score) {
- return;
- }
- sft->sft_score = (int16_t)score;
- }
-
- // Find the word nr in the soundfold tree.
- sfwordnr = soundfold_find(slang, goodword);
- if (sfwordnr < 0) {
- internal_error("add_sound_suggest()");
- return;
- }
-
- // Go over the list of good words that produce this soundfold word
- nrline = ml_get_buf(slang->sl_sugbuf, (linenr_T)sfwordnr + 1, false);
- orgnr = 0;
- while (*nrline != NUL) {
- // The wordnr was stored in a minimal nr of bytes as an offset to the
- // previous wordnr.
- orgnr += bytes2offset(&nrline);
-
- byts = slang->sl_fbyts;
- idxs = slang->sl_fidxs;
-
- // Lookup the word "orgnr" one of the two tries.
- n = 0;
- wordcount = 0;
- for (wlen = 0; wlen < MAXWLEN - 3; ++wlen) {
- i = 1;
- if (wordcount == orgnr && byts[n + 1] == NUL) {
- break; // found end of word
- }
- if (byts[n + 1] == NUL) {
- ++wordcount;
- }
-
- // skip over the NUL bytes
- for (; byts[n + i] == NUL; ++i) {
- if (i > byts[n]) { // safety check
- STRCPY(theword + wlen, "BAD");
- wlen += 3;
- goto badword;
- }
- }
-
- // One of the siblings must have the word.
- for (; i < byts[n]; ++i) {
- wc = idxs[idxs[n + i]]; // nr of words under this byte
- if (wordcount + wc > orgnr) {
- break;
- }
- wordcount += wc;
- }
-
- theword[wlen] = byts[n + i];
- n = idxs[n + i];
- }
-badword:
- theword[wlen] = NUL;
-
- // Go over the possible flags and regions.
- for (; i <= byts[n] && byts[n + i] == NUL; ++i) {
- char_u cword[MAXWLEN];
- char_u *p;
- int flags = (int)idxs[n + i];
-
- // Skip words with the NOSUGGEST flag
- if (flags & WF_NOSUGGEST) {
- continue;
- }
-
- if (flags & WF_KEEPCAP) {
- // Must find the word in the keep-case tree.
- find_keepcap_word(slang, theword, cword);
- p = cword;
- } else {
- flags |= su->su_badflags;
- if ((flags & WF_CAPMASK) != 0) {
- // Need to fix case according to "flags".
- make_case_word(theword, cword, flags);
- p = cword;
- } else {
- p = theword;
- }
- }
-
- // Add the suggestion.
- if (sps_flags & SPS_DOUBLE) {
- // Add the suggestion if the score isn't too bad.
- if (score <= su->su_maxscore) {
- add_suggestion(su, &su->su_sga, p, su->su_badlen,
- score, 0, false, slang, false);
- }
- } else {
- // Add a penalty for words in another region.
- if ((flags & WF_REGION)
- && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) {
- goodscore = SCORE_REGION;
- } else {
- goodscore = 0;
- }
-
- // Add a small penalty for changing the first letter from
- // lower to upper case. Helps for "tath" -> "Kath", which is
- // less common than "tath" -> "path". Don't do it when the
- // letter is the same, that has already been counted.
- gc = utf_ptr2char((char *)p);
- if (SPELL_ISUPPER(gc)) {
- bc = utf_ptr2char((char *)su->su_badword);
- if (!SPELL_ISUPPER(bc)
- && SPELL_TOFOLD(bc) != SPELL_TOFOLD(gc)) {
- goodscore += SCORE_ICASE / 2;
- }
- }
-
- // Compute the score for the good word. This only does letter
- // insert/delete/swap/replace. REP items are not considered,
- // which may make the score a bit higher.
- // Use a limit for the score to make it work faster. Use
- // MAXSCORE(), because RESCORE() will change the score.
- // If the limit is very high then the iterative method is
- // inefficient, using an array is quicker.
- limit = MAXSCORE(su->su_sfmaxscore - goodscore, score);
- if (limit > SCORE_LIMITMAX) {
- goodscore += spell_edit_score(slang, su->su_badword, p);
- } else {
- goodscore += spell_edit_score_limit(slang, su->su_badword,
- p, limit);
- }
-
- // When going over the limit don't bother to do the rest.
- if (goodscore < SCORE_MAXMAX) {
- // Give a bonus to words seen before.
- goodscore = score_wordcount_adj(slang, goodscore, p, false);
-
- // Add the suggestion if the score isn't too bad.
- goodscore = RESCORE(goodscore, score);
- if (goodscore <= su->su_sfmaxscore) {
- add_suggestion(su, &su->su_ga, p, su->su_badlen,
- goodscore, score, true, slang, true);
- }
- }
- }
- }
- }
-}
-
-// Find word "word" in fold-case tree for "slang" and return the word number.
-static int soundfold_find(slang_T *slang, char_u *word)
-{
- idx_T arridx = 0;
- int len;
- int wlen = 0;
- int c;
- char_u *ptr = word;
- char_u *byts;
- idx_T *idxs;
- int wordnr = 0;
-
- byts = slang->sl_sbyts;
- idxs = slang->sl_sidxs;
-
- for (;;) {
- // First byte is the number of possible bytes.
- len = byts[arridx++];
-
- // If the first possible byte is a zero the word could end here.
- // If the word ends we found the word. If not skip the NUL bytes.
- c = ptr[wlen];
- if (byts[arridx] == NUL) {
- if (c == NUL) {
- break;
- }
-
- // Skip over the zeros, there can be several.
- while (len > 0 && byts[arridx] == NUL) {
- ++arridx;
- --len;
- }
- if (len == 0) {
- return -1; // no children, word should have ended here
- }
- ++wordnr;
- }
-
- // If the word ends we didn't find it.
- if (c == NUL) {
- return -1;
- }
-
- // Perform a binary search in the list of accepted bytes.
- if (c == TAB) { // <Tab> is handled like <Space>
- c = ' ';
- }
- while (byts[arridx] < c) {
- // The word count is in the first idxs[] entry of the child.
- wordnr += idxs[idxs[arridx]];
- ++arridx;
- if (--len == 0) { // end of the bytes, didn't find it
- return -1;
- }
- }
- if (byts[arridx] != c) { // didn't find the byte
- return -1;
- }
-
- // Continue at the child (if there is one).
- arridx = idxs[arridx];
- ++wlen;
-
- // One space in the good word may stand for several spaces in the
- // checked word.
- if (c == ' ') {
- while (ptr[wlen] == ' ' || ptr[wlen] == TAB) {
- ++wlen;
- }
- }
- }
-
- return wordnr;
-}
-
// Copy "fword" to "cword", fixing case according to "flags".
-static void make_case_word(char_u *fword, char_u *cword, int flags)
+void make_case_word(char_u *fword, char_u *cword, int flags)
{
if (flags & WF_ALLCAP) {
// Make it all upper-case
@@ -5645,291 +2655,6 @@ static void make_case_word(char_u *fword, char_u *cword, int flags)
}
}
-// Returns true if "c1" and "c2" are similar characters according to the MAP
-// lines in the .aff file.
-static bool similar_chars(slang_T *slang, int c1, int c2)
-{
- int m1, m2;
- char buf[MB_MAXBYTES + 1];
- hashitem_T *hi;
-
- if (c1 >= 256) {
- buf[utf_char2bytes(c1, (char *)buf)] = 0;
- hi = hash_find(&slang->sl_map_hash, buf);
- if (HASHITEM_EMPTY(hi)) {
- m1 = 0;
- } else {
- m1 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
- }
- } else {
- m1 = slang->sl_map_array[c1];
- }
- if (m1 == 0) {
- return false;
- }
-
- if (c2 >= 256) {
- buf[utf_char2bytes(c2, (char *)buf)] = 0;
- hi = hash_find(&slang->sl_map_hash, buf);
- if (HASHITEM_EMPTY(hi)) {
- m2 = 0;
- } else {
- m2 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
- }
- } else {
- m2 = slang->sl_map_array[c2];
- }
-
- return m1 == m2;
-}
-
-/// Adds a suggestion to the list of suggestions.
-/// For a suggestion that is already in the list the lowest score is remembered.
-///
-/// @param gap either su_ga or su_sga
-/// @param badlenarg len of bad word replaced with "goodword"
-/// @param had_bonus value for st_had_bonus
-/// @param slang language for sound folding
-/// @param maxsf su_maxscore applies to soundfold score, su_sfmaxscore to the total score.
-static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, int badlenarg,
- int score, int altscore, bool had_bonus, slang_T *slang, bool maxsf)
-{
- int goodlen; // len of goodword changed
- int badlen; // len of bad word changed
- suggest_T *stp;
- suggest_T new_sug;
-
- // Minimize "badlen" for consistency. Avoids that changing "the the" to
- // "thee the" is added next to changing the first "the" the "thee".
- const char_u *pgood = goodword + STRLEN(goodword);
- char_u *pbad = su->su_badptr + badlenarg;
- for (;;) {
- goodlen = (int)(pgood - goodword);
- badlen = (int)(pbad - su->su_badptr);
- if (goodlen <= 0 || badlen <= 0) {
- break;
- }
- MB_PTR_BACK(goodword, pgood);
- MB_PTR_BACK(su->su_badptr, pbad);
- if (utf_ptr2char((char *)pgood) != utf_ptr2char((char *)pbad)) {
- break;
- }
- }
-
- if (badlen == 0 && goodlen == 0) {
- // goodword doesn't change anything; may happen for "the the" changing
- // the first "the" to itself.
- return;
- }
-
- int i;
- if (GA_EMPTY(gap)) {
- i = -1;
- } else {
- // Check if the word is already there. Also check the length that is
- // being replaced "thes," -> "these" is a different suggestion from
- // "thes" -> "these".
- stp = &SUG(*gap, 0);
- for (i = gap->ga_len; --i >= 0; ++stp) {
- if (stp->st_wordlen == goodlen
- && stp->st_orglen == badlen
- && STRNCMP(stp->st_word, goodword, goodlen) == 0) {
- // Found it. Remember the word with the lowest score.
- if (stp->st_slang == NULL) {
- stp->st_slang = slang;
- }
-
- new_sug.st_score = score;
- new_sug.st_altscore = altscore;
- new_sug.st_had_bonus = had_bonus;
-
- if (stp->st_had_bonus != had_bonus) {
- // Only one of the two had the soundalike score computed.
- // Need to do that for the other one now, otherwise the
- // scores can't be compared. This happens because
- // suggest_try_change() doesn't compute the soundalike
- // word to keep it fast, while some special methods set
- // the soundalike score to zero.
- if (had_bonus) {
- rescore_one(su, stp);
- } else {
- new_sug.st_word = stp->st_word;
- new_sug.st_wordlen = stp->st_wordlen;
- new_sug.st_slang = stp->st_slang;
- new_sug.st_orglen = badlen;
- rescore_one(su, &new_sug);
- }
- }
-
- if (stp->st_score > new_sug.st_score) {
- stp->st_score = new_sug.st_score;
- stp->st_altscore = new_sug.st_altscore;
- stp->st_had_bonus = new_sug.st_had_bonus;
- }
- break;
- }
- }
- }
-
- if (i < 0) {
- // Add a suggestion.
- stp = GA_APPEND_VIA_PTR(suggest_T, gap);
- stp->st_word = vim_strnsave(goodword, (size_t)goodlen);
- stp->st_wordlen = goodlen;
- stp->st_score = score;
- stp->st_altscore = altscore;
- stp->st_had_bonus = had_bonus;
- stp->st_orglen = badlen;
- stp->st_slang = slang;
-
- // If we have too many suggestions now, sort the list and keep
- // the best suggestions.
- if (gap->ga_len > SUG_MAX_COUNT(su)) {
- if (maxsf) {
- su->su_sfmaxscore = cleanup_suggestions(gap,
- su->su_sfmaxscore, SUG_CLEAN_COUNT(su));
- } else {
- su->su_maxscore = cleanup_suggestions(gap,
- su->su_maxscore, SUG_CLEAN_COUNT(su));
- }
- }
- }
-}
-
-/// Suggestions may in fact be flagged as errors. Esp. for banned words and
-/// for split words, such as "the the". Remove these from the list here.
-///
-/// @param gap either su_ga or su_sga
-static void check_suggestions(suginfo_T *su, garray_T *gap)
-{
- suggest_T *stp;
- char_u longword[MAXWLEN + 1];
- int len;
- hlf_T attr;
-
- if (gap->ga_len == 0) {
- return;
- }
- stp = &SUG(*gap, 0);
- for (int i = gap->ga_len - 1; i >= 0; --i) {
- // Need to append what follows to check for "the the".
- STRLCPY(longword, stp[i].st_word, MAXWLEN + 1);
- len = stp[i].st_wordlen;
- STRLCPY(longword + len, su->su_badptr + stp[i].st_orglen,
- MAXWLEN - len + 1);
- attr = HLF_COUNT;
- (void)spell_check(curwin, longword, &attr, NULL, false);
- if (attr != HLF_COUNT) {
- // Remove this entry.
- xfree(stp[i].st_word);
- --gap->ga_len;
- if (i < gap->ga_len) {
- memmove(stp + i, stp + i + 1, sizeof(suggest_T) * (size_t)(gap->ga_len - i));
- }
- }
- }
-}
-
-// Add a word to be banned.
-static void add_banned(suginfo_T *su, char_u *word)
-{
- char_u *s;
- hash_T hash;
- hashitem_T *hi;
-
- hash = hash_hash(word);
- const size_t word_len = STRLEN(word);
- hi = hash_lookup(&su->su_banned, (const char *)word, word_len, hash);
- if (HASHITEM_EMPTY(hi)) {
- s = xmemdupz(word, word_len);
- hash_add_item(&su->su_banned, hi, s, hash);
- }
-}
-
-// Recompute the score for all suggestions if sound-folding is possible. This
-// is slow, thus only done for the final results.
-static void rescore_suggestions(suginfo_T *su)
-{
- if (su->su_sallang != NULL) {
- for (int i = 0; i < su->su_ga.ga_len; ++i) {
- rescore_one(su, &SUG(su->su_ga, i));
- }
- }
-}
-
-// Recompute the score for one suggestion if sound-folding is possible.
-static void rescore_one(suginfo_T *su, suggest_T *stp)
-{
- slang_T *slang = stp->st_slang;
- char_u sal_badword[MAXWLEN];
- char_u *p;
-
- // Only rescore suggestions that have no sal score yet and do have a
- // language.
- if (slang != NULL && !GA_EMPTY(&slang->sl_sal) && !stp->st_had_bonus) {
- if (slang == su->su_sallang) {
- p = su->su_sal_badword;
- } else {
- spell_soundfold(slang, su->su_fbadword, true, sal_badword);
- p = sal_badword;
- }
-
- stp->st_altscore = stp_sal_score(stp, su, slang, p);
- if (stp->st_altscore == SCORE_MAXMAX) {
- stp->st_altscore = SCORE_BIG;
- }
- stp->st_score = RESCORE(stp->st_score, stp->st_altscore);
- stp->st_had_bonus = true;
- }
-}
-
-// Function given to qsort() to sort the suggestions on st_score.
-// First on "st_score", then "st_altscore" then alphabetically.
-static int sug_compare(const void *s1, const void *s2)
-{
- suggest_T *p1 = (suggest_T *)s1;
- suggest_T *p2 = (suggest_T *)s2;
- int n = p1->st_score - p2->st_score;
-
- if (n == 0) {
- n = p1->st_altscore - p2->st_altscore;
- if (n == 0) {
- n = STRICMP(p1->st_word, p2->st_word);
- }
- }
- return n;
-}
-
-/// Cleanup the suggestions:
-/// - Sort on score.
-/// - Remove words that won't be displayed.
-///
-/// @param keep nr of suggestions to keep
-///
-/// @return the maximum score in the list or "maxscore" unmodified.
-static int cleanup_suggestions(garray_T *gap, int maxscore, int keep)
- FUNC_ATTR_NONNULL_ALL
-{
- if (gap->ga_len > 0) {
- // Sort the list.
- qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(suggest_T), sug_compare);
-
- // Truncate the list to the number of suggestions that will be displayed.
- if (gap->ga_len > keep) {
- suggest_T *const stp = &SUG(*gap, 0);
-
- for (int i = keep; i < gap->ga_len; i++) {
- xfree(stp[i].st_word);
- }
- gap->ga_len = keep;
- if (keep >= 1) {
- return stp[keep - 1].st_score;
- }
- }
- }
- return maxscore;
-}
-
/// Soundfold a string, for soundfold()
///
/// @param[in] word Word to soundfold.
@@ -5974,13 +2699,12 @@ char *eval_soundfold(const char *const word)
/// @param[in,out] res destination for soundfolded word
void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res)
{
- char_u fword[MAXWLEN];
- char_u *word;
-
if (slang->sl_sofo) {
// SOFOFROM and SOFOTO used
spell_soundfold_sofo(slang, inword, res);
} else {
+ char_u fword[MAXWLEN];
+ char_u *word;
// SAL items used. Requires the word to be case-folded.
if (folded) {
word = inword;
@@ -6047,29 +2771,26 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data;
int word[MAXWLEN] = { 0 };
int wres[MAXWLEN] = { 0 };
- int l;
int *ws;
int *pf;
- int i, j, z;
+ int j, z;
int reslen;
- int n, k = 0;
+ int k = 0;
int z0;
int k0;
int n0;
- int c;
int pri;
int p0 = -333;
int c0;
bool did_white = false;
- int wordlen;
// Convert the multi-byte string to a wide-character string.
// Remove accents, if wanted. We actually remove all non-word characters.
// But keep white space.
- wordlen = 0;
+ int wordlen = 0;
for (const char_u *s = inword; *s != NUL;) {
const char_u *t = s;
- c = mb_cptr2char_adv(&s);
+ int c = mb_cptr2char_adv(&s);
if (slang->sl_rem_accents) {
if (utf_class(c) == 0) {
if (did_white) {
@@ -6088,13 +2809,14 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
word[wordlen] = NUL;
+ int c;
// This algorithm comes from Aspell phonet.cpp.
// Converted from C++ to C. Added support for multi-byte chars.
// Changed to keep spaces.
- i = reslen = z = 0;
+ int i = reslen = z = 0;
while ((c = word[i]) != NUL) {
// Start with the first rule that has the character in the word.
- n = slang->sl_sal_first[c & 0xff];
+ int n = slang->sl_sal_first[c & 0xff];
z0 = 0;
if (n >= 0) {
@@ -6102,7 +2824,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// If c is 0x300 need extra check for the end of the array, as
// (c & 0xff) is NUL.
for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff)
- && ws[0] != NUL; ++n) {
+ && ws[0] != NUL; n++) {
// Quickly skip entries that don't match the word. Most
// entries are less than three chars, optimize for that.
if (c != ws[0]) {
@@ -6114,7 +2836,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
continue;
}
if (k > 2) {
- for (j = 2; j < k; ++j) {
+ for (j = 2; j < k; j++) {
if (word[i + j] != ws[j]) {
break;
}
@@ -6128,12 +2850,12 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
if ((pf = smp[n].sm_oneof_w) != NULL) {
// Check for match with one of the chars in "sm_oneof".
while (*pf != NUL && *pf != word[i + k]) {
- ++pf;
+ pf++;
}
if (*pf == NUL) {
continue;
}
- ++k;
+ k++;
}
char_u *s = smp[n].sm_rules;
pri = 5; // default priority
@@ -6175,7 +2897,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// Test follow-up rule for "word[i + k]"; loop over
// all entries with the same index byte.
for (; ((ws = smp[n0].sm_lead_w)[0] & 0xff)
- == (c0 & 0xff); ++n0) {
+ == (c0 & 0xff); n0++) {
// Quickly skip entries that don't match the word.
if (c0 != ws[0]) {
continue;
@@ -6187,7 +2909,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
if (k0 > 2) {
pf = word + i + k + 1;
- for (j = 2; j < k0; ++j) {
+ for (j = 2; j < k0; j++) {
if (*pf++ != ws[j]) {
break;
}
@@ -6203,12 +2925,12 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
// Check for match with one of the chars in
// "sm_oneof".
while (*pf != NUL && *pf != word[i + k0]) {
- ++pf;
+ pf++;
}
if (*pf == NUL) {
continue;
}
- ++k0;
+ k0++;
}
p0 = 5;
@@ -6329,8 +3051,8 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
}
// Convert wide characters in "wres" to a multi-byte string in "res".
- l = 0;
- for (n = 0; n < reslen; n++) {
+ int l = 0;
+ for (int n = 0; n < reslen; n++) {
l += utf_char2bytes(wres[n], (char *)res + l);
if (l + MB_MAXBYTES > MAXWLEN) {
break;
@@ -6339,499 +3061,6 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res)
res[l] = NUL;
}
-/// Compute a score for two sound-a-like words.
-/// This permits up to two inserts/deletes/swaps/etc. to keep things fast.
-/// Instead of a generic loop we write out the code. That keeps it fast by
-/// avoiding checks that will not be possible.
-///
-/// @param goodstart sound-folded good word
-/// @param badstart sound-folded bad word
-static int soundalike_score(char_u *goodstart, char_u *badstart)
-{
- char_u *goodsound = goodstart;
- char_u *badsound = badstart;
- int goodlen;
- int badlen;
- int n;
- char_u *pl, *ps;
- char_u *pl2, *ps2;
- int score = 0;
-
- // Adding/inserting "*" at the start (word starts with vowel) shouldn't be
- // counted so much, vowels in the middle of the word aren't counted at all.
- if ((*badsound == '*' || *goodsound == '*') && *badsound != *goodsound) {
- if ((badsound[0] == NUL && goodsound[1] == NUL)
- || (goodsound[0] == NUL && badsound[1] == NUL)) {
- // changing word with vowel to word without a sound
- return SCORE_DEL;
- }
- if (badsound[0] == NUL || goodsound[0] == NUL) {
- // more than two changes
- return SCORE_MAXMAX;
- }
-
- if (badsound[1] == goodsound[1]
- || (badsound[1] != NUL
- && goodsound[1] != NUL
- && badsound[2] == goodsound[2])) {
- // handle like a substitute
- } else {
- score = 2 * SCORE_DEL / 3;
- if (*badsound == '*') {
- ++badsound;
- } else {
- ++goodsound;
- }
- }
- }
-
- goodlen = (int)STRLEN(goodsound);
- badlen = (int)STRLEN(badsound);
-
- // Return quickly if the lengths are too different to be fixed by two
- // changes.
- n = goodlen - badlen;
- if (n < -2 || n > 2) {
- return SCORE_MAXMAX;
- }
-
- if (n > 0) {
- pl = goodsound; // goodsound is longest
- ps = badsound;
- } else {
- pl = badsound; // badsound is longest
- ps = goodsound;
- }
-
- // Skip over the identical part.
- while (*pl == *ps && *pl != NUL) {
- ++pl;
- ++ps;
- }
-
- switch (n) {
- case -2:
- case 2:
- // Must delete two characters from "pl".
- ++pl; // first delete
- while (*pl == *ps) {
- ++pl;
- ++ps;
- }
- // strings must be equal after second delete
- if (STRCMP(pl + 1, ps) == 0) {
- return score + SCORE_DEL * 2;
- }
-
- // Failed to compare.
- break;
-
- case -1:
- case 1:
- // Minimal one delete from "pl" required.
-
- // 1: delete
- pl2 = pl + 1;
- ps2 = ps;
- while (*pl2 == *ps2) {
- if (*pl2 == NUL) { // reached the end
- return score + SCORE_DEL;
- }
- ++pl2;
- ++ps2;
- }
-
- // 2: delete then swap, then rest must be equal
- if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
- && STRCMP(pl2 + 2, ps2 + 2) == 0) {
- return score + SCORE_DEL + SCORE_SWAP;
- }
-
- // 3: delete then substitute, then the rest must be equal
- if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
- return score + SCORE_DEL + SCORE_SUBST;
- }
-
- // 4: first swap then delete
- if (pl[0] == ps[1] && pl[1] == ps[0]) {
- pl2 = pl + 2; // swap, skip two chars
- ps2 = ps + 2;
- while (*pl2 == *ps2) {
- ++pl2;
- ++ps2;
- }
- // delete a char and then strings must be equal
- if (STRCMP(pl2 + 1, ps2) == 0) {
- return score + SCORE_SWAP + SCORE_DEL;
- }
- }
-
- // 5: first substitute then delete
- pl2 = pl + 1; // substitute, skip one char
- ps2 = ps + 1;
- while (*pl2 == *ps2) {
- ++pl2;
- ++ps2;
- }
- // delete a char and then strings must be equal
- if (STRCMP(pl2 + 1, ps2) == 0) {
- return score + SCORE_SUBST + SCORE_DEL;
- }
-
- // Failed to compare.
- break;
-
- case 0:
- // Lengths are equal, thus changes must result in same length: An
- // insert is only possible in combination with a delete.
- // 1: check if for identical strings
- if (*pl == NUL) {
- return score;
- }
-
- // 2: swap
- if (pl[0] == ps[1] && pl[1] == ps[0]) {
- pl2 = pl + 2; // swap, skip two chars
- ps2 = ps + 2;
- while (*pl2 == *ps2) {
- if (*pl2 == NUL) { // reached the end
- return score + SCORE_SWAP;
- }
- ++pl2;
- ++ps2;
- }
- // 3: swap and swap again
- if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
- && STRCMP(pl2 + 2, ps2 + 2) == 0) {
- return score + SCORE_SWAP + SCORE_SWAP;
- }
-
- // 4: swap and substitute
- if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
- return score + SCORE_SWAP + SCORE_SUBST;
- }
- }
-
- // 5: substitute
- pl2 = pl + 1;
- ps2 = ps + 1;
- while (*pl2 == *ps2) {
- if (*pl2 == NUL) { // reached the end
- return score + SCORE_SUBST;
- }
- ++pl2;
- ++ps2;
- }
-
- // 6: substitute and swap
- if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
- && STRCMP(pl2 + 2, ps2 + 2) == 0) {
- return score + SCORE_SUBST + SCORE_SWAP;
- }
-
- // 7: substitute and substitute
- if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
- return score + SCORE_SUBST + SCORE_SUBST;
- }
-
- // 8: insert then delete
- pl2 = pl;
- ps2 = ps + 1;
- while (*pl2 == *ps2) {
- ++pl2;
- ++ps2;
- }
- if (STRCMP(pl2 + 1, ps2) == 0) {
- return score + SCORE_INS + SCORE_DEL;
- }
-
- // 9: delete then insert
- pl2 = pl + 1;
- ps2 = ps;
- while (*pl2 == *ps2) {
- ++pl2;
- ++ps2;
- }
- if (STRCMP(pl2, ps2 + 1) == 0) {
- return score + SCORE_INS + SCORE_DEL;
- }
-
- // Failed to compare.
- break;
- }
-
- return SCORE_MAXMAX;
-}
-
-// Compute the "edit distance" to turn "badword" into "goodword". The less
-// deletes/inserts/substitutes/swaps are required the lower the score.
-//
-// The algorithm is described by Du and Chang, 1992.
-// The implementation of the algorithm comes from Aspell editdist.cpp,
-// edit_distance(). It has been converted from C++ to C and modified to
-// support multi-byte characters.
-static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword)
-{
- int *cnt;
- int j, i;
- int t;
- int bc, gc;
- int pbc, pgc;
- int wbadword[MAXWLEN];
- int wgoodword[MAXWLEN];
-
- // Lengths with NUL.
- int badlen;
- int goodlen;
- {
- // Get the characters from the multi-byte strings and put them in an
- // int array for easy access.
- badlen = 0;
- for (const char_u *p = badword; *p != NUL;) {
- wbadword[badlen++] = mb_cptr2char_adv(&p);
- }
- wbadword[badlen++] = 0;
- goodlen = 0;
- for (const char_u *p = goodword; *p != NUL;) {
- wgoodword[goodlen++] = mb_cptr2char_adv(&p);
- }
- wgoodword[goodlen++] = 0;
- }
-
- // We use "cnt" as an array: CNT(badword_idx, goodword_idx).
-#define CNT(a, b) cnt[(a) + (b) * (badlen + 1)]
- cnt = xmalloc(sizeof(int) * ((size_t)badlen + 1) * ((size_t)goodlen + 1));
-
- CNT(0, 0) = 0;
- for (j = 1; j <= goodlen; ++j) {
- CNT(0, j) = CNT(0, j - 1) + SCORE_INS;
- }
-
- for (i = 1; i <= badlen; ++i) {
- CNT(i, 0) = CNT(i - 1, 0) + SCORE_DEL;
- for (j = 1; j <= goodlen; j++) {
- bc = wbadword[i - 1];
- gc = wgoodword[j - 1];
- if (bc == gc) {
- CNT(i, j) = CNT(i - 1, j - 1);
- } else {
- // Use a better score when there is only a case difference.
- if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) {
- CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1);
- } else {
- // For a similar character use SCORE_SIMILAR.
- if (slang != NULL
- && slang->sl_has_map
- && similar_chars(slang, gc, bc)) {
- CNT(i, j) = SCORE_SIMILAR + CNT(i - 1, j - 1);
- } else {
- CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1);
- }
- }
-
- if (i > 1 && j > 1) {
- pbc = wbadword[i - 2];
- pgc = wgoodword[j - 2];
- if (bc == pgc && pbc == gc) {
- t = SCORE_SWAP + CNT(i - 2, j - 2);
- if (t < CNT(i, j)) {
- CNT(i, j) = t;
- }
- }
- }
- t = SCORE_DEL + CNT(i - 1, j);
- if (t < CNT(i, j)) {
- CNT(i, j) = t;
- }
- t = SCORE_INS + CNT(i, j - 1);
- if (t < CNT(i, j)) {
- CNT(i, j) = t;
- }
- }
- }
- }
-
- i = CNT(badlen - 1, goodlen - 1);
- xfree(cnt);
- return i;
-}
-
-// Like spell_edit_score(), but with a limit on the score to make it faster.
-// May return SCORE_MAXMAX when the score is higher than "limit".
-//
-// This uses a stack for the edits still to be tried.
-// The idea comes from Aspell leditdist.cpp. Rewritten in C and added support
-// for multi-byte characters.
-static int spell_edit_score_limit(slang_T *slang, char_u *badword, char_u *goodword, int limit)
-{
- return spell_edit_score_limit_w(slang, badword, goodword, limit);
-}
-
-// Multi-byte version of spell_edit_score_limit().
-// Keep it in sync with the above!
-static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goodword, int limit)
-{
- limitscore_T stack[10]; // allow for over 3 * 2 edits
- int stackidx;
- int bi, gi;
- int bi2, gi2;
- int bc, gc;
- int score;
- int score_off;
- int minscore;
- int round;
- int wbadword[MAXWLEN];
- int wgoodword[MAXWLEN];
-
- // Get the characters from the multi-byte strings and put them in an
- // int array for easy access.
- bi = 0;
- for (const char_u *p = badword; *p != NUL;) {
- wbadword[bi++] = mb_cptr2char_adv(&p);
- }
- wbadword[bi++] = 0;
- gi = 0;
- for (const char_u *p = goodword; *p != NUL;) {
- wgoodword[gi++] = mb_cptr2char_adv(&p);
- }
- wgoodword[gi++] = 0;
-
- // The idea is to go from start to end over the words. So long as
- // characters are equal just continue, this always gives the lowest score.
- // When there is a difference try several alternatives. Each alternative
- // increases "score" for the edit distance. Some of the alternatives are
- // pushed unto a stack and tried later, some are tried right away. At the
- // end of the word the score for one alternative is known. The lowest
- // possible score is stored in "minscore".
- stackidx = 0;
- bi = 0;
- gi = 0;
- score = 0;
- minscore = limit + 1;
-
- for (;;) {
- // Skip over an equal part, score remains the same.
- for (;;) {
- bc = wbadword[bi];
- gc = wgoodword[gi];
-
- if (bc != gc) { // stop at a char that's different
- break;
- }
- if (bc == NUL) { // both words end
- if (score < minscore) {
- minscore = score;
- }
- goto pop; // do next alternative
- }
- ++bi;
- ++gi;
- }
-
- if (gc == NUL) { // goodword ends, delete badword chars
- do {
- if ((score += SCORE_DEL) >= minscore) {
- goto pop; // do next alternative
- }
- } while (wbadword[++bi] != NUL);
- minscore = score;
- } else if (bc == NUL) { // badword ends, insert badword chars
- do {
- if ((score += SCORE_INS) >= minscore) {
- goto pop; // do next alternative
- }
- } while (wgoodword[++gi] != NUL);
- minscore = score;
- } else { // both words continue
- // If not close to the limit, perform a change. Only try changes
- // that may lead to a lower score than "minscore".
- // round 0: try deleting a char from badword
- // round 1: try inserting a char in badword
- for (round = 0; round <= 1; ++round) {
- score_off = score + (round == 0 ? SCORE_DEL : SCORE_INS);
- if (score_off < minscore) {
- if (score_off + SCORE_EDIT_MIN >= minscore) {
- // Near the limit, rest of the words must match. We
- // can check that right now, no need to push an item
- // onto the stack.
- bi2 = bi + 1 - round;
- gi2 = gi + round;
- while (wgoodword[gi2] == wbadword[bi2]) {
- if (wgoodword[gi2] == NUL) {
- minscore = score_off;
- break;
- }
- ++bi2;
- ++gi2;
- }
- } else {
- // try deleting a character from badword later
- stack[stackidx].badi = bi + 1 - round;
- stack[stackidx].goodi = gi + round;
- stack[stackidx].score = score_off;
- ++stackidx;
- }
- }
- }
-
- if (score + SCORE_SWAP < minscore) {
- // If swapping two characters makes a match then the
- // substitution is more expensive, thus there is no need to
- // try both.
- if (gc == wbadword[bi + 1] && bc == wgoodword[gi + 1]) {
- // Swap two characters, that is: skip them.
- gi += 2;
- bi += 2;
- score += SCORE_SWAP;
- continue;
- }
- }
-
- // Substitute one character for another which is the same
- // thing as deleting a character from both goodword and badword.
- // Use a better score when there is only a case difference.
- if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) {
- score += SCORE_ICASE;
- } else {
- // For a similar character use SCORE_SIMILAR.
- if (slang != NULL
- && slang->sl_has_map
- && similar_chars(slang, gc, bc)) {
- score += SCORE_SIMILAR;
- } else {
- score += SCORE_SUBST;
- }
- }
-
- if (score < minscore) {
- // Do the substitution.
- ++gi;
- ++bi;
- continue;
- }
- }
-pop:
- // Get here to try the next alternative, pop it from the stack.
- if (stackidx == 0) { // stack is empty, finished
- break;
- }
-
- // pop an item from the stack
- --stackidx;
- gi = stack[stackidx].goodi;
- bi = stack[stackidx].badi;
- score = stack[stackidx].score;
- }
-
- // When the score goes over "limit" it may actually be much higher.
- // Return a very large number to avoid going below the limit when giving a
- // bonus.
- if (minscore > limit) {
- return SCORE_MAXMAX;
- }
- return minscore;
-}
-
// ":spellinfo"
void ex_spellinfo(exarg_T *eap)
{
@@ -6863,20 +3092,19 @@ void ex_spellinfo(exarg_T *eap)
// ":spelldump"
void ex_spelldump(exarg_T *eap)
{
- char *spl;
- long dummy;
-
if (no_spell_checking(curwin)) {
return;
}
+ char *spl;
+ long dummy;
(void)get_option_value("spl", &dummy, &spl, OPT_LOCAL);
// Create a new empty buffer in a new window.
do_cmdline_cmd("new");
// enable spelling locally in the new window
- set_option_value("spell", true, "", OPT_LOCAL);
- set_option_value("spl", dummy, spl, OPT_LOCAL);
+ set_option_value_give_err("spell", true, "", OPT_LOCAL);
+ set_option_value_give_err("spl", dummy, spl, OPT_LOCAL);
xfree(spl);
if (!buf_is_empty(curbuf)) {
@@ -6889,7 +3117,7 @@ void ex_spelldump(exarg_T *eap)
if (curbuf->b_ml.ml_line_count > 1) {
ml_delete(curbuf->b_ml.ml_line_count, false);
}
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/// Go through all possible words and:
@@ -6912,7 +3140,6 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
char_u *byts;
idx_T *idxs;
linenr_T lnum = 0;
- int round;
int depth;
int n;
int flags;
@@ -6940,7 +3167,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// Find out if we can support regions: All languages must support the same
// regions or none at all.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
p = lp->lp_slang->sl_regions;
if (p[0] != 0) {
@@ -6963,7 +3190,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
}
// Loop over all files loaded for the entries in 'spelllang'.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
slang = lp->lp_slang;
if (slang->sl_fbyts == NULL) { // reloading failed
@@ -6985,7 +3212,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
// round 1: case-folded tree
// round 2: keep-case tree
- for (round = 1; round <= 2; ++round) {
+ for (int round = 1; round <= 2; round++) {
if (round == 1) {
dumpflags &= ~DUMPFLAG_KEEPCASE;
byts = slang->sl_fbyts;
@@ -7005,13 +3232,13 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
&& (pat == NULL || !ins_compl_interrupted())) {
if (curi[depth] > byts[arridx[depth]]) {
// Done all bytes at this node, go up one level.
- --depth;
+ depth--;
line_breakcheck();
ins_compl_check_keys(50, false);
} else {
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0 || depth >= MAXWLEN - 1) {
// End of word or reached maximum length, deal with the
@@ -7038,7 +3265,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
dump_word(slang, word, pat, dir,
dumpflags, flags, lnum);
if (pat == NULL) {
- ++lnum;
+ lnum++;
}
}
@@ -7063,7 +3290,7 @@ void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg)
assert(depth >= 0);
if (depth <= patlen
&& mb_strnicmp(word, pat, (size_t)depth) != 0) {
- --depth;
+ depth--;
}
}
}
@@ -7079,10 +3306,8 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
{
bool keepcap = false;
char_u *p;
- char_u *tw;
char_u cword[MAXWLEN];
char_u badword[MAXWLEN + 10];
- int i;
int flags = wordflags;
if (dumpflags & DUMPFLAG_ONECAP) {
@@ -7104,7 +3329,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
keepcap = true;
}
}
- tw = p;
+ char_u *tw = p;
if (pat == NULL) {
// Add flags and regions after a slash.
@@ -7120,7 +3345,7 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir,
STRCAT(badword, "?");
}
if (flags & WF_REGION) {
- for (i = 0; i < 7; i++) {
+ for (int i = 0; i < 7; i++) {
if (flags & (0x10000 << i)) {
const size_t badword_len = STRLEN(badword);
snprintf((char *)badword + badword_len,
@@ -7171,46 +3396,40 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
char_u prefix[MAXWLEN];
char_u word_up[MAXWLEN];
bool has_word_up = false;
- int c;
- char_u *byts;
- idx_T *idxs;
linenr_T lnum = startlnum;
- int depth;
- int n;
- int len;
- int i;
// If the word starts with a lower-case letter make the word with an
// upper-case letter in word_up[].
- c = utf_ptr2char((char *)word);
+ int c = utf_ptr2char((char *)word);
if (SPELL_TOUPPER(c) != c) {
onecap_copy(word, word_up, true);
has_word_up = true;
}
- byts = slang->sl_pbyts;
- idxs = slang->sl_pidxs;
+ char_u *byts = slang->sl_pbyts;
+ idx_T *idxs = slang->sl_pidxs;
if (byts != NULL) { // array not is empty
// Loop over all prefixes, building them byte-by-byte in prefix[].
// When at the end of a prefix check that it supports "flags".
- depth = 0;
+ int depth = 0;
arridx[0] = 0;
curi[0] = 1;
while (depth >= 0 && !got_int) {
- n = arridx[depth];
- len = byts[n];
+ int n = arridx[depth];
+ int len = byts[n];
if (curi[depth] > len) {
// Done all bytes at this node, go up one level.
- --depth;
+ depth--;
line_breakcheck();
} else {
// Do one more byte at this node.
n += curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0) {
// End of prefix, find out how many IDs there are.
- for (i = 1; i < len; ++i) {
+ int i;
+ for (i = 1; i < len; i++) {
if (byts[n + i] != 0) {
break;
}
@@ -7221,10 +3440,9 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
if (c != 0) {
STRLCPY(prefix + depth, word, MAXWLEN - depth);
dump_word(slang, prefix, pat, dir, dumpflags,
- (c & WF_RAREPFX) ? (flags | WF_RARE)
- : flags, lnum);
+ (c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum);
if (lnum != 0) {
- ++lnum;
+ lnum++;
}
}
@@ -7237,10 +3455,9 @@ static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Directi
if (c != 0) {
STRLCPY(prefix + depth, word_up, MAXWLEN - depth);
dump_word(slang, prefix, pat, dir, dumpflags,
- (c & WF_RAREPFX) ? (flags | WF_RARE)
- : flags, lnum);
+ (c & WF_RAREPFX) ? (flags | WF_RARE) : flags, lnum);
if (lnum != 0) {
- ++lnum;
+ lnum++;
}
}
}
@@ -7276,16 +3493,14 @@ char_u *spell_to_word_end(char_u *start, win_T *win)
// Returns the column number of the word.
int spell_word_start(int startcol)
{
- char_u *line;
- char_u *p;
- int col = 0;
-
if (no_spell_checking(curwin)) {
return startcol;
}
+ char_u *line = get_cursor_line_ptr();
+ char_u *p;
+
// Find a word character before "startcol".
- line = get_cursor_line_ptr();
for (p = line + startcol; p > line;) {
MB_PTR_BACK(line, p);
if (spell_iswordp_nmw(p, curwin)) {
@@ -7293,6 +3508,8 @@ int spell_word_start(int startcol)
}
}
+ int col = 0;
+
// Go back to start of the word.
while (p > line) {
col = (int)(p - line);
@@ -7327,3 +3544,70 @@ int expand_spelling(linenr_T lnum, char_u *pat, char ***matchp)
*matchp = ga.ga_data;
return ga.ga_len;
}
+
+/// @return true if "val" is a valid 'spelllang' value.
+bool valid_spelllang(const char *val)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return valid_name(val, ".-_,@");
+}
+
+/// @return true if "val" is a valid 'spellfile' value.
+bool valid_spellfile(const char *val)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ for (const char_u *s = (char_u *)val; *s != NUL; s++) {
+ if (!vim_isfilec(*s) && *s != ',' && *s != ' ') {
+ return false;
+ }
+ }
+ return true;
+}
+
+char *did_set_spell_option(bool is_spellfile)
+{
+ char *errmsg = NULL;
+
+ if (is_spellfile) {
+ int l = (int)STRLEN(curwin->w_s->b_p_spf);
+ if (l > 0
+ && (l < 4 || STRCMP(curwin->w_s->b_p_spf + l - 4, ".add") != 0)) {
+ errmsg = e_invarg;
+ }
+ }
+
+ if (errmsg == NULL) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_buffer == curbuf && wp->w_p_spell) {
+ errmsg = did_set_spelllang(wp);
+ break;
+ }
+ }
+ }
+
+ return errmsg;
+}
+
+/// Set curbuf->b_cap_prog to the regexp program for 'spellcapcheck'.
+/// Return error message when failed, NULL when OK.
+char *compile_cap_prog(synblock_T *synblock)
+ FUNC_ATTR_NONNULL_ALL
+{
+ regprog_T *rp = synblock->b_cap_prog;
+
+ if (synblock->b_p_spc == NULL || *synblock->b_p_spc == NUL) {
+ synblock->b_cap_prog = NULL;
+ } else {
+ // Prepend a ^ so that we only match at one column
+ char *re = concat_str("^", synblock->b_p_spc);
+ synblock->b_cap_prog = vim_regcomp(re, RE_MAGIC);
+ xfree(re);
+ if (synblock->b_cap_prog == NULL) {
+ synblock->b_cap_prog = rp; // restore the previous program
+ return e_invarg;
+ }
+ }
+
+ vim_regfree(rp);
+ return NULL;
+}
diff --git a/src/nvim/spell_defs.h b/src/nvim/spell_defs.h
index 222d103f5d..27b9777dc2 100644
--- a/src/nvim/spell_defs.h
+++ b/src/nvim/spell_defs.h
@@ -35,6 +35,8 @@ typedef int idx_T;
#define WF_FIXCAP 0x40 // keep-case word, allcap not allowed
#define WF_KEEPCAP 0x80 // keep-case word
+#define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP)
+
// for <flags2>, shifted up one byte to be used in wn_flags
#define WF_HAS_AFF 0x0100 // word includes affix
#define WF_NEEDCOMP 0x0200 // word only valid in compound
@@ -113,9 +115,9 @@ typedef struct slang_S slang_T;
struct slang_S {
slang_T *sl_next; // next language
- char_u *sl_name; // language name "en", "en.rare", "nl", etc.
- char_u *sl_fname; // name of .spl file
- bool sl_add; // true if it's a .add file.
+ char_u *sl_name; // language name "en", "en.rare", "nl", etc.
+ char *sl_fname; // name of .spl file
+ bool sl_add; // true if it's a .add file.
char_u *sl_fbyts; // case-folded word bytes
long sl_fbyts_len; // length of sl_fbyts
@@ -208,56 +210,6 @@ typedef struct {
char_u st_upper[256]; // chars: upper case
} spelltab_T;
-// For finding suggestions: At each node in the tree these states are tried:
-typedef enum {
- STATE_START = 0, // At start of node check for NUL bytes (goodword
- // ends); if badword ends there is a match, otherwise
- // try splitting word.
- STATE_NOPREFIX, // try without prefix
- STATE_SPLITUNDO, // Undo splitting.
- STATE_ENDNUL, // Past NUL bytes at start of the node.
- STATE_PLAIN, // Use each byte of the node.
- STATE_DEL, // Delete a byte from the bad word.
- STATE_INS_PREP, // Prepare for inserting bytes.
- STATE_INS, // Insert a byte in the bad word.
- STATE_SWAP, // Swap two bytes.
- STATE_UNSWAP, // Undo swap two characters.
- STATE_SWAP3, // Swap two characters over three.
- STATE_UNSWAP3, // Undo Swap two characters over three.
- STATE_UNROT3L, // Undo rotate three characters left
- STATE_UNROT3R, // Undo rotate three characters right
- STATE_REP_INI, // Prepare for using REP items.
- STATE_REP, // Use matching REP items from the .aff file.
- STATE_REP_UNDO, // Undo a REP item replacement.
- STATE_FINAL, // End of this node.
-} state_T;
-
-// Struct to keep the state at each level in suggest_try_change().
-typedef struct trystate_S {
- state_T ts_state; // state at this level, STATE_
- int ts_score; // score
- idx_T ts_arridx; // index in tree array, start of node
- int16_t ts_curi; // index in list of child nodes
- char_u ts_fidx; // index in fword[], case-folded bad word
- char_u ts_fidxtry; // ts_fidx at which bytes may be changed
- char_u ts_twordlen; // valid length of tword[]
- char_u ts_prefixdepth; // stack depth for end of prefix or
- // PFD_PREFIXTREE or PFD_NOPREFIX
- char_u ts_flags; // TSF_ flags
- char_u ts_tcharlen; // number of bytes in tword character
- char_u ts_tcharidx; // current byte index in tword character
- char_u ts_isdiff; // DIFF_ values
- char_u ts_fcharstart; // index in fword where badword char started
- char_u ts_prewordlen; // length of word in "preword[]"
- char_u ts_splitoff; // index in "tword" after last split
- char_u ts_splitfidx; // "ts_fidx" at word split
- char_u ts_complen; // nr of compound words used
- char_u ts_compsplit; // index for "compflags" where word was spit
- char_u ts_save_badflags; // su_badflags saved here
- char_u ts_delidx; // index in fword for char that was deleted,
- // valid when "ts_flags" has TSF_DIDDEL
-} trystate_T;
-
// Use our own character-case definitions, because the current locale may
// differ from what the .spl file uses.
// These must not be called with negative number!
@@ -290,4 +242,17 @@ typedef enum {
SPELL_ADD_RARE = 2,
} SpellAddType;
+typedef struct wordcount_S {
+ uint16_t wc_count; ///< nr of times word was seen
+ char_u wc_word[1]; ///< word, actually longer
+} wordcount_T;
+
+#define WC_KEY_OFF offsetof(wordcount_T, wc_word)
+#define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF))
+#define MAXWORDCOUNT 0xffff
+
+// Remember what "z?" replaced.
+extern char_u *repl_from;
+extern char_u *repl_to;
+
#endif // NVIM_SPELL_DEFS_H
diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c
index 9f21e24d4c..0f8034312e 100644
--- a/src/nvim/spellfile.c
+++ b/src/nvim/spellfile.c
@@ -230,9 +230,11 @@
#include <stdio.h>
#include <wctype.h>
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
#include "nvim/ex_cmds2.h"
#include "nvim/fileio.h"
#include "nvim/memline.h"
@@ -242,7 +244,7 @@
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/spell.h"
#include "nvim/spell_defs.h"
#include "nvim/spellfile.h"
@@ -438,7 +440,7 @@ typedef struct spellinfo_S {
sblock_T *si_blocks; // memory blocks used
long si_blocks_cnt; // memory blocks allocated
- int si_did_emsg; // TRUE when ran out of memory
+ int si_did_emsg; // true when ran out of memory
long si_compress_cnt; // words to add before lowering
// compression limit
@@ -452,7 +454,7 @@ typedef struct spellinfo_S {
int si_ascii; // handling only ASCII words
int si_add; // addition file
- int si_clear_chartab; // when TRUE clear char tables
+ int si_clear_chartab; // when true clear char tables
int si_region; // region mask
vimconv_T si_conv; // for conversion to 'encoding'
int si_memtot; // runtime memory used
@@ -576,11 +578,10 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
char_u *p;
int n;
int len;
- char_u *save_sourcing_name = (char_u *)sourcing_name;
- linenr_T save_sourcing_lnum = sourcing_lnum;
slang_T *lp = NULL;
int c = 0;
int res;
+ bool did_estack_push = false;
fd = os_fopen((char *)fname, "r");
if (fd == NULL) {
@@ -603,7 +604,7 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
lp = slang_alloc(lang);
// Remember the file name, used to reload the file when it's updated.
- lp->sl_fname = vim_strsave(fname);
+ lp->sl_fname = (char *)vim_strsave(fname);
// Check for .add.spl.
lp->sl_add = strstr(path_tail((char *)fname), SPL_FNAME_ADD) != NULL;
@@ -612,8 +613,8 @@ slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool sile
}
// Set sourcing_name, so that error messages mention the file name.
- sourcing_name = (char *)fname;
- sourcing_lnum = 0;
+ estack_push(ETYPE_SPELL, (char *)fname, 0);
+ did_estack_push = true;
// <HEADER>: <fileID>
const int scms_ret = spell_check_magic_string(fd);
@@ -809,8 +810,9 @@ endOK:
if (fd != NULL) {
fclose(fd);
}
- sourcing_name = (char *)save_sourcing_name;
- sourcing_lnum = save_sourcing_lnum;
+ if (did_estack_push) {
+ estack_pop();
+ }
return lp;
}
@@ -838,27 +840,27 @@ static void tree_count_words(char_u *byts, idx_T *idxs)
wordcount[depth - 1] += wordcount[depth];
}
- --depth;
+ depth--;
fast_breakcheck();
} else {
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0) {
// End of word, count it.
- ++wordcount[depth];
+ wordcount[depth]++;
// Skip over any other NUL bytes (same word with different
// flags).
while (byts[n + 1] == 0) {
- ++n;
- ++curi[depth];
+ n++;
+ curi[depth]++;
}
} else {
// Normal char, go one level deeper to count the words.
- ++depth;
+ depth++;
arridx[depth] = idxs[n];
curi[depth] = 1;
wordcount[depth] = 0;
@@ -867,12 +869,12 @@ static void tree_count_words(char_u *byts, idx_T *idxs)
}
}
-// Load the .sug files for languages that have one and weren't loaded yet.
+/// Load the .sug files for languages that have one and weren't loaded yet.
void suggest_load_files(void)
{
langp_T *lp;
slang_T *slang;
- char_u *dotp;
+ char *dotp;
FILE *fd;
char_u buf[MAXWLEN];
int i;
@@ -883,7 +885,7 @@ void suggest_load_files(void)
int c;
// Do this for all languages that support sound folding.
- for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) {
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
slang = lp->lp_slang;
if (slang->sl_sugtime != 0 && !slang->sl_sugloaded) {
@@ -892,12 +894,12 @@ void suggest_load_files(void)
// don't try again and again.
slang->sl_sugloaded = true;
- dotp = STRRCHR(slang->sl_fname, '.');
+ dotp = strrchr(slang->sl_fname, '.');
if (dotp == NULL || FNAMECMP(dotp, ".spl") != 0) {
continue;
}
STRCPY(dotp, ".sug");
- fd = os_fopen((char *)slang->sl_fname, "r");
+ fd = os_fopen(slang->sl_fname, "r");
if (fd == NULL) {
goto nextone;
}
@@ -958,7 +960,7 @@ someerror:
// Read all the wordnr lists into the buffer, one NUL terminated
// list per line.
ga_init(&ga, 1, 100);
- for (wordnr = 0; wordnr < wcount; ++wordnr) {
+ for (wordnr = 0; wordnr < wcount; wordnr++) {
ga.ga_len = 0;
for (;;) {
c = getc(fd); // <sugline>
@@ -1140,10 +1142,10 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first)
}
// Fill the first-index table.
- for (int i = 0; i < 256; ++i) {
+ for (int i = 0; i < 256; i++) {
first[i] = -1;
}
- for (int i = 0; i < gap->ga_len; ++i) {
+ for (int i = 0; i < gap->ga_len; i++) {
ftp = &((fromto_T *)gap->ga_data)[i];
if (first[*ftp->ft_from] == -1) {
first[*ftp->ft_from] = (int16_t)i;
@@ -1198,7 +1200,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
// Read up to the first special char into sm_lead.
int i = 0;
- for (; i < ccnt; ++i) {
+ for (; i < ccnt; i++) {
c = getc(fd); // <salfrom>
if (vim_strchr("0123456789(-<^$", c) != NULL) {
break;
@@ -1211,7 +1213,7 @@ static int read_sal_section(FILE *fd, slang_T *slang)
// Put (abc) chars in sm_oneof, if any.
if (c == '(') {
smp->sm_oneof = p;
- for (++i; i < ccnt; ++i) {
+ for (++i; i < ccnt; i++) {
c = getc(fd); // <salfrom>
if (c == ')') {
break;
@@ -1297,7 +1299,7 @@ static int read_words_section(FILE *fd, slang_T *lp, int len)
while (done < len) {
// Read one word at a time.
- for (i = 0;; ++i) {
+ for (i = 0;; i++) {
c = getc(fd);
if (c == EOF) {
return SP_TRUNCERROR;
@@ -1369,21 +1371,21 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
if (todo < 2) {
return SP_FORMERROR; // need at least two bytes
}
- --todo;
+ todo--;
c = getc(fd); // <compmax>
if (c < 2) {
c = MAXWLEN;
}
slang->sl_compmax = c;
- --todo;
+ todo--;
c = getc(fd); // <compminlen>
if (c < 1) {
c = 0;
}
slang->sl_compminlen = c;
- --todo;
+ todo--;
c = getc(fd); // <compsylmax>
if (c < 1) {
c = MAXWLEN;
@@ -1394,9 +1396,9 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
if (c != 0) {
ungetc(c, fd); // be backwards compatible with Vim 7.0b
} else {
- --todo;
+ todo--;
c = getc(fd); // only use the lower byte for now
- --todo;
+ todo--;
slang->sl_compoptions = c;
gap = &slang->sl_comppat;
@@ -1408,8 +1410,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len)
ga_init(gap, sizeof(char_u *), c);
ga_grow(gap, c);
while (--c >= 0) {
- ((char_u **)(gap->ga_data))[gap->ga_len++] =
- read_cnt_string(fd, 1, &cnt);
+ ((char **)(gap->ga_data))[gap->ga_len++] = (char *)read_cnt_string(fd, 1, &cnt);
// <comppatlen> <comppattext>
if (cnt < 0) {
return cnt;
@@ -1596,7 +1597,7 @@ static void set_sal_first(slang_T *lp)
garray_T *gap = &lp->sl_sal;
sfirst = lp->sl_sal_first;
- for (int i = 0; i < 256; ++i) {
+ for (int i = 0; i < 256; i++) {
sfirst[i] = -1;
}
smp = (salitem_T *)gap->ga_data;
@@ -1731,7 +1732,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx
byts[idx++] = (char_u)len;
// Read the byte values, flag/region bytes and shared indexes.
- for (i = 1; i <= len; ++i) {
+ for (i = 1; i <= len; i++) {
c = getc(fd); // <byte>
if (c < 0) {
return SP_TRUNCERROR;
@@ -1794,7 +1795,7 @@ static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx
// Recursively read the children for non-shared siblings.
// Skip the end-of-word ones (zero byte value) and the shared ones (and
// remove SHARED_MASK)
- for (i = 1; i <= len; ++i) {
+ for (i = 1; i <= len; i++) {
if (byts[startidx + i] != 0) {
if (idxs[startidx + i] & SHARED_MASK) {
idxs[startidx + i] &= ~SHARED_MASK;
@@ -1821,13 +1822,13 @@ static void spell_reload_one(char_u *fname, bool added_word)
bool didit = false;
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare((char *)fname, (char *)slang->sl_fname, false, true) == kEqualFiles) {
+ if (path_full_compare((char *)fname, slang->sl_fname, false, true) == kEqualFiles) {
slang_clear(slang);
if (spell_load_file(fname, NULL, slang, false) == NULL) {
// reloading failed, clear the language
slang_clear(slang);
}
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
didit = true;
}
}
@@ -1862,7 +1863,7 @@ static long compress_added = 500000; // word count
// Sets "sps_flags".
int spell_check_msm(void)
{
- char *p = (char *)p_msm;
+ char *p = p_msm;
long start = 0;
long incr = 0;
long added = 0;
@@ -1923,7 +1924,7 @@ static void spell_clear_flags(wordnode_T *node)
wordnode_T *np;
for (np = node; np != NULL; np = np->wn_sibling) {
- np->wn_u1.index = FALSE;
+ np->wn_u1.index = false;
spell_clear_flags(np->wn_child);
}
}
@@ -1939,7 +1940,7 @@ static void spell_print_node(wordnode_T *node, int depth)
msg((char_u *)line2);
msg((char_u *)line3);
} else {
- node->wn_u1.index = TRUE;
+ node->wn_u1.index = true;
if (node->wn_byte != NUL) {
if (node->wn_child != NULL) {
@@ -2041,7 +2042,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
vim_snprintf((char *)IObuff, IOSIZE, _("Reading affix file %s..."), fname);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
// Only do REP lines when not done in another .aff file already.
do_rep = GA_EMPTY(&spin->si_rep);
@@ -2064,7 +2065,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Read all the lines in the file one by one.
while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
- ++lnum;
+ lnum++;
// Skip comment lines.
if (*rline == '#') {
@@ -2074,7 +2075,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Convert from "SET" to 'encoding' when needed.
xfree(pc);
if (spin->si_conv.vc_type != CONV_NONE) {
- pc = string_convert(&spin->si_conv, rline, NULL);
+ pc = (char_u *)string_convert(&spin->si_conv, (char *)rline, NULL);
if (pc == NULL) {
smsg(_("Conversion failure for word in %s line %d: %s"),
fname, lnum, rline);
@@ -2091,7 +2092,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
itemcnt = 0;
for (p = line;;) {
while (*p != NUL && *p <= ' ') { // skip white space and CR/NL
- ++p;
+ p++;
}
if (*p == NUL) {
break;
@@ -2103,11 +2104,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// A few items have arbitrary text argument, don't split them.
if (itemcnt == 2 && spell_info_item(items[0])) {
while (*p >= ' ' || *p == TAB) { // skip until CR/NL
- ++p;
+ p++;
}
} else {
while (*p > ' ') { // skip until white space or CR/NL
- ++p;
+ p++;
}
}
if (*p == NUL) {
@@ -2120,10 +2121,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if (itemcnt > 0) {
if (is_aff_rule(items, itemcnt, "SET", 2) && aff->af_enc == NULL) {
// Setup for conversion from "ENC" to 'encoding'.
- aff->af_enc = enc_canonize(items[1]);
+ aff->af_enc = (char_u *)enc_canonize((char *)items[1]);
if (!spin->si_ascii
- && convert_setup(&spin->si_conv, aff->af_enc,
- p_enc) == FAIL) {
+ && convert_setup(&spin->si_conv, (char *)aff->af_enc, p_enc) == FAIL) {
smsg(_("Conversion in %s not supported: from %s to %s"),
fname, aff->af_enc, p_enc);
}
@@ -2167,9 +2167,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
STRCAT(p, " ");
STRCAT(p, items[1]);
spin->si_info = p;
- } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2)
- && midword == NULL) {
- midword = getroom_save(spin, items[1]);
+ } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2) && midword == NULL) {
+ midword = (char_u *)getroom_save(spin, items[1]);
} else if (is_aff_rule(items, itemcnt, "TRY", 2)) {
// ignored, we look in the tree for what chars may appear
}
@@ -2300,22 +2299,19 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Only add the couple if it isn't already there.
for (i = 0; i < gap->ga_len - 1; i += 2) {
- if (STRCMP(((char_u **)(gap->ga_data))[i], items[1]) == 0
- && STRCMP(((char_u **)(gap->ga_data))[i + 1],
- items[2]) == 0) {
+ if (STRCMP(((char **)(gap->ga_data))[i], items[1]) == 0
+ && STRCMP(((char **)(gap->ga_data))[i + 1], items[2]) == 0) {
break;
}
}
if (i >= gap->ga_len) {
ga_grow(gap, 2);
- ((char_u **)(gap->ga_data))[gap->ga_len++]
- = getroom_save(spin, items[1]);
- ((char_u **)(gap->ga_data))[gap->ga_len++]
- = getroom_save(spin, items[2]);
+ ((char **)(gap->ga_data))[gap->ga_len++] = getroom_save(spin, items[1]);
+ ((char **)(gap->ga_data))[gap->ga_len++] = getroom_save(spin, items[2]);
}
} else if (is_aff_rule(items, itemcnt, "SYLLABLE", 2)
&& syllable == NULL) {
- syllable = getroom_save(spin, items[1]);
+ syllable = (char_u *)getroom_save(spin, items[1]);
} else if (is_aff_rule(items, itemcnt, "NOBREAK", 1)) {
spin->si_nobreak = true;
} else if (is_aff_rule(items, itemcnt, "NOSPLITSUGS", 1)) {
@@ -2387,7 +2383,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Check for the "S" flag, which apparently means that another
// block with the same affix name is following.
if (itemcnt > lasti && STRCMP(items[lasti], "S") == 0) {
- ++lasti;
+ lasti++;
cur_aff->ah_follows = true;
} else {
cur_aff->ah_follows = false;
@@ -2448,10 +2444,10 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
aff_entry = getroom(spin, sizeof(*aff_entry), true);
if (STRCMP(items[2], "0") != 0) {
- aff_entry->ae_chop = getroom_save(spin, items[2]);
+ aff_entry->ae_chop = (char_u *)getroom_save(spin, items[2]);
}
if (STRCMP(items[3], "0") != 0) {
- aff_entry->ae_add = getroom_save(spin, items[3]);
+ aff_entry->ae_add = (char_u *)getroom_save(spin, items[3]);
// Recognize flags on the affix: abcd/XYZ
aff_entry->ae_flags = (char_u *)vim_strchr((char *)aff_entry->ae_add, '/');
@@ -2471,7 +2467,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if (STRCMP(items[4], ".") != 0) {
char_u buf[MAXLINELEN];
- aff_entry->ae_cond = getroom_save(spin, items[4]);
+ aff_entry->ae_cond = (char_u *)getroom_save(spin, items[4]);
if (*items[0] == 'P') {
sprintf((char *)buf, "^%s", items[4]);
} else {
@@ -2520,7 +2516,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
if (aff_entry->ae_cond != NULL) {
char_u buf[MAXLINELEN];
onecap_copy(items[4], buf, true);
- aff_entry->ae_cond = getroom_save(spin, buf);
+ aff_entry->ae_cond = (char_u *)getroom_save(spin, buf);
if (aff_entry->ae_cond != NULL) {
sprintf((char *)buf, "^%s",
aff_entry->ae_cond);
@@ -2550,7 +2546,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
idx = spin->si_prefcond.ga_len;
pp = GA_APPEND_VIA_PTR(char_u *, &spin->si_prefcond);
*pp = (aff_entry->ae_cond == NULL) ?
- NULL : getroom_save(spin, aff_entry->ae_cond);
+ NULL : (char_u *)getroom_save(spin, aff_entry->ae_cond);
}
// Add the prefix to the prefix tree.
@@ -2582,7 +2578,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
// Didn't actually use ah_newID, backup si_newprefID.
if (aff_todo == 0 && !did_postpone_prefix) {
- --spin->si_newprefID;
+ spin->si_newprefID--;
cur_aff->ah_newID = 0;
}
}
@@ -2676,10 +2672,10 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname)
}
} else if (is_aff_rule(items, itemcnt, "SOFOFROM", 2)
&& sofofrom == NULL) {
- sofofrom = getroom_save(spin, items[1]);
+ sofofrom = (char_u *)getroom_save(spin, items[1]);
} else if (is_aff_rule(items, itemcnt, "SOFOTO", 2)
&& sofoto == NULL) {
- sofoto = getroom_save(spin, items[1]);
+ sofoto = (char_u *)getroom_save(spin, items[1]);
} else if (STRCMP(items[0], "COMMON") == 0) {
int i;
@@ -2809,7 +2805,7 @@ static void aff_process_flags(afffile_T *affile, affentry_T *entry)
}
}
if (affile->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
if (*entry->ae_flags == NUL) {
@@ -2945,7 +2941,7 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla
*tp++ = (char_u)id;
}
if (aff->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
}
@@ -2968,7 +2964,7 @@ static void check_renumber(spellinfo_T *spin)
// Returns true if flag "flag" appears in affix list "afflist".
static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
{
- char_u *p;
+ char *p;
unsigned n;
switch (flagtype) {
@@ -2977,7 +2973,7 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
case AFT_CAPLONG:
case AFT_LONG:
- for (p = afflist; *p != NUL;) {
+ for (p = (char *)afflist; *p != NUL;) {
n = (unsigned)mb_ptr2char_adv((const char_u **)&p);
if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z'))
&& *p != NUL) {
@@ -2990,8 +2986,8 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag)
break;
case AFT_NUM:
- for (p = afflist; *p != NUL;) {
- int digits = getdigits_int((char **)&p, true, 0);
+ for (p = (char *)afflist; *p != NUL;) {
+ int digits = getdigits_int(&p, true, 0);
assert(digits >= 0);
n = (unsigned int)digits;
if (n == 0) {
@@ -3045,9 +3041,9 @@ static void add_fromto(spellinfo_T *spin, garray_T *gap, char_u *from, char_u *t
fromto_T *ftp = GA_APPEND_VIA_PTR(fromto_T, gap);
(void)spell_casefold(curwin, from, (int)STRLEN(from), word, MAXWLEN);
- ftp->ft_from = getroom_save(spin, word);
+ ftp->ft_from = (char_u *)getroom_save(spin, word);
(void)spell_casefold(curwin, to, (int)STRLEN(to), word, MAXWLEN);
- ftp->ft_to = getroom_save(spin, word);
+ ftp->ft_to = (char_u *)getroom_save(spin, word);
}
// Converts a boolean argument in a SAL line to true or false;
@@ -3070,9 +3066,9 @@ static void spell_free_aff(afffile_T *aff)
// All this trouble to free the "ae_prog" items...
for (ht = &aff->af_pref;; ht = &aff->af_suff) {
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ for (hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
ah = HI2AH(hi);
for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) {
vim_regfree(ae->ae_prog);
@@ -3127,7 +3123,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
vim_snprintf((char *)IObuff, IOSIZE,
_("Reading dictionary file %s..."), fname);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
// start with a message for the first line
spin->si_msg_count = 999999;
@@ -3142,7 +3138,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// the hashtable.
while (!vim_fgets(line, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
- ++lnum;
+ lnum++;
if (line[0] == '#' || line[0] == '/') {
continue; // comment line
}
@@ -3150,7 +3146,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// the word is kept to allow multi-word terms like "et al.".
l = (int)STRLEN(line);
while (l > 0 && line[l - 1] <= ' ') {
- --l;
+ l--;
}
if (l == 0) {
continue; // empty line
@@ -3159,7 +3155,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// Convert from "SET" to 'encoding' when needed.
if (spin->si_conv.vc_type != CONV_NONE) {
- pc = string_convert(&spin->si_conv, line, NULL);
+ pc = (char_u *)string_convert(&spin->si_conv, (char *)line, NULL);
if (pc == NULL) {
smsg(_("Conversion failure for word in %s line %d: %s"),
fname, lnum, line);
@@ -3186,7 +3182,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
// Skip non-ASCII words when "spin->si_ascii" is true.
if (spin->si_ascii && has_non_ascii(w)) {
- ++non_ascii;
+ non_ascii++;
xfree(pc);
continue;
}
@@ -3210,7 +3206,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
}
// Store the word in the hashtable to be able to find duplicates.
- dw = getroom_save(spin, w);
+ dw = (char_u *)getroom_save(spin, w);
if (dw == NULL) {
retval = FAIL;
xfree(pc);
@@ -3227,7 +3223,7 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile)
smsg(_("First duplicate word in %s line %d: %s"),
fname, lnum, dw);
}
- ++duplicate;
+ duplicate++;
} else {
hash_add_item(&ht, hi, dw, hash);
}
@@ -3362,7 +3358,7 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist
}
}
if (affile->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
@@ -3392,7 +3388,7 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl
}
}
if (affile->af_flagtype == AFT_NUM && *p == ',') {
- ++p;
+ p++;
}
}
@@ -3436,9 +3432,9 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
int use_condit;
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0 && retval == OK; ++hi) {
+ for (hi = ht->ht_array; todo > 0 && retval == OK; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
ah = HI2AH(hi);
// Check that the affix combines, if required, and that the word
@@ -3542,8 +3538,8 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
// Combine the prefix IDs. Avoid adding the
// same ID twice.
- for (i = 0; i < pfxlen; ++i) {
- for (j = 0; j < use_pfxlen; ++j) {
+ for (i = 0; i < pfxlen; i++) {
+ for (j = 0; j < use_pfxlen; j++) {
if (pfxlist[i] == use_pfxlist[j]) {
break;
}
@@ -3564,9 +3560,8 @@ static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afff
// Combine the list of compound flags.
// Concatenate them to the prefix IDs list.
// Avoid adding the same ID twice.
- for (i = pfxlen; pfxlist[i] != NUL; ++i) {
- for (j = use_pfxlen;
- use_pfxlist[j] != NUL; ++j) {
+ for (i = pfxlen; pfxlist[i] != NUL; i++) {
+ for (j = use_pfxlen; use_pfxlist[j] != NUL; j++) {
if (pfxlist[i] == use_pfxlist[j]) {
break;
}
@@ -3681,12 +3676,12 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
}
vim_snprintf((char *)IObuff, IOSIZE, _("Reading word file %s..."), fname);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
// Read all the lines in the file one by one.
while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) {
line_breakcheck();
- ++lnum;
+ lnum++;
// Skip comment lines.
if (*rline == '#') {
@@ -3696,7 +3691,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
// Remove CR, LF and white space from the end.
l = (int)STRLEN(rline);
while (l > 0 && rline[l - 1] <= ' ') {
- --l;
+ l--;
}
if (l == 0) {
continue; // empty or blank line
@@ -3706,7 +3701,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
// Convert from "/encoding={encoding}" to 'encoding' when needed.
xfree(pc);
if (spin->si_conv.vc_type != CONV_NONE) {
- pc = string_convert(&spin->si_conv, rline, NULL);
+ pc = (char_u *)string_convert(&spin->si_conv, (char *)rline, NULL);
if (pc == NULL) {
smsg(_("Conversion failure for word in %s line %ld: %s"),
fname, lnum, rline);
@@ -3719,7 +3714,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
}
if (*line == '/') {
- ++line;
+ line++;
if (STRNCMP(line, "encoding=", 9) == 0) {
if (spin->si_conv.vc_type != CONV_NONE) {
smsg(_("Duplicate /encoding= line ignored in %s line %ld: %s"),
@@ -3728,14 +3723,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
smsg(_("/encoding= line after word ignored in %s line %ld: %s"),
fname, lnum, line - 1);
} else {
- char_u *enc;
+ char *enc;
// Setup for conversion to 'encoding'.
line += 9;
- enc = enc_canonize(line);
+ enc = enc_canonize((char *)line);
if (!spin->si_ascii
- && convert_setup(&spin->si_conv, enc,
- p_enc) == FAIL) {
+ && convert_setup(&spin->si_conv, enc, p_enc) == FAIL) {
smsg(_("Conversion in %s not supported: from %s to %s"),
fname, line, p_enc);
}
@@ -3802,13 +3796,13 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
fname, lnum, p);
break;
}
- ++p;
+ p++;
}
}
// Skip non-ASCII words when "spin->si_ascii" is true.
if (spin->si_ascii && has_non_ascii(line)) {
- ++non_ascii;
+ non_ascii++;
continue;
}
@@ -3826,7 +3820,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname)
if (spin->si_ascii && non_ascii > 0) {
vim_snprintf((char *)IObuff, IOSIZE,
_("Ignored %d words with non-ASCII characters"), non_ascii);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
}
return retval;
@@ -3860,7 +3854,7 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align)
bl->sb_next = spin->si_blocks;
spin->si_blocks = bl;
bl->sb_used = 0;
- ++spin->si_blocks_cnt;
+ spin->si_blocks_cnt++;
}
p = bl->sb_data + bl->sb_used;
@@ -3869,9 +3863,10 @@ static void *getroom(spellinfo_T *spin, size_t len, bool align)
return p;
}
-// Make a copy of a string into memory allocated with getroom().
-// Returns NULL when out of memory.
-static char_u *getroom_save(spellinfo_T *spin, char_u *s)
+/// Make a copy of a string into memory allocated with getroom().
+///
+/// @return NULL when out of memory.
+static char *getroom_save(spellinfo_T *spin, char_u *s)
{
const size_t s_size = STRLEN(s) + 1;
return memcpy(getroom(spin, s_size, false), s, s_size);
@@ -3899,13 +3894,13 @@ static wordnode_T *wordtree_alloc(spellinfo_T *spin)
/// Return true if "word" contains valid word characters.
/// Control characters and trailing '/' are invalid. Space is OK.
-static bool valid_spell_word(const char_u *word, const char_u *end)
+static bool valid_spell_word(const char *word, const char *end)
{
- if (!utf_valid_string(word, end)) {
+ if (!utf_valid_string((char_u *)word, (char_u *)end)) {
return false;
}
- for (const char_u *p = word; *p != NUL && p < end; p += utfc_ptr2len((const char *)p)) {
- if (*p < ' ' || (p[0] == '/' && p[1] == NUL)) {
+ for (const char *p = word; *p != NUL && p < end; p += utfc_ptr2len(p)) {
+ if ((uint8_t)(*p) < ' ' || (p[0] == '/' && p[1] == NUL)) {
return false;
}
}
@@ -3933,7 +3928,7 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
int res = OK;
// Avoid adding illegal bytes to the word tree.
- if (!valid_spell_word(word, word + len)) {
+ if (!valid_spell_word((char *)word, (char *)word + len)) {
return FAIL;
}
@@ -3947,7 +3942,7 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
break;
}
}
- ++spin->si_foldwcount;
+ spin->si_foldwcount++;
if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) {
for (const char_u *p = pfxlist; res == OK; p++) {
@@ -3959,7 +3954,7 @@ static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, co
break;
}
}
- ++spin->si_keepwcount;
+ spin->si_keepwcount++;
}
return res;
}
@@ -3978,12 +3973,12 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
int i;
// Add each byte of the word to the tree, including the NUL at the end.
- for (i = 0;; ++i) {
+ for (i = 0;; i++) {
// When there is more than one reference to this node we need to make
// a copy, so that we can modify it. Copy the whole list of siblings
// (we don't optimize for a partly shared list of siblings).
if (node != NULL && node->wn_refs > 1) {
- --node->wn_refs;
+ node->wn_refs--;
copyprev = prev;
for (copyp = node; copyp != NULL; copyp = copyp->wn_sibling) {
// Allocate a new node and copy the info.
@@ -3993,7 +3988,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
}
np->wn_child = copyp->wn_child;
if (np->wn_child != NULL) {
- ++np->wn_child->wn_refs; // child gets extra ref
+ np->wn_child->wn_refs++; // child gets extra ref
}
np->wn_byte = copyp->wn_byte;
if (np->wn_byte == NUL) {
@@ -4080,7 +4075,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int
#endif
// count nr of words added since last message
- ++spin->si_msg_count;
+ spin->si_msg_count++;
if (spin->si_compress_cnt > 1) {
if (--spin->si_compress_cnt == 1) {
@@ -4146,8 +4141,8 @@ static wordnode_T *get_wordnode(spellinfo_T *spin)
} else {
n = spin->si_first_free;
spin->si_first_free = n->wn_child;
- memset(n, 0, sizeof(wordnode_T));
- --spin->si_free_count;
+ CLEAR_POINTER(n);
+ spin->si_free_count--;
}
#ifdef SPELL_PRINTTREE
if (n != NULL) {
@@ -4173,9 +4168,9 @@ static int deref_wordnode(spellinfo_T *spin, wordnode_T *node)
cnt += deref_wordnode(spin, np->wn_child);
}
free_wordnode(spin, np);
- ++cnt;
+ cnt++;
}
- ++cnt; // length field
+ cnt++; // length field
}
return cnt;
}
@@ -4187,7 +4182,7 @@ static void free_wordnode(spellinfo_T *spin, wordnode_T *n)
{
n->wn_child = spin->si_first_free;
spin->si_first_free = n;
- ++spin->si_free_count;
+ spin->si_free_count++;
}
// Compress a tree: find tails that are identical and can be shared.
@@ -4218,7 +4213,7 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *n
vim_snprintf((char *)IObuff, IOSIZE,
_("Compressed %s of %ld nodes; %ld (%ld%%) remaining"),
name, tot, tot - n, perc);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
}
#ifdef SPELL_PRINTTREE
spell_print_tree(root->wn_sibling);
@@ -4248,7 +4243,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
// Note that with "child" we mean not just the node that is pointed to,
// but the whole list of siblings of which the child node is the first.
for (np = node; np != NULL && !got_int; np = np->wn_sibling) {
- ++len;
+ len++;
if ((child = np->wn_child) != NULL) {
// Compress the child first. This fills hashkey.
compressed += node_compress(spin, child, ht, tot);
@@ -4266,7 +4261,7 @@ static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, lo
// Found one! Now use that child in place of the
// current one. This means the current child and all
// its siblings is unlinked from the tree.
- ++tp->wn_refs;
+ tp->wn_refs++;
compressed += deref_wordnode(spin, child);
np->wn_child = tp;
break;
@@ -4352,14 +4347,15 @@ static int rep_compare(const void *s1, const void *s2)
return STRCMP(p1->ft_from, p2->ft_from);
}
-// Write the Vim .spl file "fname".
-// Return OK/FAIL.
-static int write_vim_spell(spellinfo_T *spin, char_u *fname)
+/// Write the Vim .spl file "fname".
+///
+/// @return OK/FAIL.
+static int write_vim_spell(spellinfo_T *spin, char *fname)
{
int retval = OK;
int regionmask;
- FILE *fd = os_fopen((char *)fname, "w");
+ FILE *fd = os_fopen(fname, "w");
if (fd == NULL) {
semsg(_(e_notopen), fname);
return FAIL;
@@ -4424,7 +4420,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
put_bytes(fd, 1 + 128 + 2 + l, 4); // <sectionlen>
fputc(128, fd); // <charflagslen>
- for (size_t i = 128; i < 256; ++i) {
+ for (size_t i = 128; i < 256; i++) {
flags = 0;
if (spelltab.st_isw[i]) {
flags |= CF_WORD;
@@ -4468,7 +4464,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// round 1: SN_REP section
// round 2: SN_SAL section (unless SN_SOFO is used)
// round 3: SN_REPSAL section
- for (unsigned int round = 1; round <= 3; ++round) {
+ for (unsigned int round = 1; round <= 3; round++) {
garray_T *gap;
if (round == 1) {
gap = &spin->si_rep;
@@ -4502,13 +4498,13 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// Compute the length of what follows.
size_t l = 2; // count <repcount> or <salcount>
assert(gap->ga_len >= 0);
- for (size_t i = 0; i < (size_t)gap->ga_len; ++i) {
+ for (size_t i = 0; i < (size_t)gap->ga_len; i++) {
fromto_T *ftp = &((fromto_T *)gap->ga_data)[i];
l += 1 + STRLEN(ftp->ft_from); // count <*fromlen> and <*from>
l += 1 + STRLEN(ftp->ft_to); // count <*tolen> and <*to>
}
if (round == 2) {
- ++l; // count <salflags>
+ l++; // count <salflags>
}
put_bytes(fd, l, 4); // <sectionlen>
@@ -4527,11 +4523,11 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
}
put_bytes(fd, (uintmax_t)gap->ga_len, 2); // <repcount> or <salcount>
- for (size_t i = 0; i < (size_t)gap->ga_len; ++i) {
+ for (size_t i = 0; i < (size_t)gap->ga_len; i++) {
// <rep> : <repfromlen> <repfrom> <reptolen> <repto>
// <sal> : <salfromlen> <salfrom> <saltolen> <salto>
fromto_T *ftp = &((fromto_T *)gap->ga_data)[i];
- for (unsigned int rr = 1; rr <= 2; ++rr) {
+ for (unsigned int rr = 1; rr <= 2; rr++) {
char_u *p = rr == 1 ? ftp->ft_from : ftp->ft_to;
l = STRLEN(p);
assert(l < INT_MAX);
@@ -4568,20 +4564,20 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// round 1: count the bytes
// round 2: write the bytes
- for (unsigned int round = 1; round <= 2; ++round) {
+ for (unsigned int round = 1; round <= 2; round++) {
size_t todo;
size_t len = 0;
hashitem_T *hi;
todo = spin->si_commonwords.ht_used;
- for (hi = spin->si_commonwords.ht_array; todo > 0; ++hi) {
+ for (hi = spin->si_commonwords.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
size_t l = STRLEN(hi->hi_key) + 1;
len += l;
if (round == 2) { // <word>
fwv &= fwrite(hi->hi_key, l, 1, fd);
}
- --todo;
+ todo--;
}
}
if (round == 1) {
@@ -4644,8 +4640,8 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
size_t l = STRLEN(spin->si_compflags);
assert(spin->si_comppat.ga_len >= 0);
- for (size_t i = 0; i < (size_t)spin->si_comppat.ga_len; ++i) {
- l += STRLEN(((char_u **)(spin->si_comppat.ga_data))[i]) + 1;
+ for (size_t i = 0; i < (size_t)spin->si_comppat.ga_len; i++) {
+ l += STRLEN(((char **)(spin->si_comppat.ga_data))[i]) + 1;
}
put_bytes(fd, l + 7, 4); // <sectionlen>
@@ -4655,8 +4651,8 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
putc(0, fd); // for Vim 7.0b compatibility
putc(spin->si_compoptions, fd); // <compoptions>
put_bytes(fd, (uintmax_t)spin->si_comppat.ga_len, 2); // <comppatcount>
- for (size_t i = 0; i < (size_t)spin->si_comppat.ga_len; ++i) {
- char_u *p = ((char_u **)(spin->si_comppat.ga_data))[i];
+ for (size_t i = 0; i < (size_t)spin->si_comppat.ga_len; i++) {
+ char *p = ((char **)(spin->si_comppat.ga_data))[i];
assert(STRLEN(p) < INT_MAX);
putc((int)STRLEN(p), fd); // <comppatlen>
fwv &= fwrite(p, STRLEN(p), 1, fd); // <comppattext>
@@ -4691,7 +4687,7 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname)
// <LWORDTREE> <KWORDTREE> <PREFIXTREE>
spin->si_memtot = 0;
- for (unsigned int round = 1; round <= 3; ++round) {
+ for (unsigned int round = 1; round <= 3; round++) {
wordnode_T *tree;
if (round == 1) {
tree = spin->si_foldroot->wn_sibling;
@@ -4782,7 +4778,7 @@ static int put_node(FILE *fd, wordnode_T *node, int idx, int regionmask, bool pr
// Count the number of siblings.
int siblingcount = 0;
for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) {
- ++siblingcount;
+ siblingcount++;
}
// Write the sibling count.
@@ -4880,12 +4876,12 @@ void ex_mkspell(exarg_T *eap)
{
int fcount;
char **fnames;
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
bool ascii = false;
if (STRNCMP(arg, "-ascii", 6) == 0) {
ascii = true;
- arg = (char_u *)skipwhite((char *)arg + 6);
+ arg = skipwhite(arg + 6);
}
// Expand all the remaining arguments (e.g., $VIMRUNTIME).
@@ -4898,7 +4894,7 @@ void ex_mkspell(exarg_T *eap)
// Create the .sug file.
// Uses the soundfold info in "spin".
// Writes the file with the name "wfname", with ".spl" changed to ".sug".
-static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
+static void spell_make_sugfile(spellinfo_T *spin, char *wfname)
{
char_u *fname = NULL;
int len;
@@ -4911,14 +4907,14 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
// of the code for the soundfolding stuff.
// It might have been done already by spell_reload_one().
for (slang = first_lang; slang != NULL; slang = slang->sl_next) {
- if (path_full_compare((char *)wfname, (char *)slang->sl_fname, false, true)
+ if (path_full_compare(wfname, slang->sl_fname, false, true)
== kEqualFiles) {
break;
}
}
if (slang == NULL) {
- spell_message(spin, (char_u *)_("Reading back spell file..."));
- slang = spell_load_file(wfname, NULL, NULL, false);
+ spell_message(spin, _("Reading back spell file..."));
+ slang = spell_load_file((char_u *)wfname, NULL, NULL, false);
if (slang == NULL) {
return;
}
@@ -4935,7 +4931,7 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
// Go through the trie of good words, soundfold each word and add it to
// the soundfold trie.
- spell_message(spin, (char_u *)_("Performing soundfolding..."));
+ spell_message(spin, _("Performing soundfolding..."));
if (sug_filltree(spin, slang) == FAIL) {
goto theend;
}
@@ -4952,7 +4948,7 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname)
(int64_t)spin->si_spellbuf->b_ml.ml_line_count);
// Compress the soundfold trie.
- spell_message(spin, (char_u *)_(msg_compressing));
+ spell_message(spin, _(msg_compressing));
wordtree_compress(spin, spin->si_foldroot, "case-folded");
// Write the .sug file.
@@ -5012,12 +5008,12 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
wordcount[depth - 1] += wordcount[depth];
}
- --depth;
+ depth--;
line_breakcheck();
} else {
// Do one more byte at this node.
n = arridx[depth] + curi[depth];
- ++curi[depth];
+ curi[depth]++;
c = byts[n];
if (c == 0) {
@@ -5033,8 +5029,8 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang)
return FAIL;
}
- ++words_done;
- ++wordcount[depth];
+ words_done++;
+ wordcount[depth]++;
// Reset the block count each time to avoid compression
// kicking in.
@@ -5198,7 +5194,7 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
vim_snprintf((char *)IObuff, IOSIZE,
_("Writing suggestion file %s..."), fname);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
// <SUGHEADER>: <fileID> <versionnr> <timestamp>
if (fwrite(VIMSUGMAGIC, VIMSUGMAGICL, (size_t)1, fd) != 1) { // <fileID>
@@ -5235,7 +5231,7 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
assert(wcount >= 0);
put_bytes(fd, (uintmax_t)wcount, 4); // <sugwcount>
- for (linenr_T lnum = 1; lnum <= wcount; ++lnum) {
+ for (linenr_T lnum = 1; lnum <= wcount; lnum++) {
// <sugline>: <sugnr> ... NUL
char_u *line = ml_get_buf(spin->si_spellbuf, lnum, false);
size_t len = STRLEN(line) + 1;
@@ -5254,7 +5250,7 @@ static void sug_write(spellinfo_T *spin, char_u *fname)
vim_snprintf((char *)IObuff, IOSIZE,
_("Estimated runtime memory use: %d bytes"), spin->si_memtot);
- spell_message(spin, IObuff);
+ spell_message(spin, (char *)IObuff);
theend:
// close the file
@@ -5273,7 +5269,6 @@ theend:
static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool added_word)
{
char_u *fname = NULL;
- char_u *wfname;
char **innames;
int incount;
afffile_T *(afile[MAXREGIONS]);
@@ -5282,7 +5277,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
bool error = false;
spellinfo_T spin;
- memset(&spin, 0, sizeof(spin));
+ CLEAR_FIELD(spin);
spin.si_verbose = !added_word;
spin.si_ascii = ascii;
spin.si_followup = true;
@@ -5301,7 +5296,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
innames = &fnames[fcount == 1 ? 0 : 1];
incount = fcount - 1;
- wfname = xmalloc(MAXPATHL);
+ char *wfname = xmalloc(MAXPATHL);
if (fcount >= 1) {
len = (int)STRLEN(fnames[0]);
@@ -5309,42 +5304,42 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
// For ":mkspell path/en.latin1.add" output file is
// "path/en.latin1.add.spl".
incount = 1;
- vim_snprintf((char *)wfname, MAXPATHL, "%s.spl", fnames[0]);
+ vim_snprintf(wfname, MAXPATHL, "%s.spl", fnames[0]);
} else if (fcount == 1) {
// For ":mkspell path/vim" output file is "path/vim.latin1.spl".
incount = 1;
- vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL,
+ vim_snprintf(wfname, MAXPATHL, SPL_FNAME_TMPL,
fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc());
} else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) {
// Name ends in ".spl", use as the file name.
STRLCPY(wfname, fnames[0], MAXPATHL);
} else {
// Name should be language, make the file name from it.
- vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL,
+ vim_snprintf(wfname, MAXPATHL, SPL_FNAME_TMPL,
fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc());
}
// Check for .ascii.spl.
- if (strstr(path_tail((char *)wfname), SPL_FNAME_ASCII) != NULL) {
+ if (strstr(path_tail(wfname), SPL_FNAME_ASCII) != NULL) {
spin.si_ascii = true;
}
// Check for .add.spl.
- if (strstr(path_tail((char *)wfname), SPL_FNAME_ADD) != NULL) {
+ if (strstr(path_tail(wfname), SPL_FNAME_ADD) != NULL) {
spin.si_add = true;
}
}
if (incount <= 0) {
emsg(_(e_invarg)); // need at least output and input names
- } else if (vim_strchr(path_tail((char *)wfname), '_') != NULL) {
+ } else if (vim_strchr(path_tail(wfname), '_') != NULL) {
emsg(_("E751: Output file name must not have region name"));
} else if (incount > MAXREGIONS) {
semsg(_("E754: Only up to %d regions supported"), MAXREGIONS);
} else {
// Check for overwriting before doing things that may take a lot of
// time.
- if (!over_write && os_path_exists(wfname)) {
+ if (!over_write && os_path_exists((char_u *)wfname)) {
emsg(_(e_exists));
goto theend;
}
@@ -5357,7 +5352,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
// Init the aff and dic pointers.
// Get the region names if there are more than 2 arguments.
- for (i = 0; i < incount; ++i) {
+ for (i = 0; i < incount; i++) {
afile[i] = NULL;
if (incount > 1) {
@@ -5389,7 +5384,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
// Read all the .aff and .dic files.
// Text is converted to 'encoding'.
// Words are stored in the case-folded and keep-case trees.
- for (i = 0; i < incount && !error; ++i) {
+ for (i = 0; i < incount && !error; i++) {
spin.si_conv.vc_type = CONV_NONE;
spin.si_region = 1 << i;
@@ -5426,7 +5421,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
if (!error && !got_int) {
// Combine tails in the tree.
- spell_message(&spin, (char_u *)_(msg_compressing));
+ spell_message(&spin, _(msg_compressing));
wordtree_compress(&spin, spin.si_foldroot, "case-folded");
wordtree_compress(&spin, spin.si_keeproot, "keep-case");
wordtree_compress(&spin, spin.si_prefroot, "prefixes");
@@ -5436,18 +5431,18 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
// Write the info in the spell file.
vim_snprintf((char *)IObuff, IOSIZE,
_("Writing spell file %s..."), wfname);
- spell_message(&spin, IObuff);
+ spell_message(&spin, (char *)IObuff);
error = write_vim_spell(&spin, wfname) == FAIL;
- spell_message(&spin, (char_u *)_("Done!"));
+ spell_message(&spin, _("Done!"));
vim_snprintf((char *)IObuff, IOSIZE,
_("Estimated runtime memory use: %d bytes"), spin.si_memtot);
- spell_message(&spin, IObuff);
+ spell_message(&spin, (char *)IObuff);
// If the file is loaded need to reload it.
if (!error) {
- spell_reload_one(wfname, added_word);
+ spell_reload_one((char_u *)wfname, added_word);
}
}
@@ -5461,7 +5456,7 @@ static void mkspell(int fcount, char **fnames, bool ascii, bool over_write, bool
hash_clear_all(&spin.si_commonwords, 0);
// Free the .aff file structures.
- for (i = 0; i < incount; ++i) {
+ for (i = 0; i < incount; i++) {
if (afile[i] != NULL) {
spell_free_aff(afile[i]);
}
@@ -5484,14 +5479,14 @@ theend:
// Display a message for spell file processing when 'verbose' is set or using
// ":mkspell". "str" can be IObuff.
-static void spell_message(const spellinfo_T *spin, char_u *str)
+static void spell_message(const spellinfo_T *spin, char *str)
FUNC_ATTR_NONNULL_ALL
{
if (spin->si_verbose || p_verbose > 2) {
if (!spin->si_verbose) {
verbose_enter();
}
- msg((char *)str);
+ msg(str);
ui_flush();
if (!spin->si_verbose) {
verbose_leave();
@@ -5529,7 +5524,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
int i;
char_u *spf;
- if (!valid_spell_word(word, word + len)) {
+ if (!valid_spell_word((char *)word, (char *)word + len)) {
emsg(_(e_illegal_character_in_word));
return;
}
@@ -5555,7 +5550,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
}
fnamebuf = xmalloc(MAXPATHL);
- for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; i++) {
+ for (spf = (char_u *)curwin->w_s->b_p_spf, i = 1; *spf != NUL; i++) {
copy_option_part((char **)&spf, (char *)fnamebuf, MAXPATHL, ",");
if (i == idx) {
break;
@@ -5669,7 +5664,7 @@ void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo
buf_reload(buf, buf->b_orig_mode, false);
}
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
}
xfree(fnamebuf);
}
@@ -5683,14 +5678,14 @@ static void init_spellfile(void)
char_u *rtp;
char_u *lend;
bool aspath = false;
- char_u *lstart = curbuf->b_s.b_p_spl;
+ char_u *lstart = (char_u *)curbuf->b_s.b_p_spl;
if (*curwin->w_s->b_p_spl != NUL && !GA_EMPTY(&curwin->w_s->b_langp)) {
buf = xmalloc(MAXPATHL);
// Find the end of the language name. Exclude the region. If there
// is a path separator remember the start of the tail.
- for (lend = curwin->w_s->b_p_spl; *lend != NUL
+ for (lend = (char_u *)curwin->w_s->b_p_spl; *lend != NUL
&& vim_strchr(",._", *lend) == NULL; lend++) {
if (vim_ispathsep(*lend)) {
aspath = true;
@@ -5700,13 +5695,12 @@ static void init_spellfile(void)
// Loop over all entries in 'runtimepath'. Use the first one where we
// are allowed to write.
- rtp = p_rtp;
+ rtp = (char_u *)p_rtp;
while (*rtp != NUL) {
if (aspath) {
// Use directory of an entry with path, e.g., for
// "/dir/lg.utf-8.spl" use "/dir".
- STRLCPY(buf, curbuf->b_s.b_p_spl,
- lstart - curbuf->b_s.b_p_spl);
+ STRLCPY(buf, curbuf->b_s.b_p_spl, lstart - (char_u *)curbuf->b_s.b_p_spl);
} else {
// Copy the path from 'runtimepath' to buf[].
copy_option_part((char **)&rtp, (char *)buf, MAXPATHL, ",");
@@ -5715,8 +5709,7 @@ static void init_spellfile(void)
// Use the first language name from 'spelllang' and the
// encoding used in the first loaded .spl file.
if (aspath) {
- STRLCPY(buf, curbuf->b_s.b_p_spl,
- lend - curbuf->b_s.b_p_spl + 1);
+ STRLCPY(buf, curbuf->b_s.b_p_spl, lend - (char_u *)curbuf->b_s.b_p_spl + 1);
} else {
// Create the "spell" directory if it doesn't exist yet.
l = (int)STRLEN(buf);
@@ -5730,14 +5723,14 @@ static void init_spellfile(void)
"/%.*s", (int)(lend - lstart), lstart);
}
l = (int)STRLEN(buf);
- fname = LANGP_ENTRY(curwin->w_s->b_langp, 0)
+ fname = (char_u *)LANGP_ENTRY(curwin->w_s->b_langp, 0)
->lp_slang->sl_fname;
vim_snprintf((char *)buf + l, MAXPATHL - (size_t)l, ".%s.add",
((fname != NULL
&& strstr(path_tail((char *)fname), ".ascii.") != NULL)
? "ascii"
: (const char *)spell_enc()));
- set_option_value("spellfile", 0L, (const char *)buf, OPT_LOCAL);
+ set_option_value_give_err("spellfile", 0L, (const char *)buf, OPT_LOCAL);
break;
}
aspath = false;
@@ -5761,7 +5754,7 @@ static void set_spell_charflags(char_u *flags, int cnt, char_u *fol)
clear_spell_chartab(&new_st);
- for (i = 0; i < 128; ++i) {
+ for (i = 0; i < 128; i++) {
if (i < cnt) {
new_st.st_isw[i + 128] = (flags[i] & CF_WORD) != 0;
new_st.st_isu[i + 128] = (flags[i] & CF_UPPER) != 0;
@@ -5785,7 +5778,7 @@ static int set_spell_finish(spelltab_T *new_st)
if (did_set_spelltab) {
// check that it's the same table
- for (i = 0; i < 256; ++i) {
+ for (i = 0; i < 256; i++) {
if (spelltab.st_isw[i] != new_st->st_isw[i]
|| spelltab.st_isu[i] != new_st->st_isu[i]
|| spelltab.st_fold[i] != new_st->st_fold[i]
@@ -5815,7 +5808,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap, size_t *fwv)
size_t totlen = 2 + (size_t)gap->ga_len; // <prefcondcnt> and <condlen> bytes
for (int i = 0; i < gap->ga_len; i++) {
// <prefcond> : <condlen> <condstr>
- char_u *p = ((char_u **)gap->ga_data)[i];
+ char *p = ((char **)gap->ga_data)[i];
if (p != NULL) {
size_t len = STRLEN(p);
if (fd != NULL) {
@@ -5848,7 +5841,7 @@ static void set_map_str(slang_T *lp, char_u *map)
lp->sl_has_map = true;
// Init the array and hash tables empty.
- for (i = 0; i < 256; ++i) {
+ for (i = 0; i < 256; i++) {
lp->sl_map_array[i] = 0;
}
hash_init(&lp->sl_map_hash);
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
new file mode 100644
index 0000000000..23c8c621b5
--- /dev/null
+++ b/src/nvim/spellsuggest.c
@@ -0,0 +1,3800 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// spellsuggest.c: functions for spelling suggestions
+
+#include "nvim/ascii.h"
+#include "nvim/change.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/eval.h"
+#include "nvim/fileio.h"
+#include "nvim/garray.h"
+#include "nvim/getchar.h"
+#include "nvim/hashtab.h"
+#include "nvim/input.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option.h"
+#include "nvim/os/fs.h"
+#include "nvim/os/input.h"
+#include "nvim/profile.h"
+#include "nvim/screen.h"
+#include "nvim/spell.h"
+#include "nvim/spell_defs.h"
+#include "nvim/spellfile.h"
+#include "nvim/spellsuggest.h"
+#include "nvim/strings.h"
+#include "nvim/ui.h"
+#include "nvim/undo.h"
+#include "nvim/vim.h"
+
+// Use this to adjust the score after finding suggestions, based on the
+// suggested word sounding like the bad word. This is much faster than doing
+// it for every possible suggestion.
+// Disadvantage: When "the" is typed as "hte" it sounds quite different ("@"
+// vs "ht") and goes down in the list.
+// Used when 'spellsuggest' is set to "best".
+#define RESCORE(word_score, sound_score) ((3 * (word_score) + (sound_score)) / 4)
+
+// Do the opposite: based on a maximum end score and a known sound score,
+// compute the maximum word score that can be used.
+#define MAXSCORE(word_score, sound_score) ((4 * (word_score) - (sound_score)) / 3)
+
+// only used for su_badflags
+#define WF_MIXCAP 0x20 // mix of upper and lower case: macaRONI
+
+/// Information used when looking for suggestions.
+typedef struct suginfo_S {
+ garray_T su_ga; ///< suggestions, contains "suggest_T"
+ int su_maxcount; ///< max. number of suggestions displayed
+ int su_maxscore; ///< maximum score for adding to su_ga
+ int su_sfmaxscore; ///< idem, for when doing soundfold words
+ garray_T su_sga; ///< like su_ga, sound-folded scoring
+ char_u *su_badptr; ///< start of bad word in line
+ int su_badlen; ///< length of detected bad word in line
+ int su_badflags; ///< caps flags for bad word
+ char_u su_badword[MAXWLEN]; ///< bad word truncated at su_badlen
+ char_u su_fbadword[MAXWLEN]; ///< su_badword case-folded
+ char_u su_sal_badword[MAXWLEN]; ///< su_badword soundfolded
+ hashtab_T su_banned; ///< table with banned words
+ slang_T *su_sallang; ///< default language for sound folding
+} suginfo_T;
+
+/// One word suggestion. Used in "si_ga".
+typedef struct {
+ char_u *st_word; ///< suggested word, allocated string
+ int st_wordlen; ///< STRLEN(st_word)
+ int st_orglen; ///< length of replaced text
+ int st_score; ///< lower is better
+ int st_altscore; ///< used when st_score compares equal
+ bool st_salscore; ///< st_score is for soundalike
+ bool st_had_bonus; ///< bonus already included in score
+ slang_T *st_slang; ///< language used for sound folding
+} suggest_T;
+
+#define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i])
+
+// True if a word appears in the list of banned words.
+#define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&(su)->su_banned, word)))
+
+// Number of suggestions kept when cleaning up. We need to keep more than
+// what is displayed, because when rescore_suggestions() is called the score
+// may change and wrong suggestions may be removed later.
+#define SUG_CLEAN_COUNT(su) ((su)->su_maxcount < \
+ 130 ? 150 : (su)->su_maxcount + 20)
+
+// Threshold for sorting and cleaning up suggestions. Don't want to keep lots
+// of suggestions that are not going to be displayed.
+#define SUG_MAX_COUNT(su) (SUG_CLEAN_COUNT(su) + 50)
+
+// score for various changes
+#define SCORE_SPLIT 149 // split bad word
+#define SCORE_SPLIT_NO 249 // split bad word with NOSPLITSUGS
+#define SCORE_ICASE 52 // slightly different case
+#define SCORE_REGION 200 // word is for different region
+#define SCORE_RARE 180 // rare word
+#define SCORE_SWAP 75 // swap two characters
+#define SCORE_SWAP3 110 // swap two characters in three
+#define SCORE_REP 65 // REP replacement
+#define SCORE_SUBST 93 // substitute a character
+#define SCORE_SIMILAR 33 // substitute a similar character
+#define SCORE_SUBCOMP 33 // substitute a composing character
+#define SCORE_DEL 94 // delete a character
+#define SCORE_DELDUP 66 // delete a duplicated character
+#define SCORE_DELCOMP 28 // delete a composing character
+#define SCORE_INS 96 // insert a character
+#define SCORE_INSDUP 67 // insert a duplicate character
+#define SCORE_INSCOMP 30 // insert a composing character
+#define SCORE_NONWORD 103 // change non-word to word char
+
+#define SCORE_FILE 30 // suggestion from a file
+#define SCORE_MAXINIT 350 // Initial maximum score: higher == slower.
+ // 350 allows for about three changes.
+
+#define SCORE_COMMON1 30 // subtracted for words seen before
+#define SCORE_COMMON2 40 // subtracted for words often seen
+#define SCORE_COMMON3 50 // subtracted for words very often seen
+#define SCORE_THRES2 10 // word count threshold for COMMON2
+#define SCORE_THRES3 100 // word count threshold for COMMON3
+
+// When trying changed soundfold words it becomes slow when trying more than
+// two changes. With less than two changes it's slightly faster but we miss a
+// few good suggestions. In rare cases we need to try three of four changes.
+#define SCORE_SFMAX1 200 // maximum score for first try
+#define SCORE_SFMAX2 300 // maximum score for second try
+#define SCORE_SFMAX3 400 // maximum score for third try
+
+#define SCORE_BIG (SCORE_INS * 3) // big difference
+#define SCORE_MAXMAX 999999 // accept any score
+#define SCORE_LIMITMAX 350 // for spell_edit_score_limit()
+
+// for spell_edit_score_limit() we need to know the minimum value of
+// SCORE_ICASE, SCORE_SWAP, SCORE_DEL, SCORE_SIMILAR and SCORE_INS
+#define SCORE_EDIT_MIN SCORE_SIMILAR
+
+/// For finding suggestions: At each node in the tree these states are tried:
+typedef enum {
+ STATE_START = 0, ///< At start of node check for NUL bytes (goodword
+ ///< ends); if badword ends there is a match, otherwise
+ ///< try splitting word.
+ STATE_NOPREFIX, ///< try without prefix
+ STATE_SPLITUNDO, ///< Undo splitting.
+ STATE_ENDNUL, ///< Past NUL bytes at start of the node.
+ STATE_PLAIN, ///< Use each byte of the node.
+ STATE_DEL, ///< Delete a byte from the bad word.
+ STATE_INS_PREP, ///< Prepare for inserting bytes.
+ STATE_INS, ///< Insert a byte in the bad word.
+ STATE_SWAP, ///< Swap two bytes.
+ STATE_UNSWAP, ///< Undo swap two characters.
+ STATE_SWAP3, ///< Swap two characters over three.
+ STATE_UNSWAP3, ///< Undo Swap two characters over three.
+ STATE_UNROT3L, ///< Undo rotate three characters left
+ STATE_UNROT3R, ///< Undo rotate three characters right
+ STATE_REP_INI, ///< Prepare for using REP items.
+ STATE_REP, ///< Use matching REP items from the .aff file.
+ STATE_REP_UNDO, ///< Undo a REP item replacement.
+ STATE_FINAL, ///< End of this node.
+} state_T;
+
+/// Struct to keep the state at each level in suggest_try_change().
+typedef struct trystate_S {
+ state_T ts_state; ///< state at this level, STATE_
+ int ts_score; ///< score
+ idx_T ts_arridx; ///< index in tree array, start of node
+ int16_t ts_curi; ///< index in list of child nodes
+ char_u ts_fidx; ///< index in fword[], case-folded bad word
+ char_u ts_fidxtry; ///< ts_fidx at which bytes may be changed
+ char_u ts_twordlen; ///< valid length of tword[]
+ char_u ts_prefixdepth; ///< stack depth for end of prefix or
+ ///< PFD_PREFIXTREE or PFD_NOPREFIX
+ char_u ts_flags; ///< TSF_ flags
+ char_u ts_tcharlen; ///< number of bytes in tword character
+ char_u ts_tcharidx; ///< current byte index in tword character
+ char_u ts_isdiff; ///< DIFF_ values
+ char_u ts_fcharstart; ///< index in fword where badword char started
+ char_u ts_prewordlen; ///< length of word in "preword[]"
+ char_u ts_splitoff; ///< index in "tword" after last split
+ char_u ts_splitfidx; ///< "ts_fidx" at word split
+ char_u ts_complen; ///< nr of compound words used
+ char_u ts_compsplit; ///< index for "compflags" where word was spit
+ char_u ts_save_badflags; ///< su_badflags saved here
+ char_u ts_delidx; ///< index in fword for char that was deleted,
+ ///< valid when "ts_flags" has TSF_DIDDEL
+} trystate_T;
+
+// values for ts_isdiff
+#define DIFF_NONE 0 // no different byte (yet)
+#define DIFF_YES 1 // different byte found
+#define DIFF_INSERT 2 // inserting character
+
+// values for ts_flags
+#define TSF_PREFIXOK 1 // already checked that prefix is OK
+#define TSF_DIDSPLIT 2 // tried split at this point
+#define TSF_DIDDEL 4 // did a delete, "ts_delidx" has index
+
+// special values ts_prefixdepth
+#define PFD_NOPREFIX 0xff // not using prefixes
+#define PFD_PREFIXTREE 0xfe // walking through the prefix tree
+#define PFD_NOTSPECIAL 0xfd // highest value that's not special
+
+static long spell_suggest_timeout = 5000;
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "spellsuggest.c.generated.h"
+#endif
+
+/// Returns true when the sequence of flags in "compflags" plus "flag" can
+/// possibly form a valid compounded word. This also checks the COMPOUNDRULE
+/// lines if they don't contain wildcards.
+static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, int flag)
+{
+ // If the flag doesn't appear in sl_compstartflags or sl_compallflags
+ // then it can't possibly compound.
+ if (!byte_in_str(sp->ts_complen == sp->ts_compsplit
+ ? slang->sl_compstartflags : slang->sl_compallflags, flag)) {
+ return false;
+ }
+
+ // If there are no wildcards, we can check if the flags collected so far
+ // possibly can form a match with COMPOUNDRULE patterns. This only
+ // makes sense when we have two or more words.
+ if (slang->sl_comprules != NULL && sp->ts_complen > sp->ts_compsplit) {
+ compflags[sp->ts_complen] = (char_u)flag;
+ compflags[sp->ts_complen + 1] = NUL;
+ bool v = match_compoundrule(slang, compflags + sp->ts_compsplit);
+ compflags[sp->ts_complen] = NUL;
+ return v;
+ }
+
+ return true;
+}
+
+/// Adjust the score of common words.
+///
+/// @param split word was split, less bonus
+static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool split)
+{
+ wordcount_T *wc;
+ int bonus;
+ int newscore;
+
+ hashitem_T *hi = hash_find(&slang->sl_wordcount, (char *)word);
+ if (!HASHITEM_EMPTY(hi)) {
+ wc = HI2WC(hi);
+ if (wc->wc_count < SCORE_THRES2) {
+ bonus = SCORE_COMMON1;
+ } else if (wc->wc_count < SCORE_THRES3) {
+ bonus = SCORE_COMMON2;
+ } else {
+ bonus = SCORE_COMMON3;
+ }
+ if (split) {
+ newscore = score - bonus / 2;
+ } else {
+ newscore = score - bonus;
+ }
+ if (newscore < 0) {
+ return 0;
+ }
+ return newscore;
+ }
+ return score;
+}
+
+/// Like captype() but for a KEEPCAP word add ONECAP if the word starts with a
+/// capital. So that make_case_word() can turn WOrd into Word.
+/// Add ALLCAP for "WOrD".
+static int badword_captype(char_u *word, char_u *end)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int flags = captype(word, end);
+ int c;
+ int l, u;
+ bool first;
+ char_u *p;
+
+ if (flags & WF_KEEPCAP) {
+ // Count the number of UPPER and lower case letters.
+ l = u = 0;
+ first = false;
+ for (p = word; p < end; MB_PTR_ADV(p)) {
+ c = utf_ptr2char((char *)p);
+ if (SPELL_ISUPPER(c)) {
+ u++;
+ if (p == word) {
+ first = true;
+ }
+ } else {
+ l++;
+ }
+ }
+
+ // If there are more UPPER than lower case letters suggest an
+ // ALLCAP word. Otherwise, if the first letter is UPPER then
+ // suggest ONECAP. Exception: "ALl" most likely should be "All",
+ // require three upper case letters.
+ if (u > l && u > 2) {
+ flags |= WF_ALLCAP;
+ } else if (first) {
+ flags |= WF_ONECAP;
+ }
+
+ if (u >= 2 && l >= 2) { // maCARONI maCAroni
+ flags |= WF_MIXCAP;
+ }
+ }
+ return flags;
+}
+
+/// Opposite of offset2bytes().
+/// "pp" points to the bytes and is advanced over it.
+///
+/// @return the offset.
+static int bytes2offset(char_u **pp)
+{
+ char_u *p = *pp;
+ int nr;
+ int c;
+
+ c = *p++;
+ if ((c & 0x80) == 0x00) { // 1 byte
+ nr = c - 1;
+ } else if ((c & 0xc0) == 0x80) { // 2 bytes
+ nr = (c & 0x3f) - 1;
+ nr = nr * 255 + (*p++ - 1);
+ } else if ((c & 0xe0) == 0xc0) { // 3 bytes
+ nr = (c & 0x1f) - 1;
+ nr = nr * 255 + (*p++ - 1);
+ nr = nr * 255 + (*p++ - 1);
+ } else { // 4 bytes
+ nr = (c & 0x0f) - 1;
+ nr = nr * 255 + (*p++ - 1);
+ nr = nr * 255 + (*p++ - 1);
+ nr = nr * 255 + (*p++ - 1);
+ }
+
+ *pp = p;
+ return nr;
+}
+
+// values for sps_flags
+#define SPS_BEST 1
+#define SPS_FAST 2
+#define SPS_DOUBLE 4
+
+static int sps_flags = SPS_BEST; ///< flags from 'spellsuggest'
+static int sps_limit = 9999; ///< max nr of suggestions given
+
+/// Check the 'spellsuggest' option. Return FAIL if it's wrong.
+/// Sets "sps_flags" and "sps_limit".
+int spell_check_sps(void)
+{
+ char *p;
+ char *s;
+ char_u buf[MAXPATHL];
+ int f;
+
+ sps_flags = 0;
+ sps_limit = 9999;
+
+ for (p = p_sps; *p != NUL;) {
+ copy_option_part(&p, (char *)buf, MAXPATHL, ",");
+
+ f = 0;
+ if (ascii_isdigit(*buf)) {
+ s = (char *)buf;
+ sps_limit = getdigits_int(&s, true, 0);
+ if (*s != NUL && !ascii_isdigit(*s)) {
+ f = -1;
+ }
+ } else if (STRCMP(buf, "best") == 0) {
+ f = SPS_BEST;
+ } else if (STRCMP(buf, "fast") == 0) {
+ f = SPS_FAST;
+ } else if (STRCMP(buf, "double") == 0) {
+ f = SPS_DOUBLE;
+ } else if (STRNCMP(buf, "expr:", 5) != 0
+ && STRNCMP(buf, "file:", 5) != 0
+ && (STRNCMP(buf, "timeout:", 8) != 0
+ || (!ascii_isdigit(buf[8])
+ && !(buf[8] == '-' && ascii_isdigit(buf[9]))))) {
+ f = -1;
+ }
+
+ if (f == -1 || (sps_flags != 0 && f != 0)) {
+ sps_flags = SPS_BEST;
+ sps_limit = 9999;
+ return FAIL;
+ }
+ if (f != 0) {
+ sps_flags = f;
+ }
+ }
+
+ if (sps_flags == 0) {
+ sps_flags = SPS_BEST;
+ }
+
+ return OK;
+}
+
+/// "z=": Find badly spelled word under or after the cursor.
+/// Give suggestions for the properly spelled word.
+/// In Visual mode use the highlighted word as the bad word.
+/// When "count" is non-zero use that suggestion.
+void spell_suggest(int count)
+{
+ char_u *line;
+ pos_T prev_cursor = curwin->w_cursor;
+ char_u wcopy[MAXWLEN + 2];
+ char_u *p;
+ int c;
+ suginfo_T sug;
+ suggest_T *stp;
+ int mouse_used;
+ int need_cap;
+ int limit;
+ int selected = count;
+ int badlen = 0;
+ int msg_scroll_save = msg_scroll;
+ const int wo_spell_save = curwin->w_p_spell;
+
+ if (!curwin->w_p_spell) {
+ did_set_spelllang(curwin);
+ curwin->w_p_spell = true;
+ }
+
+ if (*curwin->w_s->b_p_spl == NUL) {
+ emsg(_(e_no_spell));
+ return;
+ }
+
+ if (VIsual_active) {
+ // Use the Visually selected text as the bad word. But reject
+ // a multi-line selection.
+ if (curwin->w_cursor.lnum != VIsual.lnum) {
+ vim_beep(BO_SPELL);
+ return;
+ }
+ badlen = (int)curwin->w_cursor.col - (int)VIsual.col;
+ if (badlen < 0) {
+ badlen = -badlen;
+ } else {
+ curwin->w_cursor.col = VIsual.col;
+ }
+ badlen++;
+ end_visual_mode();
+ // Find the start of the badly spelled word.
+ } else if (spell_move_to(curwin, FORWARD, true, true, NULL) == 0
+ || curwin->w_cursor.col > prev_cursor.col) {
+ // No bad word or it starts after the cursor: use the word under the
+ // cursor.
+ curwin->w_cursor = prev_cursor;
+ line = get_cursor_line_ptr();
+ p = line + curwin->w_cursor.col;
+ // Backup to before start of word.
+ while (p > line && spell_iswordp_nmw(p, curwin)) {
+ MB_PTR_BACK(line, p);
+ }
+ // Forward to start of word.
+ while (*p != NUL && !spell_iswordp_nmw(p, curwin)) {
+ MB_PTR_ADV(p);
+ }
+
+ if (!spell_iswordp_nmw(p, curwin)) { // No word found.
+ beep_flush();
+ return;
+ }
+ curwin->w_cursor.col = (colnr_T)(p - line);
+ }
+
+ // Get the word and its length.
+
+ // Figure out if the word should be capitalised.
+ need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col);
+
+ // Make a copy of current line since autocommands may free the line.
+ line = vim_strsave(get_cursor_line_ptr());
+ spell_suggest_timeout = 5000;
+
+ // Get the list of suggestions. Limit to 'lines' - 2 or the number in
+ // 'spellsuggest', whatever is smaller.
+ if (sps_limit > Rows - 2) {
+ limit = Rows - 2;
+ } else {
+ limit = sps_limit;
+ }
+ spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
+ true, need_cap, true);
+
+ if (GA_EMPTY(&sug.su_ga)) {
+ msg(_("Sorry, no suggestions"));
+ } else if (count > 0) {
+ if (count > sug.su_ga.ga_len) {
+ smsg(_("Sorry, only %" PRId64 " suggestions"),
+ (int64_t)sug.su_ga.ga_len);
+ }
+ } else {
+ // When 'rightleft' is set the list is drawn right-left.
+ cmdmsg_rl = curwin->w_p_rl;
+ if (cmdmsg_rl) {
+ msg_col = Columns - 1;
+ }
+
+ // List the suggestions.
+ msg_start();
+ msg_row = Rows - 1; // for when 'cmdheight' > 1
+ lines_left = Rows; // avoid more prompt
+ vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
+ sug.su_badlen, sug.su_badptr);
+ if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) {
+ // And now the rabbit from the high hat: Avoid showing the
+ // untranslated message rightleft.
+ vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC",
+ sug.su_badlen, sug.su_badptr);
+ }
+ msg_puts((const char *)IObuff);
+ msg_clr_eos();
+ msg_putchar('\n');
+
+ msg_scroll = true;
+ for (int i = 0; i < sug.su_ga.ga_len; i++) {
+ stp = &SUG(sug.su_ga, i);
+
+ // The suggested word may replace only part of the bad word, add
+ // the not replaced part. But only when it's not getting too long.
+ STRLCPY(wcopy, stp->st_word, MAXWLEN + 1);
+ int el = sug.su_badlen - stp->st_orglen;
+ if (el > 0 && stp->st_wordlen + el <= MAXWLEN) {
+ STRLCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen, el + 1);
+ }
+ vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1);
+ if (cmdmsg_rl) {
+ rl_mirror(IObuff);
+ }
+ msg_puts((const char *)IObuff);
+
+ vim_snprintf((char *)IObuff, IOSIZE, " \"%s\"", wcopy);
+ msg_puts((const char *)IObuff);
+
+ // The word may replace more than "su_badlen".
+ if (sug.su_badlen < stp->st_orglen) {
+ vim_snprintf((char *)IObuff, IOSIZE, _(" < \"%.*s\""),
+ stp->st_orglen, sug.su_badptr);
+ msg_puts((const char *)IObuff);
+ }
+
+ if (p_verbose > 0) {
+ // Add the score.
+ if (sps_flags & (SPS_DOUBLE | SPS_BEST)) {
+ vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)",
+ stp->st_salscore ? "s " : "",
+ stp->st_score, stp->st_altscore);
+ } else {
+ vim_snprintf((char *)IObuff, IOSIZE, " (%d)",
+ stp->st_score);
+ }
+ if (cmdmsg_rl) {
+ // Mirror the numbers, but keep the leading space.
+ rl_mirror(IObuff + 1);
+ }
+ msg_advance(30);
+ msg_puts((const char *)IObuff);
+ }
+ msg_putchar('\n');
+ }
+
+ cmdmsg_rl = false;
+ msg_col = 0;
+ // Ask for choice.
+ selected = prompt_for_number(&mouse_used);
+
+ if (ui_has(kUIMessages)) {
+ ui_call_msg_clear();
+ }
+
+ if (mouse_used) {
+ selected -= lines_left;
+ }
+ lines_left = Rows; // avoid more prompt
+ // don't delay for 'smd' in normal_cmd()
+ msg_scroll = msg_scroll_save;
+ }
+
+ if (selected > 0 && selected <= sug.su_ga.ga_len && u_save_cursor() == OK) {
+ // Save the from and to text for :spellrepall.
+ XFREE_CLEAR(repl_from);
+ XFREE_CLEAR(repl_to);
+
+ stp = &SUG(sug.su_ga, selected - 1);
+ if (sug.su_badlen > stp->st_orglen) {
+ // Replacing less than "su_badlen", append the remainder to
+ // repl_to.
+ repl_from = vim_strnsave(sug.su_badptr, (size_t)sug.su_badlen);
+ vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word,
+ sug.su_badlen - stp->st_orglen,
+ sug.su_badptr + stp->st_orglen);
+ repl_to = vim_strsave(IObuff);
+ } else {
+ // Replacing su_badlen or more, use the whole word.
+ repl_from = vim_strnsave(sug.su_badptr, (size_t)stp->st_orglen);
+ repl_to = vim_strsave(stp->st_word);
+ }
+
+ // Replace the word.
+ p = xmalloc(STRLEN(line) - (size_t)stp->st_orglen + (size_t)stp->st_wordlen + 1);
+ c = (int)(sug.su_badptr - line);
+ memmove(p, line, (size_t)c);
+ STRCPY(p + c, stp->st_word);
+ STRCAT(p, sug.su_badptr + stp->st_orglen);
+
+ // For redo we use a change-word command.
+ ResetRedobuff();
+ AppendToRedobuff("ciw");
+ AppendToRedobuffLit((char *)p + c,
+ stp->st_wordlen + sug.su_badlen - stp->st_orglen);
+ AppendCharToRedobuff(ESC);
+
+ // "p" may be freed here
+ ml_replace(curwin->w_cursor.lnum, (char *)p, false);
+ curwin->w_cursor.col = c;
+
+ inserted_bytes(curwin->w_cursor.lnum, c, stp->st_orglen, stp->st_wordlen);
+ } else {
+ curwin->w_cursor = prev_cursor;
+ }
+
+ spell_find_cleanup(&sug);
+ xfree(line);
+ curwin->w_p_spell = wo_spell_save;
+}
+
+/// Find spell suggestions for "word". Return them in the growarray "*gap" as
+/// a list of allocated strings.
+///
+/// @param maxcount maximum nr of suggestions
+/// @param need_cap 'spellcapcheck' matched
+void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap, bool interactive)
+{
+ suginfo_T sug;
+ suggest_T *stp;
+ char_u *wcopy;
+
+ spell_find_suggest(word, 0, &sug, maxcount, false, need_cap, interactive);
+
+ // Make room in "gap".
+ ga_init(gap, sizeof(char_u *), sug.su_ga.ga_len + 1);
+ ga_grow(gap, sug.su_ga.ga_len);
+ for (int i = 0; i < sug.su_ga.ga_len; i++) {
+ stp = &SUG(sug.su_ga, i);
+
+ // The suggested word may replace only part of "word", add the not
+ // replaced part.
+ wcopy = xmalloc((size_t)stp->st_wordlen + STRLEN(sug.su_badptr + stp->st_orglen) + 1);
+ STRCPY(wcopy, stp->st_word);
+ STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen);
+ ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy;
+ }
+
+ spell_find_cleanup(&sug);
+}
+
+/// Find spell suggestions for the word at the start of "badptr".
+/// Return the suggestions in "su->su_ga".
+/// The maximum number of suggestions is "maxcount".
+/// Note: does use info for the current window.
+/// This is based on the mechanisms of Aspell, but completely reimplemented.
+///
+/// @param badlen length of bad word or 0 if unknown
+/// @param banbadword don't include badword in suggestions
+/// @param need_cap word should start with capital
+static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int maxcount,
+ bool banbadword, bool need_cap, bool interactive)
+{
+ hlf_T attr = HLF_COUNT;
+ char_u buf[MAXPATHL];
+ char *p;
+ bool do_combine = false;
+ char_u *sps_copy;
+ static bool expr_busy = false;
+ int c;
+ langp_T *lp;
+ bool did_intern = false;
+
+ // Set the info in "*su".
+ CLEAR_POINTER(su);
+ ga_init(&su->su_ga, (int)sizeof(suggest_T), 10);
+ ga_init(&su->su_sga, (int)sizeof(suggest_T), 10);
+ if (*badptr == NUL) {
+ return;
+ }
+ hash_init(&su->su_banned);
+
+ su->su_badptr = badptr;
+ if (badlen != 0) {
+ su->su_badlen = badlen;
+ } else {
+ size_t tmplen = spell_check(curwin, su->su_badptr, &attr, NULL, false);
+ assert(tmplen <= INT_MAX);
+ su->su_badlen = (int)tmplen;
+ }
+ su->su_maxcount = maxcount;
+ su->su_maxscore = SCORE_MAXINIT;
+
+ if (su->su_badlen >= MAXWLEN) {
+ su->su_badlen = MAXWLEN - 1; // just in case
+ }
+ STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1);
+ (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword,
+ MAXWLEN);
+
+ // TODO(vim): make this work if the case-folded text is longer than the
+ // original text. Currently an illegal byte causes wrong pointer
+ // computations.
+ su->su_fbadword[su->su_badlen] = NUL;
+
+ // get caps flags for bad word
+ su->su_badflags = badword_captype(su->su_badptr,
+ su->su_badptr + su->su_badlen);
+ if (need_cap) {
+ su->su_badflags |= WF_ONECAP;
+ }
+
+ // Find the default language for sound folding. We simply use the first
+ // one in 'spelllang' that supports sound folding. That's good for when
+ // using multiple files for one language, it's not that bad when mixing
+ // languages (e.g., "pl,en").
+ for (int i = 0; i < curbuf->b_s.b_langp.ga_len; i++) {
+ lp = LANGP_ENTRY(curbuf->b_s.b_langp, i);
+ if (lp->lp_sallang != NULL) {
+ su->su_sallang = lp->lp_sallang;
+ break;
+ }
+ }
+
+ // Soundfold the bad word with the default sound folding, so that we don't
+ // have to do this many times.
+ if (su->su_sallang != NULL) {
+ spell_soundfold(su->su_sallang, su->su_fbadword, true,
+ su->su_sal_badword);
+ }
+
+ // If the word is not capitalised and spell_check() doesn't consider the
+ // word to be bad then it might need to be capitalised. Add a suggestion
+ // for that.
+ c = utf_ptr2char((char *)su->su_badptr);
+ if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) {
+ make_case_word(su->su_badword, buf, WF_ONECAP);
+ add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE,
+ 0, true, su->su_sallang, false);
+ }
+
+ // Ban the bad word itself. It may appear in another region.
+ if (banbadword) {
+ add_banned(su, su->su_badword);
+ }
+
+ // Make a copy of 'spellsuggest', because the expression may change it.
+ sps_copy = vim_strsave((char_u *)p_sps);
+
+ // Loop over the items in 'spellsuggest'.
+ for (p = (char *)sps_copy; *p != NUL;) {
+ copy_option_part(&p, (char *)buf, MAXPATHL, ",");
+
+ if (STRNCMP(buf, "expr:", 5) == 0) {
+ // Evaluate an expression. Skip this when called recursively,
+ // when using spellsuggest() in the expression.
+ if (!expr_busy) {
+ expr_busy = true;
+ spell_suggest_expr(su, buf + 5);
+ expr_busy = false;
+ }
+ } else if (STRNCMP(buf, "file:", 5) == 0) {
+ // Use list of suggestions in a file.
+ spell_suggest_file(su, buf + 5);
+ } else if (STRNCMP(buf, "timeout:", 8) == 0) {
+ // Limit the time searching for suggestions.
+ spell_suggest_timeout = atol((char *)buf + 8);
+ } else if (!did_intern) {
+ // Use internal method once.
+ spell_suggest_intern(su, interactive);
+ if (sps_flags & SPS_DOUBLE) {
+ do_combine = true;
+ }
+ did_intern = true;
+ }
+ }
+
+ xfree(sps_copy);
+
+ if (do_combine) {
+ // Combine the two list of suggestions. This must be done last,
+ // because sorting changes the order again.
+ score_combine(su);
+ }
+}
+
+/// Find suggestions by evaluating expression "expr".
+static void spell_suggest_expr(suginfo_T *su, char_u *expr)
+{
+ int score;
+ const char *p;
+
+ // The work is split up in a few parts to avoid having to export
+ // suginfo_T.
+ // First evaluate the expression and get the resulting list.
+ list_T *const list = eval_spell_expr((char *)su->su_badword, (char *)expr);
+ if (list != NULL) {
+ // Loop over the items in the list.
+ TV_LIST_ITER(list, li, {
+ if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) {
+ // Get the word and the score from the items.
+ score = get_spellword(TV_LIST_ITEM_TV(li)->vval.v_list, &p);
+ if (score >= 0 && score <= su->su_maxscore) {
+ add_suggestion(su, &su->su_ga, (const char_u *)p, su->su_badlen,
+ score, 0, true, su->su_sallang, false);
+ }
+ }
+ });
+ tv_list_unref(list);
+ }
+
+ // Remove bogus suggestions, sort and truncate at "maxcount".
+ check_suggestions(su, &su->su_ga);
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
+}
+
+/// Find suggestions in file "fname". Used for "file:" in 'spellsuggest'.
+static void spell_suggest_file(suginfo_T *su, char_u *fname)
+{
+ FILE *fd;
+ char_u line[MAXWLEN * 2];
+ char_u *p;
+ int len;
+ char_u cword[MAXWLEN];
+
+ // Open the file.
+ fd = os_fopen((char *)fname, "r");
+ if (fd == NULL) {
+ semsg(_(e_notopen), fname);
+ return;
+ }
+
+ // Read it line by line.
+ while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int) {
+ line_breakcheck();
+
+ p = (char_u *)vim_strchr((char *)line, '/');
+ if (p == NULL) {
+ continue; // No Tab found, just skip the line.
+ }
+ *p++ = NUL;
+ if (STRICMP(su->su_badword, line) == 0) {
+ // Match! Isolate the good word, until CR or NL.
+ for (len = 0; p[len] >= ' '; len++) {}
+ p[len] = NUL;
+
+ // If the suggestion doesn't have specific case duplicate the case
+ // of the bad word.
+ if (captype(p, NULL) == 0) {
+ make_case_word(p, cword, su->su_badflags);
+ p = cword;
+ }
+
+ add_suggestion(su, &su->su_ga, p, su->su_badlen,
+ SCORE_FILE, 0, true, su->su_sallang, false);
+ }
+ }
+
+ fclose(fd);
+
+ // Remove bogus suggestions, sort and truncate at "maxcount".
+ check_suggestions(su, &su->su_ga);
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
+}
+
+/// Find suggestions for the internal method indicated by "sps_flags".
+static void spell_suggest_intern(suginfo_T *su, bool interactive)
+{
+ // Load the .sug file(s) that are available and not done yet.
+ suggest_load_files();
+
+ // 1. Try special cases, such as repeating a word: "the the" -> "the".
+ //
+ // Set a maximum score to limit the combination of operations that is
+ // tried.
+ suggest_try_special(su);
+
+ // 2. Try inserting/deleting/swapping/changing a letter, use REP entries
+ // from the .aff file and inserting a space (split the word).
+ suggest_try_change(su);
+
+ // For the resulting top-scorers compute the sound-a-like score.
+ if (sps_flags & SPS_DOUBLE) {
+ score_comp_sal(su);
+ }
+
+ // 3. Try finding sound-a-like words.
+ if ((sps_flags & SPS_FAST) == 0) {
+ if (sps_flags & SPS_BEST) {
+ // Adjust the word score for the suggestions found so far for how
+ // they sounds like.
+ rescore_suggestions(su);
+ }
+
+ // While going through the soundfold tree "su_maxscore" is the score
+ // for the soundfold word, limits the changes that are being tried,
+ // and "su_sfmaxscore" the rescored score, which is set by
+ // cleanup_suggestions().
+ // First find words with a small edit distance, because this is much
+ // faster and often already finds the top-N suggestions. If we didn't
+ // find many suggestions try again with a higher edit distance.
+ // "sl_sounddone" is used to avoid doing the same word twice.
+ suggest_try_soundalike_prep();
+ su->su_maxscore = SCORE_SFMAX1;
+ su->su_sfmaxscore = SCORE_MAXINIT * 3;
+ suggest_try_soundalike(su);
+ if (su->su_ga.ga_len < SUG_CLEAN_COUNT(su)) {
+ // We didn't find enough matches, try again, allowing more
+ // changes to the soundfold word.
+ su->su_maxscore = SCORE_SFMAX2;
+ suggest_try_soundalike(su);
+ if (su->su_ga.ga_len < SUG_CLEAN_COUNT(su)) {
+ // Still didn't find enough matches, try again, allowing even
+ // more changes to the soundfold word.
+ su->su_maxscore = SCORE_SFMAX3;
+ suggest_try_soundalike(su);
+ }
+ }
+ su->su_maxscore = su->su_sfmaxscore;
+ suggest_try_soundalike_finish();
+ }
+
+ // When CTRL-C was hit while searching do show the results. Only clear
+ // got_int when using a command, not for spellsuggest().
+ os_breakcheck();
+ if (interactive && got_int) {
+ (void)vgetc();
+ got_int = false;
+ }
+
+ if ((sps_flags & SPS_DOUBLE) == 0 && su->su_ga.ga_len != 0) {
+ if (sps_flags & SPS_BEST) {
+ // Adjust the word score for how it sounds like.
+ rescore_suggestions(su);
+ }
+
+ // Remove bogus suggestions, sort and truncate at "maxcount".
+ check_suggestions(su, &su->su_ga);
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
+ }
+}
+
+/// Free the info put in "*su" by spell_find_suggest().
+static void spell_find_cleanup(suginfo_T *su)
+{
+#define FREE_SUG_WORD(sug) xfree((sug)->st_word)
+ // Free the suggestions.
+ GA_DEEP_CLEAR(&su->su_ga, suggest_T, FREE_SUG_WORD);
+ GA_DEEP_CLEAR(&su->su_sga, suggest_T, FREE_SUG_WORD);
+
+ // Free the banned words.
+ hash_clear_all(&su->su_banned, 0);
+}
+
+/// Try finding suggestions by recognizing specific situations.
+static void suggest_try_special(suginfo_T *su)
+{
+ int c;
+ char_u word[MAXWLEN];
+
+ // Recognize a word that is repeated: "the the".
+ char_u *p = (char_u *)skiptowhite((char *)su->su_fbadword);
+ size_t len = (size_t)(p - su->su_fbadword);
+ p = (char_u *)skipwhite((char *)p);
+ if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) {
+ // Include badflags: if the badword is onecap or allcap
+ // use that for the goodword too: "The the" -> "The".
+ c = su->su_fbadword[len];
+ su->su_fbadword[len] = NUL;
+ make_case_word(su->su_fbadword, word, su->su_badflags);
+ su->su_fbadword[len] = (char_u)c;
+
+ // Give a soundalike score of 0, compute the score as if deleting one
+ // character.
+ add_suggestion(su, &su->su_ga, word, su->su_badlen,
+ RESCORE(SCORE_REP, 0), 0, true, su->su_sallang, false);
+ }
+}
+
+// Measure how much time is spent in each state.
+// Output is dumped in "suggestprof".
+
+#ifdef SUGGEST_PROFILE
+proftime_T current;
+proftime_T total;
+proftime_T times[STATE_FINAL + 1];
+long counts[STATE_FINAL + 1];
+
+static void prof_init(void)
+{
+ for (int i = 0; i <= STATE_FINAL; i++) {
+ profile_zero(&times[i]);
+ counts[i] = 0;
+ }
+ profile_start(&current);
+ profile_start(&total);
+}
+
+/// call before changing state
+static void prof_store(state_T state)
+{
+ profile_end(&current);
+ profile_add(&times[state], &current);
+ counts[state]++;
+ profile_start(&current);
+}
+# define PROF_STORE(state) prof_store(state);
+
+static void prof_report(char *name)
+{
+ FILE *fd = fopen("suggestprof", "a");
+
+ profile_end(&total);
+ fprintf(fd, "-----------------------\n");
+ fprintf(fd, "%s: %s\n", name, profile_msg(&total));
+ for (int i = 0; i <= STATE_FINAL; i++) {
+ fprintf(fd, "%d: %s ("%" PRId64)\n", i, profile_msg(&times[i]), counts[i]);
+ }
+ fclose(fd);
+}
+#else
+# define PROF_STORE(state)
+#endif
+
+/// Try finding suggestions by adding/removing/swapping letters.
+static void suggest_try_change(suginfo_T *su)
+{
+ char_u fword[MAXWLEN]; // copy of the bad word, case-folded
+ int n;
+ char_u *p;
+ langp_T *lp;
+
+ // We make a copy of the case-folded bad word, so that we can modify it
+ // to find matches (esp. REP items). Append some more text, changing
+ // chars after the bad word may help.
+ STRCPY(fword, su->su_fbadword);
+ n = (int)STRLEN(fword);
+ p = su->su_badptr + su->su_badlen;
+ (void)spell_casefold(curwin, p, (int)STRLEN(p), fword + n, MAXWLEN - n);
+
+ // Make sure the resulting text is not longer than the original text.
+ n = (int)STRLEN(su->su_badptr);
+ if (n < MAXWLEN) {
+ fword[n] = NUL;
+ }
+
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
+ lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
+
+ // If reloading a spell file fails it's still in the list but
+ // everything has been cleared.
+ if (lp->lp_slang->sl_fbyts == NULL) {
+ continue;
+ }
+
+ // Try it for this language. Will add possible suggestions.
+#ifdef SUGGEST_PROFILE
+ prof_init();
+#endif
+ suggest_trie_walk(su, lp, fword, false);
+#ifdef SUGGEST_PROFILE
+ prof_report("try_change");
+#endif
+ }
+}
+
+// Check the maximum score, if we go over it we won't try this change.
+#define TRY_DEEPER(su, stack, depth, add) \
+ ((depth) < MAXWLEN - 1 && (stack)[depth].ts_score + (add) < (su)->su_maxscore)
+
+/// Try finding suggestions by adding/removing/swapping letters.
+///
+/// This uses a state machine. At each node in the tree we try various
+/// operations. When trying if an operation works "depth" is increased and the
+/// stack[] is used to store info. This allows combinations, thus insert one
+/// character, replace one and delete another. The number of changes is
+/// limited by su->su_maxscore.
+///
+/// After implementing this I noticed an article by Kemal Oflazer that
+/// describes something similar: "Error-tolerant Finite State Recognition with
+/// Applications to Morphological Analysis and Spelling Correction" (1996).
+/// The implementation in the article is simplified and requires a stack of
+/// unknown depth. The implementation here only needs a stack depth equal to
+/// the length of the word.
+///
+/// This is also used for the sound-folded word, "soundfold" is true then.
+/// The mechanism is the same, but we find a match with a sound-folded word
+/// that comes from one or more original words. Each of these words may be
+/// added, this is done by add_sound_suggest().
+/// Don't use:
+/// the prefix tree or the keep-case tree
+/// "su->su_badlen"
+/// anything to do with upper and lower case
+/// anything to do with word or non-word characters ("spell_iswordp()")
+/// banned words
+/// word flags (rare, region, compounding)
+/// word splitting for now
+/// "similar_chars()"
+/// use "slang->sl_repsal" instead of "lp->lp_replang->sl_rep"
+static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool soundfold)
+{
+ char_u tword[MAXWLEN]; // good word collected so far
+ trystate_T stack[MAXWLEN];
+ char_u preword[MAXWLEN * 3] = { 0 }; // word found with proper case;
+ // concatenation of prefix compound
+ // words and split word. NUL terminated
+ // when going deeper but not when coming
+ // back.
+ char_u compflags[MAXWLEN]; // compound flags, one for each word
+ trystate_T *sp;
+ int newscore;
+ int score;
+ char_u *byts, *fbyts, *pbyts;
+ idx_T *idxs, *fidxs, *pidxs;
+ int depth;
+ int c, c2, c3;
+ int n = 0;
+ int flags;
+ garray_T *gap;
+ idx_T arridx;
+ int len;
+ char_u *p;
+ fromto_T *ftp;
+ int fl = 0, tl;
+ int repextra = 0; // extra bytes in fword[] from REP item
+ slang_T *slang = lp->lp_slang;
+ int fword_ends;
+ bool goodword_ends;
+#ifdef DEBUG_TRIEWALK
+ // Stores the name of the change made at each level.
+ char_u changename[MAXWLEN][80];
+#endif
+ int breakcheckcount = 1000;
+ bool compound_ok;
+
+ // Go through the whole case-fold tree, try changes at each node.
+ // "tword[]" contains the word collected from nodes in the tree.
+ // "fword[]" the word we are trying to match with (initially the bad
+ // word).
+ depth = 0;
+ sp = &stack[0];
+ CLEAR_POINTER(sp); // -V1068
+ sp->ts_curi = 1;
+
+ if (soundfold) {
+ // Going through the soundfold tree.
+ byts = fbyts = slang->sl_sbyts;
+ idxs = fidxs = slang->sl_sidxs;
+ pbyts = NULL;
+ pidxs = NULL;
+ sp->ts_prefixdepth = PFD_NOPREFIX;
+ sp->ts_state = STATE_START;
+ } else {
+ // When there are postponed prefixes we need to use these first. At
+ // the end of the prefix we continue in the case-fold tree.
+ fbyts = slang->sl_fbyts;
+ fidxs = slang->sl_fidxs;
+ pbyts = slang->sl_pbyts;
+ pidxs = slang->sl_pidxs;
+ if (pbyts != NULL) {
+ byts = pbyts;
+ idxs = pidxs;
+ sp->ts_prefixdepth = PFD_PREFIXTREE;
+ sp->ts_state = STATE_NOPREFIX; // try without prefix first
+ } else {
+ byts = fbyts;
+ idxs = fidxs;
+ sp->ts_prefixdepth = PFD_NOPREFIX;
+ sp->ts_state = STATE_START;
+ }
+ }
+
+ // The loop may take an indefinite amount of time. Break out after some
+ // time.
+ proftime_T time_limit;
+ if (spell_suggest_timeout > 0) {
+ time_limit = profile_setlimit(spell_suggest_timeout);
+ }
+
+ // Loop to find all suggestions. At each round we either:
+ // - For the current state try one operation, advance "ts_curi",
+ // increase "depth".
+ // - When a state is done go to the next, set "ts_state".
+ // - When all states are tried decrease "depth".
+ while (depth >= 0 && !got_int) {
+ sp = &stack[depth];
+ switch (sp->ts_state) {
+ case STATE_START:
+ case STATE_NOPREFIX:
+ // Start of node: Deal with NUL bytes, which means
+ // tword[] may end here.
+ arridx = sp->ts_arridx; // current node in the tree
+ len = byts[arridx]; // bytes in this node
+ arridx += sp->ts_curi; // index of current byte
+
+ if (sp->ts_prefixdepth == PFD_PREFIXTREE) {
+ // Skip over the NUL bytes, we use them later.
+ for (n = 0; n < len && byts[arridx + n] == 0; n++) {}
+ sp->ts_curi = (int16_t)(sp->ts_curi + n);
+
+ // Always past NUL bytes now.
+ n = (int)sp->ts_state;
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_ENDNUL;
+ sp->ts_save_badflags = (char_u)su->su_badflags;
+
+ // At end of a prefix or at start of prefixtree: check for
+ // following word.
+ if (depth < MAXWLEN - 1 && (byts[arridx] == 0 || n == STATE_NOPREFIX)) {
+ // Set su->su_badflags to the caps type at this position.
+ // Use the caps type until here for the prefix itself.
+ n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
+ flags = badword_captype(su->su_badptr, su->su_badptr + n);
+ su->su_badflags = badword_captype(su->su_badptr + n,
+ su->su_badptr + su->su_badlen);
+#ifdef DEBUG_TRIEWALK
+ sprintf(changename[depth], "prefix"); // NOLINT(runtime/printf)
+#endif
+ go_deeper(stack, depth, 0);
+ depth++;
+ sp = &stack[depth];
+ sp->ts_prefixdepth = (char_u)(depth - 1);
+ byts = fbyts;
+ idxs = fidxs;
+ sp->ts_arridx = 0;
+
+ // Move the prefix to preword[] with the right case
+ // and make find_keepcap_word() works.
+ tword[sp->ts_twordlen] = NUL;
+ make_case_word(tword + sp->ts_splitoff,
+ preword + sp->ts_prewordlen, flags);
+ sp->ts_prewordlen = (char_u)STRLEN(preword);
+ sp->ts_splitoff = sp->ts_twordlen;
+ }
+ break;
+ }
+
+ if (sp->ts_curi > len || byts[arridx] != 0) {
+ // Past bytes in node and/or past NUL bytes.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_ENDNUL;
+ sp->ts_save_badflags = (char_u)su->su_badflags;
+ break;
+ }
+
+ // End of word in tree.
+ sp->ts_curi++; // eat one NUL byte
+
+ flags = (int)idxs[arridx];
+
+ // Skip words with the NOSUGGEST flag.
+ if (flags & WF_NOSUGGEST) {
+ break;
+ }
+
+ fword_ends = (fword[sp->ts_fidx] == NUL
+ || (soundfold
+ ? ascii_iswhite(fword[sp->ts_fidx])
+ : !spell_iswordp(fword + sp->ts_fidx, curwin)));
+ tword[sp->ts_twordlen] = NUL;
+
+ if (sp->ts_prefixdepth <= PFD_NOTSPECIAL
+ && (sp->ts_flags & TSF_PREFIXOK) == 0
+ && pbyts != NULL) {
+ // There was a prefix before the word. Check that the prefix
+ // can be used with this word.
+ // Count the length of the NULs in the prefix. If there are
+ // none this must be the first try without a prefix.
+ n = stack[sp->ts_prefixdepth].ts_arridx;
+ len = pbyts[n++];
+ for (c = 0; c < len && pbyts[n + c] == 0; c++) {}
+ if (c > 0) {
+ c = valid_word_prefix(c, n, flags,
+ tword + sp->ts_splitoff, slang, false);
+ if (c == 0) {
+ break;
+ }
+
+ // Use the WF_RARE flag for a rare prefix.
+ if (c & WF_RAREPFX) {
+ flags |= WF_RARE;
+ }
+
+ // Tricky: when checking for both prefix and compounding
+ // we run into the prefix flag first.
+ // Remember that it's OK, so that we accept the prefix
+ // when arriving at a compound flag.
+ sp->ts_flags |= TSF_PREFIXOK;
+ }
+ }
+
+ // Check NEEDCOMPOUND: can't use word without compounding. Do try
+ // appending another compound word below.
+ if (sp->ts_complen == sp->ts_compsplit && fword_ends
+ && (flags & WF_NEEDCOMP)) {
+ goodword_ends = false;
+ } else {
+ goodword_ends = true;
+ }
+
+ p = NULL;
+ compound_ok = true;
+ if (sp->ts_complen > sp->ts_compsplit) {
+ if (slang->sl_nobreak) {
+ // There was a word before this word. When there was no
+ // change in this word (it was correct) add the first word
+ // as a suggestion. If this word was corrected too, we
+ // need to check if a correct word follows.
+ if (sp->ts_fidx - sp->ts_splitfidx
+ == sp->ts_twordlen - sp->ts_splitoff
+ && STRNCMP(fword + sp->ts_splitfidx,
+ tword + sp->ts_splitoff,
+ sp->ts_fidx - sp->ts_splitfidx) == 0) {
+ preword[sp->ts_prewordlen] = NUL;
+ newscore = score_wordcount_adj(slang, sp->ts_score,
+ preword + sp->ts_prewordlen,
+ sp->ts_prewordlen > 0);
+ // Add the suggestion if the score isn't too bad.
+ if (newscore <= su->su_maxscore) {
+ add_suggestion(su, &su->su_ga, preword,
+ sp->ts_splitfidx - repextra,
+ newscore, 0, false,
+ lp->lp_sallang, false);
+ }
+ break;
+ }
+ } else {
+ // There was a compound word before this word. If this
+ // word does not support compounding then give up
+ // (splitting is tried for the word without compound
+ // flag).
+ if (((unsigned)flags >> 24) == 0
+ || sp->ts_twordlen - sp->ts_splitoff
+ < slang->sl_compminlen) {
+ break;
+ }
+ // For multi-byte chars check character length against
+ // COMPOUNDMIN.
+ if (slang->sl_compminlen > 0
+ && mb_charlen(tword + sp->ts_splitoff)
+ < slang->sl_compminlen) {
+ break;
+ }
+
+ compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24);
+ compflags[sp->ts_complen + 1] = NUL;
+ STRLCPY(preword + sp->ts_prewordlen,
+ tword + sp->ts_splitoff,
+ sp->ts_twordlen - sp->ts_splitoff + 1);
+
+ // Verify CHECKCOMPOUNDPATTERN rules.
+ if (match_checkcompoundpattern(preword, sp->ts_prewordlen,
+ &slang->sl_comppat)) {
+ compound_ok = false;
+ }
+
+ if (compound_ok) {
+ p = preword;
+ while (*skiptowhite((char *)p) != NUL) {
+ p = (char_u *)skipwhite(skiptowhite((char *)p));
+ }
+ if (fword_ends && !can_compound(slang, p,
+ compflags + sp->ts_compsplit)) {
+ // Compound is not allowed. But it may still be
+ // possible if we add another (short) word.
+ compound_ok = false;
+ }
+ }
+
+ // Get pointer to last char of previous word.
+ p = preword + sp->ts_prewordlen;
+ MB_PTR_BACK(preword, p);
+ }
+ }
+
+ // Form the word with proper case in preword.
+ // If there is a word from a previous split, append.
+ // For the soundfold tree don't change the case, simply append.
+ if (soundfold) {
+ STRCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff);
+ } else if (flags & WF_KEEPCAP) {
+ // Must find the word in the keep-case tree.
+ find_keepcap_word(slang, tword + sp->ts_splitoff,
+ preword + sp->ts_prewordlen);
+ } else {
+ // Include badflags: If the badword is onecap or allcap
+ // use that for the goodword too. But if the badword is
+ // allcap and it's only one char long use onecap.
+ c = su->su_badflags;
+ if ((c & WF_ALLCAP)
+ && su->su_badlen ==
+ utfc_ptr2len((char *)su->su_badptr)) {
+ c = WF_ONECAP;
+ }
+ c |= flags;
+
+ // When appending a compound word after a word character don't
+ // use Onecap.
+ if (p != NULL && spell_iswordp_nmw(p, curwin)) {
+ c &= ~WF_ONECAP;
+ }
+ make_case_word(tword + sp->ts_splitoff,
+ preword + sp->ts_prewordlen, c);
+ }
+
+ if (!soundfold) {
+ // Don't use a banned word. It may appear again as a good
+ // word, thus remember it.
+ if (flags & WF_BANNED) {
+ add_banned(su, preword + sp->ts_prewordlen);
+ break;
+ }
+ if ((sp->ts_complen == sp->ts_compsplit
+ && WAS_BANNED(su, (char *)preword + sp->ts_prewordlen))
+ || WAS_BANNED(su, (char *)preword)) {
+ if (slang->sl_compprog == NULL) {
+ break;
+ }
+ // the word so far was banned but we may try compounding
+ goodword_ends = false;
+ }
+ }
+
+ newscore = 0;
+ if (!soundfold) { // soundfold words don't have flags
+ if ((flags & WF_REGION)
+ && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) {
+ newscore += SCORE_REGION;
+ }
+ if (flags & WF_RARE) {
+ newscore += SCORE_RARE;
+ }
+
+ if (!spell_valid_case(su->su_badflags,
+ captype(preword + sp->ts_prewordlen, NULL))) {
+ newscore += SCORE_ICASE;
+ }
+ }
+
+ // TODO(vim): how about splitting in the soundfold tree?
+ if (fword_ends
+ && goodword_ends
+ && sp->ts_fidx >= sp->ts_fidxtry
+ && compound_ok) {
+ // The badword also ends: add suggestions.
+#ifdef DEBUG_TRIEWALK
+ if (soundfold && STRCMP(preword, "smwrd") == 0) {
+ int j;
+
+ // print the stack of changes that brought us here
+ smsg("------ %s -------", fword);
+ for (j = 0; j < depth; j++) {
+ smsg("%s", changename[j]);
+ }
+ }
+#endif
+ if (soundfold) {
+ // For soundfolded words we need to find the original
+ // words, the edit distance and then add them.
+ add_sound_suggest(su, preword, sp->ts_score, lp);
+ } else if (sp->ts_fidx > 0) {
+ // Give a penalty when changing non-word char to word
+ // char, e.g., "thes," -> "these".
+ p = fword + sp->ts_fidx;
+ MB_PTR_BACK(fword, p);
+ if (!spell_iswordp(p, curwin) && *preword != NUL) {
+ p = preword + STRLEN(preword);
+ MB_PTR_BACK(preword, p);
+ if (spell_iswordp(p, curwin)) {
+ newscore += SCORE_NONWORD;
+ }
+ }
+
+ // Give a bonus to words seen before.
+ score = score_wordcount_adj(slang,
+ sp->ts_score + newscore,
+ preword + sp->ts_prewordlen,
+ sp->ts_prewordlen > 0);
+
+ // Add the suggestion if the score isn't too bad.
+ if (score <= su->su_maxscore) {
+ add_suggestion(su, &su->su_ga, preword,
+ sp->ts_fidx - repextra,
+ score, 0, false, lp->lp_sallang, false);
+
+ if (su->su_badflags & WF_MIXCAP) {
+ // We really don't know if the word should be
+ // upper or lower case, add both.
+ c = captype(preword, NULL);
+ if (c == 0 || c == WF_ALLCAP) {
+ make_case_word(tword + sp->ts_splitoff,
+ preword + sp->ts_prewordlen,
+ c == 0 ? WF_ALLCAP : 0);
+
+ add_suggestion(su, &su->su_ga, preword,
+ sp->ts_fidx - repextra,
+ score + SCORE_ICASE, 0, false,
+ lp->lp_sallang, false);
+ }
+ }
+ }
+ }
+ }
+
+ // Try word split and/or compounding.
+ if ((sp->ts_fidx >= sp->ts_fidxtry || fword_ends)
+ // Don't split in the middle of a character
+ && (sp->ts_tcharlen == 0)) {
+ bool try_compound;
+ int try_split;
+
+ // If past the end of the bad word don't try a split.
+ // Otherwise try changing the next word. E.g., find
+ // suggestions for "the the" where the second "the" is
+ // different. It's done like a split.
+ // TODO(vim): word split for soundfold words
+ try_split = (sp->ts_fidx - repextra < su->su_badlen)
+ && !soundfold;
+
+ // Get here in several situations:
+ // 1. The word in the tree ends:
+ // If the word allows compounding try that. Otherwise try
+ // a split by inserting a space. For both check that a
+ // valid words starts at fword[sp->ts_fidx].
+ // For NOBREAK do like compounding to be able to check if
+ // the next word is valid.
+ // 2. The badword does end, but it was due to a change (e.g.,
+ // a swap). No need to split, but do check that the
+ // following word is valid.
+ // 3. The badword and the word in the tree end. It may still
+ // be possible to compound another (short) word.
+ try_compound = false;
+ if (!soundfold
+ && !slang->sl_nocompoundsugs
+ && slang->sl_compprog != NULL
+ && ((unsigned)flags >> 24) != 0
+ && sp->ts_twordlen - sp->ts_splitoff
+ >= slang->sl_compminlen
+ && (slang->sl_compminlen == 0
+ || mb_charlen(tword + sp->ts_splitoff)
+ >= slang->sl_compminlen)
+ && (slang->sl_compsylmax < MAXWLEN
+ || sp->ts_complen + 1 - sp->ts_compsplit
+ < slang->sl_compmax)
+ && (can_be_compound(sp, slang, compflags, (int)((unsigned)flags >> 24)))) {
+ try_compound = true;
+ compflags[sp->ts_complen] = (char_u)((unsigned)flags >> 24);
+ compflags[sp->ts_complen + 1] = NUL;
+ }
+
+ // For NOBREAK we never try splitting, it won't make any word
+ // valid.
+ if (slang->sl_nobreak && !slang->sl_nocompoundsugs) {
+ try_compound = true;
+ } else if (!fword_ends
+ && try_compound
+ && (sp->ts_flags & TSF_DIDSPLIT) == 0) {
+ // If we could add a compound word, and it's also possible to
+ // split at this point, do the split first and set
+ // TSF_DIDSPLIT to avoid doing it again.
+ try_compound = false;
+ sp->ts_flags |= TSF_DIDSPLIT;
+ sp->ts_curi--; // do the same NUL again
+ compflags[sp->ts_complen] = NUL;
+ } else {
+ sp->ts_flags &= (char_u) ~TSF_DIDSPLIT;
+ }
+
+ if (try_split || try_compound) {
+ if (!try_compound && (!fword_ends || !goodword_ends)) {
+ // If we're going to split need to check that the
+ // words so far are valid for compounding. If there
+ // is only one word it must not have the NEEDCOMPOUND
+ // flag.
+ if (sp->ts_complen == sp->ts_compsplit
+ && (flags & WF_NEEDCOMP)) {
+ break;
+ }
+ p = preword;
+ while (*skiptowhite((char *)p) != NUL) {
+ p = (char_u *)skipwhite(skiptowhite((char *)p));
+ }
+ if (sp->ts_complen > sp->ts_compsplit
+ && !can_compound(slang, p,
+ compflags + sp->ts_compsplit)) {
+ break;
+ }
+
+ if (slang->sl_nosplitsugs) {
+ newscore += SCORE_SPLIT_NO;
+ } else {
+ newscore += SCORE_SPLIT;
+ }
+
+ // Give a bonus to words seen before.
+ newscore = score_wordcount_adj(slang, newscore,
+ preword + sp->ts_prewordlen, true);
+ }
+
+ if (TRY_DEEPER(su, stack, depth, newscore)) {
+ go_deeper(stack, depth, newscore);
+#ifdef DEBUG_TRIEWALK
+ if (!try_compound && !fword_ends) {
+ sprintf(changename[depth], "%.*s-%s: split", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx);
+ } else {
+ sprintf(changename[depth], "%.*s-%s: compound", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx);
+ }
+#endif
+ // Save things to be restored at STATE_SPLITUNDO.
+ sp->ts_save_badflags = (char_u)su->su_badflags;
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_SPLITUNDO;
+
+ depth++;
+ sp = &stack[depth];
+
+ // Append a space to preword when splitting.
+ if (!try_compound && !fword_ends) {
+ STRCAT(preword, " ");
+ }
+ sp->ts_prewordlen = (char_u)STRLEN(preword);
+ sp->ts_splitoff = sp->ts_twordlen;
+ sp->ts_splitfidx = sp->ts_fidx;
+
+ // If the badword has a non-word character at this
+ // position skip it. That means replacing the
+ // non-word character with a space. Always skip a
+ // character when the word ends. But only when the
+ // good word can end.
+ if (((!try_compound && !spell_iswordp_nmw(fword
+ + sp->ts_fidx,
+ curwin))
+ || fword_ends)
+ && fword[sp->ts_fidx] != NUL
+ && goodword_ends) {
+ int l;
+
+ l = utfc_ptr2len((char *)fword + sp->ts_fidx);
+ if (fword_ends) {
+ // Copy the skipped character to preword.
+ memmove(preword + sp->ts_prewordlen, fword + sp->ts_fidx, (size_t)l);
+ sp->ts_prewordlen = (char_u)(sp->ts_prewordlen + l);
+ preword[sp->ts_prewordlen] = NUL;
+ } else {
+ sp->ts_score -= SCORE_SPLIT - SCORE_SUBST;
+ }
+ sp->ts_fidx = (char_u)(sp->ts_fidx + l);
+ }
+
+ // When compounding include compound flag in
+ // compflags[] (already set above). When splitting we
+ // may start compounding over again.
+ if (try_compound) {
+ sp->ts_complen++;
+ } else {
+ sp->ts_compsplit = sp->ts_complen;
+ }
+ sp->ts_prefixdepth = PFD_NOPREFIX;
+
+ // set su->su_badflags to the caps type at this
+ // position
+ n = nofold_len(fword, sp->ts_fidx, su->su_badptr);
+ su->su_badflags = badword_captype(su->su_badptr + n,
+ su->su_badptr + su->su_badlen);
+
+ // Restart at top of the tree.
+ sp->ts_arridx = 0;
+
+ // If there are postponed prefixes, try these too.
+ if (pbyts != NULL) {
+ byts = pbyts;
+ idxs = pidxs;
+ sp->ts_prefixdepth = PFD_PREFIXTREE;
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_NOPREFIX;
+ }
+ }
+ }
+ }
+ break;
+
+ case STATE_SPLITUNDO:
+ // Undo the changes done for word split or compound word.
+ su->su_badflags = sp->ts_save_badflags;
+
+ // Continue looking for NUL bytes.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_START;
+
+ // In case we went into the prefix tree.
+ byts = fbyts;
+ idxs = fidxs;
+ break;
+
+ case STATE_ENDNUL:
+ // Past the NUL bytes in the node.
+ su->su_badflags = sp->ts_save_badflags;
+ if (fword[sp->ts_fidx] == NUL
+ && sp->ts_tcharlen == 0) {
+ // The badword ends, can't use STATE_PLAIN.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_DEL;
+ break;
+ }
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_PLAIN;
+ FALLTHROUGH;
+
+ case STATE_PLAIN:
+ // Go over all possible bytes at this node, add each to tword[]
+ // and use child node. "ts_curi" is the index.
+ arridx = sp->ts_arridx;
+ if (sp->ts_curi > byts[arridx]) {
+ // Done all bytes at this node, do next state. When still at
+ // already changed bytes skip the other tricks.
+ PROF_STORE(sp->ts_state)
+ if (sp->ts_fidx >= sp->ts_fidxtry) {
+ sp->ts_state = STATE_DEL;
+ } else {
+ sp->ts_state = STATE_FINAL;
+ }
+ } else {
+ arridx += sp->ts_curi++;
+ c = byts[arridx];
+
+ // Normal byte, go one level deeper. If it's not equal to the
+ // byte in the bad word adjust the score. But don't even try
+ // when the byte was already changed. And don't try when we
+ // just deleted this byte, accepting it is always cheaper than
+ // delete + substitute.
+ if (c == fword[sp->ts_fidx]
+ || (sp->ts_tcharlen > 0
+ && sp->ts_isdiff != DIFF_NONE)) {
+ newscore = 0;
+ } else {
+ newscore = SCORE_SUBST;
+ }
+ if ((newscore == 0
+ || (sp->ts_fidx >= sp->ts_fidxtry
+ && ((sp->ts_flags & TSF_DIDDEL) == 0
+ || c != fword[sp->ts_delidx])))
+ && TRY_DEEPER(su, stack, depth, newscore)) {
+ go_deeper(stack, depth, newscore);
+#ifdef DEBUG_TRIEWALK
+ if (newscore > 0) {
+ sprintf(changename[depth], "%.*s-%s: subst %c to %c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ fword[sp->ts_fidx], c);
+ } else {
+ sprintf(changename[depth], "%.*s-%s: accept %c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ fword[sp->ts_fidx]);
+ }
+#endif
+ depth++;
+ sp = &stack[depth];
+ if (fword[sp->ts_fidx] != NUL) {
+ sp->ts_fidx++;
+ }
+ tword[sp->ts_twordlen++] = (char_u)c;
+ sp->ts_arridx = idxs[arridx];
+ if (newscore == SCORE_SUBST) {
+ sp->ts_isdiff = DIFF_YES;
+ }
+ // Multi-byte characters are a bit complicated to
+ // handle: They differ when any of the bytes differ
+ // and then their length may also differ.
+ if (sp->ts_tcharlen == 0) {
+ // First byte.
+ sp->ts_tcharidx = 0;
+ sp->ts_tcharlen = MB_BYTE2LEN(c);
+ sp->ts_fcharstart = (char_u)(sp->ts_fidx - 1);
+ sp->ts_isdiff = (newscore != 0)
+ ? DIFF_YES : DIFF_NONE;
+ } else if (sp->ts_isdiff == DIFF_INSERT && sp->ts_fidx > 0) {
+ // When inserting trail bytes don't advance in the
+ // bad word.
+ sp->ts_fidx--;
+ }
+ if (++sp->ts_tcharidx == sp->ts_tcharlen) {
+ // Last byte of character.
+ if (sp->ts_isdiff == DIFF_YES) {
+ // Correct ts_fidx for the byte length of the
+ // character (we didn't check that before).
+ sp->ts_fidx = (char_u)(sp->ts_fcharstart
+ + utfc_ptr2len((char *)fword + sp->ts_fcharstart));
+
+ // For changing a composing character adjust
+ // the score from SCORE_SUBST to
+ // SCORE_SUBCOMP.
+ if (utf_iscomposing(utf_ptr2char((char *)tword + sp->ts_twordlen
+ - sp->ts_tcharlen))
+ && utf_iscomposing(utf_ptr2char((char *)fword
+ + sp->ts_fcharstart))) {
+ sp->ts_score -= SCORE_SUBST - SCORE_SUBCOMP;
+ } else if (!soundfold
+ && slang->sl_has_map
+ && similar_chars(slang,
+ utf_ptr2char((char *)tword + sp->ts_twordlen -
+ sp->ts_tcharlen),
+ utf_ptr2char((char *)fword + sp->ts_fcharstart))) {
+ // For a similar character adjust score from
+ // SCORE_SUBST to SCORE_SIMILAR.
+ sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR;
+ }
+ } else if (sp->ts_isdiff == DIFF_INSERT
+ && sp->ts_twordlen > sp->ts_tcharlen) {
+ p = tword + sp->ts_twordlen - sp->ts_tcharlen;
+ c = utf_ptr2char((char *)p);
+ if (utf_iscomposing(c)) {
+ // Inserting a composing char doesn't
+ // count that much.
+ sp->ts_score -= SCORE_INS - SCORE_INSCOMP;
+ } else {
+ // If the previous character was the same,
+ // thus doubling a character, give a bonus
+ // to the score. Also for the soundfold
+ // tree (might seem illogical but does
+ // give better scores).
+ MB_PTR_BACK(tword, p);
+ if (c == utf_ptr2char((char *)p)) {
+ sp->ts_score -= SCORE_INS - SCORE_INSDUP;
+ }
+ }
+ }
+
+ // Starting a new char, reset the length.
+ sp->ts_tcharlen = 0;
+ }
+ }
+ }
+ break;
+
+ case STATE_DEL:
+ // When past the first byte of a multi-byte char don't try
+ // delete/insert/swap a character.
+ if (sp->ts_tcharlen > 0) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_FINAL;
+ break;
+ }
+ // Try skipping one character in the bad word (delete it).
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_INS_PREP;
+ sp->ts_curi = 1;
+ if (soundfold && sp->ts_fidx == 0 && fword[sp->ts_fidx] == '*') {
+ // Deleting a vowel at the start of a word counts less, see
+ // soundalike_score().
+ newscore = 2 * SCORE_DEL / 3;
+ } else {
+ newscore = SCORE_DEL;
+ }
+ if (fword[sp->ts_fidx] != NUL
+ && TRY_DEEPER(su, stack, depth, newscore)) {
+ go_deeper(stack, depth, newscore);
+#ifdef DEBUG_TRIEWALK
+ sprintf(changename[depth], "%.*s-%s: delete %c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ fword[sp->ts_fidx]);
+#endif
+ depth++;
+
+ // Remember what character we deleted, so that we can avoid
+ // inserting it again.
+ stack[depth].ts_flags |= TSF_DIDDEL;
+ stack[depth].ts_delidx = sp->ts_fidx;
+
+ // Advance over the character in fword[]. Give a bonus to the
+ // score if the same character is following "nn" -> "n". It's
+ // a bit illogical for soundfold tree but it does give better
+ // results.
+ c = utf_ptr2char((char *)fword + sp->ts_fidx);
+ stack[depth].ts_fidx =
+ (char_u)(stack[depth].ts_fidx + utfc_ptr2len((char *)fword + sp->ts_fidx));
+ if (utf_iscomposing(c)) {
+ stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP;
+ } else if (c == utf_ptr2char((char *)fword + stack[depth].ts_fidx)) {
+ stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP;
+ }
+
+ break;
+ }
+ FALLTHROUGH;
+
+ case STATE_INS_PREP:
+ if (sp->ts_flags & TSF_DIDDEL) {
+ // If we just deleted a byte then inserting won't make sense,
+ // a substitute is always cheaper.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_SWAP;
+ break;
+ }
+
+ // skip over NUL bytes
+ n = sp->ts_arridx;
+ for (;;) {
+ if (sp->ts_curi > byts[n]) {
+ // Only NUL bytes at this node, go to next state.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_SWAP;
+ break;
+ }
+ if (byts[n + sp->ts_curi] != NUL) {
+ // Found a byte to insert.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_INS;
+ break;
+ }
+ sp->ts_curi++;
+ }
+ break;
+
+ case STATE_INS:
+ // Insert one byte. Repeat this for each possible byte at this
+ // node.
+ n = sp->ts_arridx;
+ if (sp->ts_curi > byts[n]) {
+ // Done all bytes at this node, go to next state.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_SWAP;
+ break;
+ }
+
+ // Do one more byte at this node, but:
+ // - Skip NUL bytes.
+ // - Skip the byte if it's equal to the byte in the word,
+ // accepting that byte is always better.
+ n += sp->ts_curi++;
+ c = byts[n];
+ if (soundfold && sp->ts_twordlen == 0 && c == '*') {
+ // Inserting a vowel at the start of a word counts less,
+ // see soundalike_score().
+ newscore = 2 * SCORE_INS / 3;
+ } else {
+ newscore = SCORE_INS;
+ }
+ if (c != fword[sp->ts_fidx]
+ && TRY_DEEPER(su, stack, depth, newscore)) {
+ go_deeper(stack, depth, newscore);
+#ifdef DEBUG_TRIEWALK
+ sprintf(changename[depth], "%.*s-%s: insert %c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ c);
+#endif
+ depth++;
+ sp = &stack[depth];
+ tword[sp->ts_twordlen++] = (char_u)c;
+ sp->ts_arridx = idxs[n];
+ fl = MB_BYTE2LEN(c);
+ if (fl > 1) {
+ // There are following bytes for the same character.
+ // We must find all bytes before trying
+ // delete/insert/swap/etc.
+ sp->ts_tcharlen = (char_u)fl;
+ sp->ts_tcharidx = 1;
+ sp->ts_isdiff = DIFF_INSERT;
+ }
+ if (fl == 1) {
+ // If the previous character was the same, thus doubling a
+ // character, give a bonus to the score. Also for
+ // soundfold words (illogical but does give a better
+ // score).
+ if (sp->ts_twordlen >= 2
+ && tword[sp->ts_twordlen - 2] == c) {
+ sp->ts_score -= SCORE_INS - SCORE_INSDUP;
+ }
+ }
+ }
+ break;
+
+ case STATE_SWAP:
+ // Swap two bytes in the bad word: "12" -> "21".
+ // We change "fword" here, it's changed back afterwards at
+ // STATE_UNSWAP.
+ p = fword + sp->ts_fidx;
+ c = *p;
+ if (c == NUL) {
+ // End of word, can't swap or replace.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_FINAL;
+ break;
+ }
+
+ // Don't swap if the first character is not a word character.
+ // SWAP3 etc. also don't make sense then.
+ if (!soundfold && !spell_iswordp(p, curwin)) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ break;
+ }
+
+ n = utf_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p);
+ if (p[n] == NUL) {
+ c2 = NUL;
+ } else if (!soundfold && !spell_iswordp(p + n, curwin)) {
+ c2 = c; // don't swap non-word char
+ } else {
+ c2 = utf_ptr2char((char *)p + n);
+ }
+
+ // When the second character is NUL we can't swap.
+ if (c2 == NUL) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ break;
+ }
+
+ // When characters are identical, swap won't do anything.
+ // Also get here if the second char is not a word character.
+ if (c == c2) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_SWAP3;
+ break;
+ }
+ if (TRY_DEEPER(su, stack, depth, SCORE_SWAP)) {
+ go_deeper(stack, depth, SCORE_SWAP);
+#ifdef DEBUG_TRIEWALK
+ snprintf(changename[depth], sizeof(changename[0]),
+ "%.*s-%s: swap %c and %c",
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ c, c2);
+#endif
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_UNSWAP;
+ depth++;
+ fl = utf_char2len(c2);
+ memmove(p, p + n, (size_t)fl);
+ utf_char2bytes(c, (char *)p + fl);
+ stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl);
+ } else {
+ // If this swap doesn't work then SWAP3 won't either.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ }
+ break;
+
+ case STATE_UNSWAP:
+ // Undo the STATE_SWAP swap: "21" -> "12".
+ p = fword + sp->ts_fidx;
+ n = utfc_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p + n);
+ memmove(p + utfc_ptr2len((char *)p + n), p, (size_t)n);
+ utf_char2bytes(c, (char *)p);
+
+ FALLTHROUGH;
+
+ case STATE_SWAP3:
+ // Swap two bytes, skipping one: "123" -> "321". We change
+ // "fword" here, it's changed back afterwards at STATE_UNSWAP3.
+ p = fword + sp->ts_fidx;
+ n = utf_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p);
+ fl = utf_ptr2len((char *)p + n);
+ c2 = utf_ptr2char((char *)p + n);
+ if (!soundfold && !spell_iswordp(p + n + fl, curwin)) {
+ c3 = c; // don't swap non-word char
+ } else {
+ c3 = utf_ptr2char((char *)p + n + fl);
+ }
+
+ // When characters are identical: "121" then SWAP3 result is
+ // identical, ROT3L result is same as SWAP: "211", ROT3L result is
+ // same as SWAP on next char: "112". Thus skip all swapping.
+ // Also skip when c3 is NUL.
+ // Also get here when the third character is not a word character.
+ // Second character may any char: "a.b" -> "b.a"
+ if (c == c3 || c3 == NUL) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ break;
+ }
+ if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) {
+ go_deeper(stack, depth, SCORE_SWAP3);
+#ifdef DEBUG_TRIEWALK
+ sprintf(changename[depth], "%.*s-%s: swap3 %c and %c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ c, c3);
+#endif
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_UNSWAP3;
+ depth++;
+ tl = utf_char2len(c3);
+ memmove(p, p + n + fl, (size_t)tl);
+ utf_char2bytes(c2, (char *)p + tl);
+ utf_char2bytes(c, (char *)p + fl + tl);
+ stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl + tl);
+ } else {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ }
+ break;
+
+ case STATE_UNSWAP3:
+ // Undo STATE_SWAP3: "321" -> "123"
+ p = fword + sp->ts_fidx;
+ n = utfc_ptr2len((char *)p);
+ c2 = utf_ptr2char((char *)p + n);
+ fl = utfc_ptr2len((char *)p + n);
+ c = utf_ptr2char((char *)p + n + fl);
+ tl = utfc_ptr2len((char *)p + n + fl);
+ memmove(p + fl + tl, p, (size_t)n);
+ utf_char2bytes(c, (char *)p);
+ utf_char2bytes(c2, (char *)p + tl);
+ p = p + tl;
+
+ if (!soundfold && !spell_iswordp(p, curwin)) {
+ // Middle char is not a word char, skip the rotate. First and
+ // third char were already checked at swap and swap3.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ break;
+ }
+
+ // Rotate three characters left: "123" -> "231". We change
+ // "fword" here, it's changed back afterwards at STATE_UNROT3L.
+ if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) {
+ go_deeper(stack, depth, SCORE_SWAP3);
+#ifdef DEBUG_TRIEWALK
+ p = fword + sp->ts_fidx;
+ sprintf(changename[depth], "%.*s-%s: rotate left %c%c%c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ p[0], p[1], p[2]);
+#endif
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_UNROT3L;
+ depth++;
+ p = fword + sp->ts_fidx;
+ n = utf_ptr2len((char *)p);
+ c = utf_ptr2char((char *)p);
+ fl = utf_ptr2len((char *)p + n);
+ fl += utf_ptr2len((char *)p + n + fl);
+ memmove(p, p + n, (size_t)fl);
+ utf_char2bytes(c, (char *)p + fl);
+ stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + fl);
+ } else {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ }
+ break;
+
+ case STATE_UNROT3L:
+ // Undo ROT3L: "231" -> "123"
+ p = fword + sp->ts_fidx;
+ n = utfc_ptr2len((char *)p);
+ n += utfc_ptr2len((char *)p + n);
+ c = utf_ptr2char((char *)p + n);
+ tl = utfc_ptr2len((char *)p + n);
+ memmove(p + tl, p, (size_t)n);
+ utf_char2bytes(c, (char *)p);
+
+ // Rotate three bytes right: "123" -> "312". We change "fword"
+ // here, it's changed back afterwards at STATE_UNROT3R.
+ if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) {
+ go_deeper(stack, depth, SCORE_SWAP3);
+#ifdef DEBUG_TRIEWALK
+ p = fword + sp->ts_fidx;
+ sprintf(changename[depth], "%.*s-%s: rotate right %c%c%c", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ p[0], p[1], p[2]);
+#endif
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_UNROT3R;
+ depth++;
+ p = fword + sp->ts_fidx;
+ n = utf_ptr2len((char *)p);
+ n += utf_ptr2len((char *)p + n);
+ c = utf_ptr2char((char *)p + n);
+ tl = utf_ptr2len((char *)p + n);
+ memmove(p + tl, p, (size_t)n);
+ utf_char2bytes(c, (char *)p);
+ stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + n + tl);
+ } else {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_INI;
+ }
+ break;
+
+ case STATE_UNROT3R:
+ // Undo ROT3R: "312" -> "123"
+ p = fword + sp->ts_fidx;
+ c = utf_ptr2char((char *)p);
+ tl = utfc_ptr2len((char *)p);
+ n = utfc_ptr2len((char *)p + tl);
+ n += utfc_ptr2len((char *)p + tl + n);
+ memmove(p, p + tl, (size_t)n);
+ utf_char2bytes(c, (char *)p + n);
+
+ FALLTHROUGH;
+
+ case STATE_REP_INI:
+ // Check if matching with REP items from the .aff file would work.
+ // Quickly skip if:
+ // - there are no REP items and we are not in the soundfold trie
+ // - the score is going to be too high anyway
+ // - already applied a REP item or swapped here
+ if ((lp->lp_replang == NULL && !soundfold)
+ || sp->ts_score + SCORE_REP >= su->su_maxscore
+ || sp->ts_fidx < sp->ts_fidxtry) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_FINAL;
+ break;
+ }
+
+ // Use the first byte to quickly find the first entry that may
+ // match. If the index is -1 there is none.
+ if (soundfold) {
+ sp->ts_curi = slang->sl_repsal_first[fword[sp->ts_fidx]];
+ } else {
+ sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]];
+ }
+
+ if (sp->ts_curi < 0) {
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_FINAL;
+ break;
+ }
+
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP;
+ FALLTHROUGH;
+
+ case STATE_REP:
+ // Try matching with REP items from the .aff file. For each match
+ // replace the characters and check if the resulting word is
+ // valid.
+ p = fword + sp->ts_fidx;
+
+ if (soundfold) {
+ gap = &slang->sl_repsal;
+ } else {
+ gap = &lp->lp_replang->sl_rep;
+ }
+ while (sp->ts_curi < gap->ga_len) {
+ ftp = (fromto_T *)gap->ga_data + sp->ts_curi++;
+ if (*ftp->ft_from != *p) {
+ // past possible matching entries
+ sp->ts_curi = (char_u)gap->ga_len;
+ break;
+ }
+ if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0
+ && TRY_DEEPER(su, stack, depth, SCORE_REP)) {
+ go_deeper(stack, depth, SCORE_REP);
+#ifdef DEBUG_TRIEWALK
+ sprintf(changename[depth], "%.*s-%s: replace %s with %s", // NOLINT(runtime/printf)
+ sp->ts_twordlen, tword, fword + sp->ts_fidx,
+ ftp->ft_from, ftp->ft_to);
+#endif
+ // Need to undo this afterwards.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP_UNDO;
+
+ // Change the "from" to the "to" string.
+ depth++;
+ fl = (int)STRLEN(ftp->ft_from);
+ tl = (int)STRLEN(ftp->ft_to);
+ if (fl != tl) {
+ STRMOVE(p + tl, p + fl);
+ repextra += tl - fl;
+ }
+ memmove(p, ftp->ft_to, (size_t)tl);
+ stack[depth].ts_fidxtry = (char_u)(sp->ts_fidx + tl);
+ stack[depth].ts_tcharlen = 0;
+ break;
+ }
+ }
+
+ if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) {
+ // No (more) matches.
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_FINAL;
+ }
+
+ break;
+
+ case STATE_REP_UNDO:
+ // Undo a REP replacement and continue with the next one.
+ if (soundfold) {
+ gap = &slang->sl_repsal;
+ } else {
+ gap = &lp->lp_replang->sl_rep;
+ }
+ ftp = (fromto_T *)gap->ga_data + sp->ts_curi - 1;
+ fl = (int)STRLEN(ftp->ft_from);
+ tl = (int)STRLEN(ftp->ft_to);
+ p = fword + sp->ts_fidx;
+ if (fl != tl) {
+ STRMOVE(p + fl, p + tl);
+ repextra -= tl - fl;
+ }
+ memmove(p, ftp->ft_from, (size_t)fl);
+ PROF_STORE(sp->ts_state)
+ sp->ts_state = STATE_REP;
+ break;
+
+ default:
+ // Did all possible states at this level, go up one level.
+ depth--;
+
+ if (depth >= 0 && stack[depth].ts_prefixdepth == PFD_PREFIXTREE) {
+ // Continue in or go back to the prefix tree.
+ byts = pbyts;
+ idxs = pidxs;
+ }
+
+ // Don't check for CTRL-C too often, it takes time.
+ if (--breakcheckcount == 0) {
+ os_breakcheck();
+ breakcheckcount = 1000;
+ if (spell_suggest_timeout > 0 && profile_passed_limit(time_limit)) {
+ got_int = true;
+ }
+ }
+ }
+ }
+}
+
+/// Go one level deeper in the tree.
+static void go_deeper(trystate_T *stack, int depth, int score_add)
+{
+ stack[depth + 1] = stack[depth];
+ stack[depth + 1].ts_state = STATE_START;
+ stack[depth + 1].ts_score = stack[depth].ts_score + score_add;
+ stack[depth + 1].ts_curi = 1; // start just after length byte
+ stack[depth + 1].ts_flags = 0;
+}
+
+/// "fword" is a good word with case folded. Find the matching keep-case
+/// words and put it in "kword".
+/// Theoretically there could be several keep-case words that result in the
+/// same case-folded word, but we only find one...
+static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword)
+{
+ char_u uword[MAXWLEN]; // "fword" in upper-case
+ int depth;
+ idx_T tryidx;
+
+ // The following arrays are used at each depth in the tree.
+ idx_T arridx[MAXWLEN];
+ int round[MAXWLEN];
+ int fwordidx[MAXWLEN];
+ int uwordidx[MAXWLEN];
+ int kwordlen[MAXWLEN];
+
+ int flen, ulen;
+ int l;
+ int len;
+ int c;
+ idx_T lo, hi, m;
+ char_u *p;
+ char_u *byts = slang->sl_kbyts; // array with bytes of the words
+ idx_T *idxs = slang->sl_kidxs; // array with indexes
+
+ if (byts == NULL) {
+ // array is empty: "cannot happen"
+ *kword = NUL;
+ return;
+ }
+
+ // Make an all-cap version of "fword".
+ allcap_copy(fword, uword);
+
+ // Each character needs to be tried both case-folded and upper-case.
+ // All this gets very complicated if we keep in mind that changing case
+ // may change the byte length of a multi-byte character...
+ depth = 0;
+ arridx[0] = 0;
+ round[0] = 0;
+ fwordidx[0] = 0;
+ uwordidx[0] = 0;
+ kwordlen[0] = 0;
+ while (depth >= 0) {
+ if (fword[fwordidx[depth]] == NUL) {
+ // We are at the end of "fword". If the tree allows a word to end
+ // here we have found a match.
+ if (byts[arridx[depth] + 1] == 0) {
+ kword[kwordlen[depth]] = NUL;
+ return;
+ }
+
+ // kword is getting too long, continue one level up
+ depth--;
+ } else if (++round[depth] > 2) {
+ // tried both fold-case and upper-case character, continue one
+ // level up
+ depth--;
+ } else {
+ // round[depth] == 1: Try using the folded-case character.
+ // round[depth] == 2: Try using the upper-case character.
+ flen = utf_ptr2len((char *)fword + fwordidx[depth]);
+ ulen = utf_ptr2len((char *)uword + uwordidx[depth]);
+ if (round[depth] == 1) {
+ p = fword + fwordidx[depth];
+ l = flen;
+ } else {
+ p = uword + uwordidx[depth];
+ l = ulen;
+ }
+
+ for (tryidx = arridx[depth]; l > 0; l--) {
+ // Perform a binary search in the list of accepted bytes.
+ len = byts[tryidx++];
+ c = *p++;
+ lo = tryidx;
+ hi = tryidx + len - 1;
+ while (lo < hi) {
+ m = (lo + hi) / 2;
+ if (byts[m] > c) {
+ hi = m - 1;
+ } else if (byts[m] < c) {
+ lo = m + 1;
+ } else {
+ lo = hi = m;
+ break;
+ }
+ }
+
+ // Stop if there is no matching byte.
+ if (hi < lo || byts[lo] != c) {
+ break;
+ }
+
+ // Continue at the child (if there is one).
+ tryidx = idxs[lo];
+ }
+
+ if (l == 0) {
+ // Found the matching char. Copy it to "kword" and go a
+ // level deeper.
+ if (round[depth] == 1) {
+ STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth], // NOLINT(runtime/printf)
+ flen);
+ kwordlen[depth + 1] = kwordlen[depth] + flen;
+ } else {
+ STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth], // NOLINT(runtime/printf)
+ ulen);
+ kwordlen[depth + 1] = kwordlen[depth] + ulen;
+ }
+ fwordidx[depth + 1] = fwordidx[depth] + flen;
+ uwordidx[depth + 1] = uwordidx[depth] + ulen;
+
+ depth++;
+ arridx[depth] = tryidx;
+ round[depth] = 0;
+ }
+ }
+ }
+
+ // Didn't find it: "cannot happen".
+ *kword = NUL;
+}
+
+/// Compute the sound-a-like score for suggestions in su->su_ga and add them to
+/// su->su_sga.
+static void score_comp_sal(suginfo_T *su)
+{
+ langp_T *lp;
+ char_u badsound[MAXWLEN];
+ int i;
+ suggest_T *stp;
+ suggest_T *sstp;
+ int score;
+
+ ga_grow(&su->su_sga, su->su_ga.ga_len);
+
+ // Use the sound-folding of the first language that supports it.
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
+ lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
+ if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
+ // soundfold the bad word
+ spell_soundfold(lp->lp_slang, su->su_fbadword, true, badsound);
+
+ for (i = 0; i < su->su_ga.ga_len; i++) {
+ stp = &SUG(su->su_ga, i);
+
+ // Case-fold the suggested word, sound-fold it and compute the
+ // sound-a-like score.
+ score = stp_sal_score(stp, su, lp->lp_slang, badsound);
+ if (score < SCORE_MAXMAX) {
+ // Add the suggestion.
+ sstp = &SUG(su->su_sga, su->su_sga.ga_len);
+ sstp->st_word = vim_strsave(stp->st_word);
+ sstp->st_wordlen = stp->st_wordlen;
+ sstp->st_score = score;
+ sstp->st_altscore = 0;
+ sstp->st_orglen = stp->st_orglen;
+ su->su_sga.ga_len++;
+ }
+ }
+ break;
+ }
+ }
+}
+
+/// Combine the list of suggestions in su->su_ga and su->su_sga.
+/// They are entwined.
+static void score_combine(suginfo_T *su)
+{
+ garray_T ga;
+ garray_T *gap;
+ langp_T *lp;
+ suggest_T *stp;
+ char_u *p;
+ char_u badsound[MAXWLEN];
+ int round;
+ slang_T *slang = NULL;
+
+ // Add the alternate score to su_ga.
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
+ lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
+ if (!GA_EMPTY(&lp->lp_slang->sl_sal)) {
+ // soundfold the bad word
+ slang = lp->lp_slang;
+ spell_soundfold(slang, su->su_fbadword, true, badsound);
+
+ for (int i = 0; i < su->su_ga.ga_len; i++) {
+ stp = &SUG(su->su_ga, i);
+ stp->st_altscore = stp_sal_score(stp, su, slang, badsound);
+ if (stp->st_altscore == SCORE_MAXMAX) {
+ stp->st_score = (stp->st_score * 3 + SCORE_BIG) / 4;
+ } else {
+ stp->st_score = (stp->st_score * 3 + stp->st_altscore) / 4;
+ }
+ stp->st_salscore = false;
+ }
+ break;
+ }
+ }
+
+ if (slang == NULL) { // Using "double" without sound folding.
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore,
+ su->su_maxcount);
+ return;
+ }
+
+ // Add the alternate score to su_sga.
+ for (int i = 0; i < su->su_sga.ga_len; i++) {
+ stp = &SUG(su->su_sga, i);
+ stp->st_altscore = spell_edit_score(slang,
+ su->su_badword, stp->st_word);
+ if (stp->st_score == SCORE_MAXMAX) {
+ stp->st_score = (SCORE_BIG * 7 + stp->st_altscore) / 8;
+ } else {
+ stp->st_score = (stp->st_score * 7 + stp->st_altscore) / 8;
+ }
+ stp->st_salscore = true;
+ }
+
+ // Remove bad suggestions, sort the suggestions and truncate at "maxcount"
+ // for both lists.
+ check_suggestions(su, &su->su_ga);
+ (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount);
+ check_suggestions(su, &su->su_sga);
+ (void)cleanup_suggestions(&su->su_sga, su->su_maxscore, su->su_maxcount);
+
+ ga_init(&ga, (int)sizeof(suginfo_T), 1);
+ ga_grow(&ga, su->su_ga.ga_len + su->su_sga.ga_len);
+
+ stp = &SUG(ga, 0);
+ for (int i = 0; i < su->su_ga.ga_len || i < su->su_sga.ga_len; i++) {
+ // round 1: get a suggestion from su_ga
+ // round 2: get a suggestion from su_sga
+ for (round = 1; round <= 2; round++) {
+ gap = round == 1 ? &su->su_ga : &su->su_sga;
+ if (i < gap->ga_len) {
+ // Don't add a word if it's already there.
+ p = SUG(*gap, i).st_word;
+ int j;
+ for (j = 0; j < ga.ga_len; j++) {
+ if (STRCMP(stp[j].st_word, p) == 0) {
+ break;
+ }
+ }
+ if (j == ga.ga_len) {
+ stp[ga.ga_len++] = SUG(*gap, i);
+ } else {
+ xfree(p);
+ }
+ }
+ }
+ }
+
+ ga_clear(&su->su_ga);
+ ga_clear(&su->su_sga);
+
+ // Truncate the list to the number of suggestions that will be displayed.
+ if (ga.ga_len > su->su_maxcount) {
+ for (int i = su->su_maxcount; i < ga.ga_len; i++) {
+ xfree(stp[i].st_word);
+ }
+ ga.ga_len = su->su_maxcount;
+ }
+
+ su->su_ga = ga;
+}
+
+/// For the goodword in "stp" compute the soundalike score compared to the
+/// badword.
+///
+/// @param badsound sound-folded badword
+static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *badsound)
+{
+ char_u *p;
+ char_u *pbad;
+ char_u *pgood;
+ char_u badsound2[MAXWLEN];
+ char_u fword[MAXWLEN];
+ char_u goodsound[MAXWLEN];
+ char_u goodword[MAXWLEN];
+ int lendiff;
+
+ lendiff = su->su_badlen - stp->st_orglen;
+ if (lendiff >= 0) {
+ pbad = badsound;
+ } else {
+ // soundfold the bad word with more characters following
+ (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN);
+
+ // When joining two words the sound often changes a lot. E.g., "t he"
+ // sounds like "t h" while "the" sounds like "@". Avoid that by
+ // removing the space. Don't do it when the good word also contains a
+ // space.
+ if (ascii_iswhite(su->su_badptr[su->su_badlen])
+ && *skiptowhite((char *)stp->st_word) == NUL) {
+ for (p = fword; *(p = (char_u *)skiptowhite((char *)p)) != NUL;) {
+ STRMOVE(p, p + 1);
+ }
+ }
+
+ spell_soundfold(slang, fword, true, badsound2);
+ pbad = badsound2;
+ }
+
+ if (lendiff > 0 && stp->st_wordlen + lendiff < MAXWLEN) {
+ // Add part of the bad word to the good word, so that we soundfold
+ // what replaces the bad word.
+ STRCPY(goodword, stp->st_word);
+ STRLCPY(goodword + stp->st_wordlen,
+ su->su_badptr + su->su_badlen - lendiff, lendiff + 1);
+ pgood = goodword;
+ } else {
+ pgood = stp->st_word;
+ }
+
+ // Sound-fold the word and compute the score for the difference.
+ spell_soundfold(slang, pgood, false, goodsound);
+
+ return soundalike_score(goodsound, pbad);
+}
+
+/// structure used to store soundfolded words that add_sound_suggest() has
+/// handled already.
+typedef struct {
+ int16_t sft_score; ///< lowest score used
+ char_u sft_word[1]; ///< soundfolded word, actually longer
+} sftword_T;
+
+static sftword_T dumsft;
+#define HIKEY2SFT(p) ((sftword_T *)((p) - (dumsft.sft_word - (char_u *)&dumsft)))
+#define HI2SFT(hi) HIKEY2SFT((hi)->hi_key)
+
+/// Prepare for calling suggest_try_soundalike().
+static void suggest_try_soundalike_prep(void)
+{
+ langp_T *lp;
+ slang_T *slang;
+
+ // Do this for all languages that support sound folding and for which a
+ // .sug file has been loaded.
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
+ lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
+ slang = lp->lp_slang;
+ if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
+ // prepare the hashtable used by add_sound_suggest()
+ hash_init(&slang->sl_sounddone);
+ }
+ }
+}
+
+/// Find suggestions by comparing the word in a sound-a-like form.
+/// Note: This doesn't support postponed prefixes.
+static void suggest_try_soundalike(suginfo_T *su)
+{
+ char_u salword[MAXWLEN];
+ langp_T *lp;
+ slang_T *slang;
+
+ // Do this for all languages that support sound folding and for which a
+ // .sug file has been loaded.
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
+ lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
+ slang = lp->lp_slang;
+ if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
+ // soundfold the bad word
+ spell_soundfold(slang, su->su_fbadword, true, salword);
+
+ // try all kinds of inserts/deletes/swaps/etc.
+ // TODO(vim): also soundfold the next words, so that we can try joining
+ // and splitting
+#ifdef SUGGEST_PROFILE
+ prof_init();
+#endif
+ suggest_trie_walk(su, lp, salword, true);
+#ifdef SUGGEST_PROFILE
+ prof_report("soundalike");
+#endif
+ }
+ }
+}
+
+/// Finish up after calling suggest_try_soundalike().
+static void suggest_try_soundalike_finish(void)
+{
+ langp_T *lp;
+ slang_T *slang;
+ int todo;
+ hashitem_T *hi;
+
+ // Do this for all languages that support sound folding and for which a
+ // .sug file has been loaded.
+ for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; lpi++) {
+ lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi);
+ slang = lp->lp_slang;
+ if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) {
+ // Free the info about handled words.
+ todo = (int)slang->sl_sounddone.ht_used;
+ for (hi = slang->sl_sounddone.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ xfree(HI2SFT(hi));
+ todo--;
+ }
+ }
+
+ // Clear the hashtable, it may also be used by another region.
+ hash_clear(&slang->sl_sounddone);
+ hash_init(&slang->sl_sounddone);
+ }
+ }
+}
+
+/// A match with a soundfolded word is found. Add the good word(s) that
+/// produce this soundfolded word.
+///
+/// @param score soundfold score
+static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_T *lp)
+{
+ slang_T *slang = lp->lp_slang; // language for sound folding
+ int sfwordnr;
+ char_u *nrline;
+ int orgnr;
+ char_u theword[MAXWLEN];
+ int i;
+ int wlen;
+ char_u *byts;
+ idx_T *idxs;
+ int n;
+ int wordcount;
+ int wc;
+ int goodscore;
+ hash_T hash;
+ hashitem_T *hi;
+ sftword_T *sft;
+ int bc, gc;
+ int limit;
+
+ // It's very well possible that the same soundfold word is found several
+ // times with different scores. Since the following is quite slow only do
+ // the words that have a better score than before. Use a hashtable to
+ // remember the words that have been done.
+ hash = hash_hash(goodword);
+ const size_t goodword_len = STRLEN(goodword);
+ hi = hash_lookup(&slang->sl_sounddone, (const char *)goodword, goodword_len,
+ hash);
+ if (HASHITEM_EMPTY(hi)) {
+ sft = xmalloc(sizeof(sftword_T) + goodword_len);
+ sft->sft_score = (int16_t)score;
+ memcpy(sft->sft_word, goodword, goodword_len + 1);
+ hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash);
+ } else {
+ sft = HI2SFT(hi);
+ if (score >= sft->sft_score) {
+ return;
+ }
+ sft->sft_score = (int16_t)score;
+ }
+
+ // Find the word nr in the soundfold tree.
+ sfwordnr = soundfold_find(slang, goodword);
+ if (sfwordnr < 0) {
+ internal_error("add_sound_suggest()");
+ return;
+ }
+
+ // Go over the list of good words that produce this soundfold word
+ nrline = ml_get_buf(slang->sl_sugbuf, (linenr_T)sfwordnr + 1, false);
+ orgnr = 0;
+ while (*nrline != NUL) {
+ // The wordnr was stored in a minimal nr of bytes as an offset to the
+ // previous wordnr.
+ orgnr += bytes2offset(&nrline);
+
+ byts = slang->sl_fbyts;
+ idxs = slang->sl_fidxs;
+
+ // Lookup the word "orgnr" one of the two tries.
+ n = 0;
+ wordcount = 0;
+ for (wlen = 0; wlen < MAXWLEN - 3; wlen++) {
+ i = 1;
+ if (wordcount == orgnr && byts[n + 1] == NUL) {
+ break; // found end of word
+ }
+ if (byts[n + 1] == NUL) {
+ wordcount++;
+ }
+
+ // skip over the NUL bytes
+ for (; byts[n + i] == NUL; i++) {
+ if (i > byts[n]) { // safety check
+ STRCPY(theword + wlen, "BAD");
+ wlen += 3;
+ goto badword;
+ }
+ }
+
+ // One of the siblings must have the word.
+ for (; i < byts[n]; i++) {
+ wc = idxs[idxs[n + i]]; // nr of words under this byte
+ if (wordcount + wc > orgnr) {
+ break;
+ }
+ wordcount += wc;
+ }
+
+ theword[wlen] = byts[n + i];
+ n = idxs[n + i];
+ }
+badword:
+ theword[wlen] = NUL;
+
+ // Go over the possible flags and regions.
+ for (; i <= byts[n] && byts[n + i] == NUL; i++) {
+ char_u cword[MAXWLEN];
+ char_u *p;
+ int flags = (int)idxs[n + i];
+
+ // Skip words with the NOSUGGEST flag
+ if (flags & WF_NOSUGGEST) {
+ continue;
+ }
+
+ if (flags & WF_KEEPCAP) {
+ // Must find the word in the keep-case tree.
+ find_keepcap_word(slang, theword, cword);
+ p = cword;
+ } else {
+ flags |= su->su_badflags;
+ if ((flags & WF_CAPMASK) != 0) {
+ // Need to fix case according to "flags".
+ make_case_word(theword, cword, flags);
+ p = cword;
+ } else {
+ p = theword;
+ }
+ }
+
+ // Add the suggestion.
+ if (sps_flags & SPS_DOUBLE) {
+ // Add the suggestion if the score isn't too bad.
+ if (score <= su->su_maxscore) {
+ add_suggestion(su, &su->su_sga, p, su->su_badlen,
+ score, 0, false, slang, false);
+ }
+ } else {
+ // Add a penalty for words in another region.
+ if ((flags & WF_REGION)
+ && (((unsigned)flags >> 16) & (unsigned)lp->lp_region) == 0) {
+ goodscore = SCORE_REGION;
+ } else {
+ goodscore = 0;
+ }
+
+ // Add a small penalty for changing the first letter from
+ // lower to upper case. Helps for "tath" -> "Kath", which is
+ // less common than "tath" -> "path". Don't do it when the
+ // letter is the same, that has already been counted.
+ gc = utf_ptr2char((char *)p);
+ if (SPELL_ISUPPER(gc)) {
+ bc = utf_ptr2char((char *)su->su_badword);
+ if (!SPELL_ISUPPER(bc)
+ && SPELL_TOFOLD(bc) != SPELL_TOFOLD(gc)) {
+ goodscore += SCORE_ICASE / 2;
+ }
+ }
+
+ // Compute the score for the good word. This only does letter
+ // insert/delete/swap/replace. REP items are not considered,
+ // which may make the score a bit higher.
+ // Use a limit for the score to make it work faster. Use
+ // MAXSCORE(), because RESCORE() will change the score.
+ // If the limit is very high then the iterative method is
+ // inefficient, using an array is quicker.
+ limit = MAXSCORE(su->su_sfmaxscore - goodscore, score);
+ if (limit > SCORE_LIMITMAX) {
+ goodscore += spell_edit_score(slang, su->su_badword, p);
+ } else {
+ goodscore += spell_edit_score_limit(slang, su->su_badword,
+ p, limit);
+ }
+
+ // When going over the limit don't bother to do the rest.
+ if (goodscore < SCORE_MAXMAX) {
+ // Give a bonus to words seen before.
+ goodscore = score_wordcount_adj(slang, goodscore, p, false);
+
+ // Add the suggestion if the score isn't too bad.
+ goodscore = RESCORE(goodscore, score);
+ if (goodscore <= su->su_sfmaxscore) {
+ add_suggestion(su, &su->su_ga, p, su->su_badlen,
+ goodscore, score, true, slang, true);
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Find word "word" in fold-case tree for "slang" and return the word number.
+static int soundfold_find(slang_T *slang, char_u *word)
+{
+ idx_T arridx = 0;
+ int len;
+ int wlen = 0;
+ int c;
+ char_u *ptr = word;
+ char_u *byts;
+ idx_T *idxs;
+ int wordnr = 0;
+
+ byts = slang->sl_sbyts;
+ idxs = slang->sl_sidxs;
+
+ for (;;) {
+ // First byte is the number of possible bytes.
+ len = byts[arridx++];
+
+ // If the first possible byte is a zero the word could end here.
+ // If the word ends we found the word. If not skip the NUL bytes.
+ c = ptr[wlen];
+ if (byts[arridx] == NUL) {
+ if (c == NUL) {
+ break;
+ }
+
+ // Skip over the zeros, there can be several.
+ while (len > 0 && byts[arridx] == NUL) {
+ arridx++;
+ len--;
+ }
+ if (len == 0) {
+ return -1; // no children, word should have ended here
+ }
+ wordnr++;
+ }
+
+ // If the word ends we didn't find it.
+ if (c == NUL) {
+ return -1;
+ }
+
+ // Perform a binary search in the list of accepted bytes.
+ if (c == TAB) { // <Tab> is handled like <Space>
+ c = ' ';
+ }
+ while (byts[arridx] < c) {
+ // The word count is in the first idxs[] entry of the child.
+ wordnr += idxs[idxs[arridx]];
+ arridx++;
+ if (--len == 0) { // end of the bytes, didn't find it
+ return -1;
+ }
+ }
+ if (byts[arridx] != c) { // didn't find the byte
+ return -1;
+ }
+
+ // Continue at the child (if there is one).
+ arridx = idxs[arridx];
+ wlen++;
+
+ // One space in the good word may stand for several spaces in the
+ // checked word.
+ if (c == ' ') {
+ while (ptr[wlen] == ' ' || ptr[wlen] == TAB) {
+ wlen++;
+ }
+ }
+ }
+
+ return wordnr;
+}
+
+/// Returns true if "c1" and "c2" are similar characters according to the MAP
+/// lines in the .aff file.
+static bool similar_chars(slang_T *slang, int c1, int c2)
+{
+ int m1, m2;
+ char buf[MB_MAXBYTES + 1];
+ hashitem_T *hi;
+
+ if (c1 >= 256) {
+ buf[utf_char2bytes(c1, (char *)buf)] = 0;
+ hi = hash_find(&slang->sl_map_hash, buf);
+ if (HASHITEM_EMPTY(hi)) {
+ m1 = 0;
+ } else {
+ m1 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
+ }
+ } else {
+ m1 = slang->sl_map_array[c1];
+ }
+ if (m1 == 0) {
+ return false;
+ }
+
+ if (c2 >= 256) {
+ buf[utf_char2bytes(c2, (char *)buf)] = 0;
+ hi = hash_find(&slang->sl_map_hash, buf);
+ if (HASHITEM_EMPTY(hi)) {
+ m2 = 0;
+ } else {
+ m2 = utf_ptr2char((char *)hi->hi_key + STRLEN(hi->hi_key) + 1);
+ }
+ } else {
+ m2 = slang->sl_map_array[c2];
+ }
+
+ return m1 == m2;
+}
+
+/// Adds a suggestion to the list of suggestions.
+/// For a suggestion that is already in the list the lowest score is remembered.
+///
+/// @param gap either su_ga or su_sga
+/// @param badlenarg len of bad word replaced with "goodword"
+/// @param had_bonus value for st_had_bonus
+/// @param slang language for sound folding
+/// @param maxsf su_maxscore applies to soundfold score, su_sfmaxscore to the total score.
+static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, int badlenarg,
+ int score, int altscore, bool had_bonus, slang_T *slang, bool maxsf)
+{
+ int goodlen; // len of goodword changed
+ int badlen; // len of bad word changed
+ suggest_T *stp;
+ suggest_T new_sug;
+
+ // Minimize "badlen" for consistency. Avoids that changing "the the" to
+ // "thee the" is added next to changing the first "the" the "thee".
+ const char_u *pgood = goodword + STRLEN(goodword);
+ char_u *pbad = su->su_badptr + badlenarg;
+ for (;;) {
+ goodlen = (int)(pgood - goodword);
+ badlen = (int)(pbad - su->su_badptr);
+ if (goodlen <= 0 || badlen <= 0) {
+ break;
+ }
+ MB_PTR_BACK(goodword, pgood);
+ MB_PTR_BACK(su->su_badptr, pbad);
+ if (utf_ptr2char((char *)pgood) != utf_ptr2char((char *)pbad)) {
+ break;
+ }
+ }
+
+ if (badlen == 0 && goodlen == 0) {
+ // goodword doesn't change anything; may happen for "the the" changing
+ // the first "the" to itself.
+ return;
+ }
+
+ int i;
+ if (GA_EMPTY(gap)) {
+ i = -1;
+ } else {
+ // Check if the word is already there. Also check the length that is
+ // being replaced "thes," -> "these" is a different suggestion from
+ // "thes" -> "these".
+ stp = &SUG(*gap, 0);
+ for (i = gap->ga_len; --i >= 0; stp++) {
+ if (stp->st_wordlen == goodlen
+ && stp->st_orglen == badlen
+ && STRNCMP(stp->st_word, goodword, goodlen) == 0) {
+ // Found it. Remember the word with the lowest score.
+ if (stp->st_slang == NULL) {
+ stp->st_slang = slang;
+ }
+
+ new_sug.st_score = score;
+ new_sug.st_altscore = altscore;
+ new_sug.st_had_bonus = had_bonus;
+
+ if (stp->st_had_bonus != had_bonus) {
+ // Only one of the two had the soundalike score computed.
+ // Need to do that for the other one now, otherwise the
+ // scores can't be compared. This happens because
+ // suggest_try_change() doesn't compute the soundalike
+ // word to keep it fast, while some special methods set
+ // the soundalike score to zero.
+ if (had_bonus) {
+ rescore_one(su, stp);
+ } else {
+ new_sug.st_word = stp->st_word;
+ new_sug.st_wordlen = stp->st_wordlen;
+ new_sug.st_slang = stp->st_slang;
+ new_sug.st_orglen = badlen;
+ rescore_one(su, &new_sug);
+ }
+ }
+
+ if (stp->st_score > new_sug.st_score) {
+ stp->st_score = new_sug.st_score;
+ stp->st_altscore = new_sug.st_altscore;
+ stp->st_had_bonus = new_sug.st_had_bonus;
+ }
+ break;
+ }
+ }
+ }
+
+ if (i < 0) {
+ // Add a suggestion.
+ stp = GA_APPEND_VIA_PTR(suggest_T, gap);
+ stp->st_word = vim_strnsave(goodword, (size_t)goodlen);
+ stp->st_wordlen = goodlen;
+ stp->st_score = score;
+ stp->st_altscore = altscore;
+ stp->st_had_bonus = had_bonus;
+ stp->st_orglen = badlen;
+ stp->st_slang = slang;
+
+ // If we have too many suggestions now, sort the list and keep
+ // the best suggestions.
+ if (gap->ga_len > SUG_MAX_COUNT(su)) {
+ if (maxsf) {
+ su->su_sfmaxscore = cleanup_suggestions(gap,
+ su->su_sfmaxscore, SUG_CLEAN_COUNT(su));
+ } else {
+ su->su_maxscore = cleanup_suggestions(gap,
+ su->su_maxscore, SUG_CLEAN_COUNT(su));
+ }
+ }
+ }
+}
+
+/// Suggestions may in fact be flagged as errors. Esp. for banned words and
+/// for split words, such as "the the". Remove these from the list here.
+///
+/// @param gap either su_ga or su_sga
+static void check_suggestions(suginfo_T *su, garray_T *gap)
+{
+ suggest_T *stp;
+ char_u longword[MAXWLEN + 1];
+ int len;
+ hlf_T attr;
+
+ if (gap->ga_len == 0) {
+ return;
+ }
+ stp = &SUG(*gap, 0);
+ for (int i = gap->ga_len - 1; i >= 0; i--) {
+ // Need to append what follows to check for "the the".
+ STRLCPY(longword, stp[i].st_word, MAXWLEN + 1);
+ len = stp[i].st_wordlen;
+ STRLCPY(longword + len, su->su_badptr + stp[i].st_orglen,
+ MAXWLEN - len + 1);
+ attr = HLF_COUNT;
+ (void)spell_check(curwin, longword, &attr, NULL, false);
+ if (attr != HLF_COUNT) {
+ // Remove this entry.
+ xfree(stp[i].st_word);
+ gap->ga_len--;
+ if (i < gap->ga_len) {
+ memmove(stp + i, stp + i + 1, sizeof(suggest_T) * (size_t)(gap->ga_len - i));
+ }
+ }
+ }
+}
+
+/// Add a word to be banned.
+static void add_banned(suginfo_T *su, char_u *word)
+{
+ char_u *s;
+ hash_T hash;
+ hashitem_T *hi;
+
+ hash = hash_hash(word);
+ const size_t word_len = STRLEN(word);
+ hi = hash_lookup(&su->su_banned, (const char *)word, word_len, hash);
+ if (HASHITEM_EMPTY(hi)) {
+ s = xmemdupz(word, word_len);
+ hash_add_item(&su->su_banned, hi, s, hash);
+ }
+}
+
+/// Recompute the score for all suggestions if sound-folding is possible. This
+/// is slow, thus only done for the final results.
+static void rescore_suggestions(suginfo_T *su)
+{
+ if (su->su_sallang != NULL) {
+ for (int i = 0; i < su->su_ga.ga_len; i++) {
+ rescore_one(su, &SUG(su->su_ga, i));
+ }
+ }
+}
+
+/// Recompute the score for one suggestion if sound-folding is possible.
+static void rescore_one(suginfo_T *su, suggest_T *stp)
+{
+ slang_T *slang = stp->st_slang;
+ char_u sal_badword[MAXWLEN];
+ char_u *p;
+
+ // Only rescore suggestions that have no sal score yet and do have a
+ // language.
+ if (slang != NULL && !GA_EMPTY(&slang->sl_sal) && !stp->st_had_bonus) {
+ if (slang == su->su_sallang) {
+ p = su->su_sal_badword;
+ } else {
+ spell_soundfold(slang, su->su_fbadword, true, sal_badword);
+ p = sal_badword;
+ }
+
+ stp->st_altscore = stp_sal_score(stp, su, slang, p);
+ if (stp->st_altscore == SCORE_MAXMAX) {
+ stp->st_altscore = SCORE_BIG;
+ }
+ stp->st_score = RESCORE(stp->st_score, stp->st_altscore);
+ stp->st_had_bonus = true;
+ }
+}
+
+/// Function given to qsort() to sort the suggestions on st_score.
+/// First on "st_score", then "st_altscore" then alphabetically.
+static int sug_compare(const void *s1, const void *s2)
+{
+ suggest_T *p1 = (suggest_T *)s1;
+ suggest_T *p2 = (suggest_T *)s2;
+ int n = p1->st_score - p2->st_score;
+
+ if (n == 0) {
+ n = p1->st_altscore - p2->st_altscore;
+ if (n == 0) {
+ n = STRICMP(p1->st_word, p2->st_word);
+ }
+ }
+ return n;
+}
+
+/// Cleanup the suggestions:
+/// - Sort on score.
+/// - Remove words that won't be displayed.
+///
+/// @param keep nr of suggestions to keep
+///
+/// @return the maximum score in the list or "maxscore" unmodified.
+static int cleanup_suggestions(garray_T *gap, int maxscore, int keep)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (gap->ga_len > 0) {
+ // Sort the list.
+ qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(suggest_T), sug_compare);
+
+ // Truncate the list to the number of suggestions that will be displayed.
+ if (gap->ga_len > keep) {
+ suggest_T *const stp = &SUG(*gap, 0);
+
+ for (int i = keep; i < gap->ga_len; i++) {
+ xfree(stp[i].st_word);
+ }
+ gap->ga_len = keep;
+ if (keep >= 1) {
+ return stp[keep - 1].st_score;
+ }
+ }
+ }
+ return maxscore;
+}
+
+/// Compute a score for two sound-a-like words.
+/// This permits up to two inserts/deletes/swaps/etc. to keep things fast.
+/// Instead of a generic loop we write out the code. That keeps it fast by
+/// avoiding checks that will not be possible.
+///
+/// @param goodstart sound-folded good word
+/// @param badstart sound-folded bad word
+static int soundalike_score(char_u *goodstart, char_u *badstart)
+{
+ char_u *goodsound = goodstart;
+ char_u *badsound = badstart;
+ int goodlen;
+ int badlen;
+ int n;
+ char_u *pl, *ps;
+ char_u *pl2, *ps2;
+ int score = 0;
+
+ // Adding/inserting "*" at the start (word starts with vowel) shouldn't be
+ // counted so much, vowels in the middle of the word aren't counted at all.
+ if ((*badsound == '*' || *goodsound == '*') && *badsound != *goodsound) {
+ if ((badsound[0] == NUL && goodsound[1] == NUL)
+ || (goodsound[0] == NUL && badsound[1] == NUL)) {
+ // changing word with vowel to word without a sound
+ return SCORE_DEL;
+ }
+ if (badsound[0] == NUL || goodsound[0] == NUL) {
+ // more than two changes
+ return SCORE_MAXMAX;
+ }
+
+ if (badsound[1] == goodsound[1]
+ || (badsound[1] != NUL
+ && goodsound[1] != NUL
+ && badsound[2] == goodsound[2])) {
+ // handle like a substitute
+ } else {
+ score = 2 * SCORE_DEL / 3;
+ if (*badsound == '*') {
+ badsound++;
+ } else {
+ goodsound++;
+ }
+ }
+ }
+
+ goodlen = (int)STRLEN(goodsound);
+ badlen = (int)STRLEN(badsound);
+
+ // Return quickly if the lengths are too different to be fixed by two
+ // changes.
+ n = goodlen - badlen;
+ if (n < -2 || n > 2) {
+ return SCORE_MAXMAX;
+ }
+
+ if (n > 0) {
+ pl = goodsound; // goodsound is longest
+ ps = badsound;
+ } else {
+ pl = badsound; // badsound is longest
+ ps = goodsound;
+ }
+
+ // Skip over the identical part.
+ while (*pl == *ps && *pl != NUL) {
+ pl++;
+ ps++;
+ }
+
+ switch (n) {
+ case -2:
+ case 2:
+ // Must delete two characters from "pl".
+ pl++; // first delete
+ while (*pl == *ps) {
+ pl++;
+ ps++;
+ }
+ // strings must be equal after second delete
+ if (STRCMP(pl + 1, ps) == 0) {
+ return score + SCORE_DEL * 2;
+ }
+
+ // Failed to compare.
+ break;
+
+ case -1:
+ case 1:
+ // Minimal one delete from "pl" required.
+
+ // 1: delete
+ pl2 = pl + 1;
+ ps2 = ps;
+ while (*pl2 == *ps2) {
+ if (*pl2 == NUL) { // reached the end
+ return score + SCORE_DEL;
+ }
+ pl2++;
+ ps2++;
+ }
+
+ // 2: delete then swap, then rest must be equal
+ if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
+ && STRCMP(pl2 + 2, ps2 + 2) == 0) {
+ return score + SCORE_DEL + SCORE_SWAP;
+ }
+
+ // 3: delete then substitute, then the rest must be equal
+ if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
+ return score + SCORE_DEL + SCORE_SUBST;
+ }
+
+ // 4: first swap then delete
+ if (pl[0] == ps[1] && pl[1] == ps[0]) {
+ pl2 = pl + 2; // swap, skip two chars
+ ps2 = ps + 2;
+ while (*pl2 == *ps2) {
+ pl2++;
+ ps2++;
+ }
+ // delete a char and then strings must be equal
+ if (STRCMP(pl2 + 1, ps2) == 0) {
+ return score + SCORE_SWAP + SCORE_DEL;
+ }
+ }
+
+ // 5: first substitute then delete
+ pl2 = pl + 1; // substitute, skip one char
+ ps2 = ps + 1;
+ while (*pl2 == *ps2) {
+ pl2++;
+ ps2++;
+ }
+ // delete a char and then strings must be equal
+ if (STRCMP(pl2 + 1, ps2) == 0) {
+ return score + SCORE_SUBST + SCORE_DEL;
+ }
+
+ // Failed to compare.
+ break;
+
+ case 0:
+ // Lengths are equal, thus changes must result in same length: An
+ // insert is only possible in combination with a delete.
+ // 1: check if for identical strings
+ if (*pl == NUL) {
+ return score;
+ }
+
+ // 2: swap
+ if (pl[0] == ps[1] && pl[1] == ps[0]) {
+ pl2 = pl + 2; // swap, skip two chars
+ ps2 = ps + 2;
+ while (*pl2 == *ps2) {
+ if (*pl2 == NUL) { // reached the end
+ return score + SCORE_SWAP;
+ }
+ pl2++;
+ ps2++;
+ }
+ // 3: swap and swap again
+ if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
+ && STRCMP(pl2 + 2, ps2 + 2) == 0) {
+ return score + SCORE_SWAP + SCORE_SWAP;
+ }
+
+ // 4: swap and substitute
+ if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
+ return score + SCORE_SWAP + SCORE_SUBST;
+ }
+ }
+
+ // 5: substitute
+ pl2 = pl + 1;
+ ps2 = ps + 1;
+ while (*pl2 == *ps2) {
+ if (*pl2 == NUL) { // reached the end
+ return score + SCORE_SUBST;
+ }
+ pl2++;
+ ps2++;
+ }
+
+ // 6: substitute and swap
+ if (pl2[0] == ps2[1] && pl2[1] == ps2[0]
+ && STRCMP(pl2 + 2, ps2 + 2) == 0) {
+ return score + SCORE_SUBST + SCORE_SWAP;
+ }
+
+ // 7: substitute and substitute
+ if (STRCMP(pl2 + 1, ps2 + 1) == 0) {
+ return score + SCORE_SUBST + SCORE_SUBST;
+ }
+
+ // 8: insert then delete
+ pl2 = pl;
+ ps2 = ps + 1;
+ while (*pl2 == *ps2) {
+ pl2++;
+ ps2++;
+ }
+ if (STRCMP(pl2 + 1, ps2) == 0) {
+ return score + SCORE_INS + SCORE_DEL;
+ }
+
+ // 9: delete then insert
+ pl2 = pl + 1;
+ ps2 = ps;
+ while (*pl2 == *ps2) {
+ pl2++;
+ ps2++;
+ }
+ if (STRCMP(pl2, ps2 + 1) == 0) {
+ return score + SCORE_INS + SCORE_DEL;
+ }
+
+ // Failed to compare.
+ break;
+ }
+
+ return SCORE_MAXMAX;
+}
+
+/// Compute the "edit distance" to turn "badword" into "goodword". The less
+/// deletes/inserts/substitutes/swaps are required the lower the score.
+///
+/// The algorithm is described by Du and Chang, 1992.
+/// The implementation of the algorithm comes from Aspell editdist.cpp,
+/// edit_distance(). It has been converted from C++ to C and modified to
+/// support multi-byte characters.
+static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword)
+{
+ int *cnt;
+ int j, i;
+ int t;
+ int bc, gc;
+ int pbc, pgc;
+ int wbadword[MAXWLEN];
+ int wgoodword[MAXWLEN];
+
+ // Lengths with NUL.
+ int badlen;
+ int goodlen;
+ {
+ // Get the characters from the multi-byte strings and put them in an
+ // int array for easy access.
+ badlen = 0;
+ for (const char_u *p = badword; *p != NUL;) {
+ wbadword[badlen++] = mb_cptr2char_adv(&p);
+ }
+ wbadword[badlen++] = 0;
+ goodlen = 0;
+ for (const char_u *p = goodword; *p != NUL;) {
+ wgoodword[goodlen++] = mb_cptr2char_adv(&p);
+ }
+ wgoodword[goodlen++] = 0;
+ }
+
+ // We use "cnt" as an array: CNT(badword_idx, goodword_idx).
+#define CNT(a, b) cnt[(a) + (b) * (badlen + 1)]
+ cnt = xmalloc(sizeof(int) * ((size_t)badlen + 1) * ((size_t)goodlen + 1));
+
+ CNT(0, 0) = 0;
+ for (j = 1; j <= goodlen; j++) {
+ CNT(0, j) = CNT(0, j - 1) + SCORE_INS;
+ }
+
+ for (i = 1; i <= badlen; i++) {
+ CNT(i, 0) = CNT(i - 1, 0) + SCORE_DEL;
+ for (j = 1; j <= goodlen; j++) {
+ bc = wbadword[i - 1];
+ gc = wgoodword[j - 1];
+ if (bc == gc) {
+ CNT(i, j) = CNT(i - 1, j - 1);
+ } else {
+ // Use a better score when there is only a case difference.
+ if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) {
+ CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1);
+ } else {
+ // For a similar character use SCORE_SIMILAR.
+ if (slang != NULL
+ && slang->sl_has_map
+ && similar_chars(slang, gc, bc)) {
+ CNT(i, j) = SCORE_SIMILAR + CNT(i - 1, j - 1);
+ } else {
+ CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1);
+ }
+ }
+
+ if (i > 1 && j > 1) {
+ pbc = wbadword[i - 2];
+ pgc = wgoodword[j - 2];
+ if (bc == pgc && pbc == gc) {
+ t = SCORE_SWAP + CNT(i - 2, j - 2);
+ if (t < CNT(i, j)) {
+ CNT(i, j) = t;
+ }
+ }
+ }
+ t = SCORE_DEL + CNT(i - 1, j);
+ if (t < CNT(i, j)) {
+ CNT(i, j) = t;
+ }
+ t = SCORE_INS + CNT(i, j - 1);
+ if (t < CNT(i, j)) {
+ CNT(i, j) = t;
+ }
+ }
+ }
+ }
+
+ i = CNT(badlen - 1, goodlen - 1);
+ xfree(cnt);
+ return i;
+}
+
+typedef struct {
+ int badi;
+ int goodi;
+ int score;
+} limitscore_T;
+
+/// Like spell_edit_score(), but with a limit on the score to make it faster.
+/// May return SCORE_MAXMAX when the score is higher than "limit".
+///
+/// This uses a stack for the edits still to be tried.
+/// The idea comes from Aspell leditdist.cpp. Rewritten in C and added support
+/// for multi-byte characters.
+static int spell_edit_score_limit(slang_T *slang, char_u *badword, char_u *goodword, int limit)
+{
+ return spell_edit_score_limit_w(slang, badword, goodword, limit);
+}
+
+/// Multi-byte version of spell_edit_score_limit().
+/// Keep it in sync with the above!
+static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goodword, int limit)
+{
+ limitscore_T stack[10]; // allow for over 3 * 2 edits
+ int stackidx;
+ int bi, gi;
+ int bi2, gi2;
+ int bc, gc;
+ int score;
+ int score_off;
+ int minscore;
+ int round;
+ int wbadword[MAXWLEN];
+ int wgoodword[MAXWLEN];
+
+ // Get the characters from the multi-byte strings and put them in an
+ // int array for easy access.
+ bi = 0;
+ for (const char_u *p = badword; *p != NUL;) {
+ wbadword[bi++] = mb_cptr2char_adv(&p);
+ }
+ wbadword[bi++] = 0;
+ gi = 0;
+ for (const char_u *p = goodword; *p != NUL;) {
+ wgoodword[gi++] = mb_cptr2char_adv(&p);
+ }
+ wgoodword[gi++] = 0;
+
+ // The idea is to go from start to end over the words. So long as
+ // characters are equal just continue, this always gives the lowest score.
+ // When there is a difference try several alternatives. Each alternative
+ // increases "score" for the edit distance. Some of the alternatives are
+ // pushed unto a stack and tried later, some are tried right away. At the
+ // end of the word the score for one alternative is known. The lowest
+ // possible score is stored in "minscore".
+ stackidx = 0;
+ bi = 0;
+ gi = 0;
+ score = 0;
+ minscore = limit + 1;
+
+ for (;;) {
+ // Skip over an equal part, score remains the same.
+ for (;;) {
+ bc = wbadword[bi];
+ gc = wgoodword[gi];
+
+ if (bc != gc) { // stop at a char that's different
+ break;
+ }
+ if (bc == NUL) { // both words end
+ if (score < minscore) {
+ minscore = score;
+ }
+ goto pop; // do next alternative
+ }
+ bi++;
+ gi++;
+ }
+
+ if (gc == NUL) { // goodword ends, delete badword chars
+ do {
+ if ((score += SCORE_DEL) >= minscore) {
+ goto pop; // do next alternative
+ }
+ } while (wbadword[++bi] != NUL);
+ minscore = score;
+ } else if (bc == NUL) { // badword ends, insert badword chars
+ do {
+ if ((score += SCORE_INS) >= minscore) {
+ goto pop; // do next alternative
+ }
+ } while (wgoodword[++gi] != NUL);
+ minscore = score;
+ } else { // both words continue
+ // If not close to the limit, perform a change. Only try changes
+ // that may lead to a lower score than "minscore".
+ // round 0: try deleting a char from badword
+ // round 1: try inserting a char in badword
+ for (round = 0; round <= 1; round++) {
+ score_off = score + (round == 0 ? SCORE_DEL : SCORE_INS);
+ if (score_off < minscore) {
+ if (score_off + SCORE_EDIT_MIN >= minscore) {
+ // Near the limit, rest of the words must match. We
+ // can check that right now, no need to push an item
+ // onto the stack.
+ bi2 = bi + 1 - round;
+ gi2 = gi + round;
+ while (wgoodword[gi2] == wbadword[bi2]) {
+ if (wgoodword[gi2] == NUL) {
+ minscore = score_off;
+ break;
+ }
+ bi2++;
+ gi2++;
+ }
+ } else {
+ // try deleting a character from badword later
+ stack[stackidx].badi = bi + 1 - round;
+ stack[stackidx].goodi = gi + round;
+ stack[stackidx].score = score_off;
+ stackidx++;
+ }
+ }
+ }
+
+ if (score + SCORE_SWAP < minscore) {
+ // If swapping two characters makes a match then the
+ // substitution is more expensive, thus there is no need to
+ // try both.
+ if (gc == wbadword[bi + 1] && bc == wgoodword[gi + 1]) {
+ // Swap two characters, that is: skip them.
+ gi += 2;
+ bi += 2;
+ score += SCORE_SWAP;
+ continue;
+ }
+ }
+
+ // Substitute one character for another which is the same
+ // thing as deleting a character from both goodword and badword.
+ // Use a better score when there is only a case difference.
+ if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) {
+ score += SCORE_ICASE;
+ } else {
+ // For a similar character use SCORE_SIMILAR.
+ if (slang != NULL
+ && slang->sl_has_map
+ && similar_chars(slang, gc, bc)) {
+ score += SCORE_SIMILAR;
+ } else {
+ score += SCORE_SUBST;
+ }
+ }
+
+ if (score < minscore) {
+ // Do the substitution.
+ gi++;
+ bi++;
+ continue;
+ }
+ }
+pop:
+ // Get here to try the next alternative, pop it from the stack.
+ if (stackidx == 0) { // stack is empty, finished
+ break;
+ }
+
+ // pop an item from the stack
+ stackidx--;
+ gi = stack[stackidx].goodi;
+ bi = stack[stackidx].badi;
+ score = stack[stackidx].score;
+ }
+
+ // When the score goes over "limit" it may actually be much higher.
+ // Return a very large number to avoid going below the limit when giving a
+ // bonus.
+ if (minscore > limit) {
+ return SCORE_MAXMAX;
+ }
+ return minscore;
+}
diff --git a/src/nvim/spellsuggest.h b/src/nvim/spellsuggest.h
new file mode 100644
index 0000000000..8813a5b3f1
--- /dev/null
+++ b/src/nvim/spellsuggest.h
@@ -0,0 +1,9 @@
+#ifndef NVIM_SPELLSUGGEST_H
+#define NVIM_SPELLSUGGEST_H
+
+#include "nvim/garray.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "spellsuggest.h.generated.h"
+#endif
+#endif // NVIM_SPELLSUGGEST_H
diff --git a/src/nvim/state.c b/src/nvim/state.c
index d6cca71ad8..61740800a1 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -5,6 +5,7 @@
#include "nvim/ascii.h"
#include "nvim/autocmd.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/getchar.h"
@@ -15,7 +16,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/os/input.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
new file mode 100644
index 0000000000..9b441ba0a9
--- /dev/null
+++ b/src/nvim/statusline.c
@@ -0,0 +1,1807 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+//
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "nvim/assert.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer.h"
+#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
+#include "nvim/eval/vars.h"
+#include "nvim/grid.h"
+#include "nvim/highlight.h"
+#include "nvim/highlight_group.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/optionstr.h"
+#include "nvim/statusline.h"
+#include "nvim/ui.h"
+#include "nvim/undo.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+// Determines how deeply nested %{} blocks will be evaluated in statusline.
+#define MAX_STL_EVAL_DEPTH 100
+
+/// Enumeration specifying the valid numeric bases that can
+/// be used when printing numbers in the status line.
+typedef enum {
+ kNumBaseDecimal = 10,
+ kNumBaseHexadecimal = 16,
+} NumberBase;
+
+/// Redraw the status line of window `wp`.
+///
+/// If inversion is possible we use it. Else '=' characters are used.
+void win_redr_status(win_T *wp)
+{
+ int row;
+ int col;
+ char_u *p;
+ int len;
+ int fillchar;
+ int attr;
+ int width;
+ int this_ru_col;
+ bool is_stl_global = global_stl_height() > 0;
+ static bool busy = false;
+
+ // May get here recursively when 'statusline' (indirectly)
+ // invokes ":redrawstatus". Simply ignore the call then.
+ if (busy
+ // Also ignore if wildmenu is showing.
+ || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) {
+ return;
+ }
+ busy = true;
+
+ wp->w_redr_status = false;
+ if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) {
+ // no status line, either global statusline is enabled or the window is a last window
+ redraw_cmdline = true;
+ } else if (!redrawing()) {
+ // Don't redraw right now, do it later. Don't update status line when
+ // popup menu is visible and may be drawn over it
+ wp->w_redr_status = true;
+ } else if (*p_stl != NUL || *wp->w_p_stl != NUL) {
+ // redraw custom status line
+ redraw_custom_statusline(wp);
+ } else {
+ fillchar = fillchar_status(&attr, wp);
+ width = is_stl_global ? Columns : wp->w_width;
+
+ get_trans_bufname(wp->w_buffer);
+ p = (char_u *)NameBuff;
+ len = (int)STRLEN(p);
+
+ if (bt_help(wp->w_buffer)
+ || wp->w_p_pvw
+ || bufIsChanged(wp->w_buffer)
+ || wp->w_buffer->b_p_ro) {
+ *(p + len++) = ' ';
+ }
+ if (bt_help(wp->w_buffer)) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
+ len += (int)STRLEN(p + len);
+ }
+ if (wp->w_p_pvw) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
+ len += (int)STRLEN(p + len);
+ }
+ if (bufIsChanged(wp->w_buffer)) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", "[+]");
+ len += (int)STRLEN(p + len);
+ }
+ if (wp->w_buffer->b_p_ro) {
+ snprintf((char *)p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
+ // len += (int)STRLEN(p + len); // dead assignment
+ }
+
+ this_ru_col = ru_col - (Columns - width);
+ if (this_ru_col < (width + 1) / 2) {
+ this_ru_col = (width + 1) / 2;
+ }
+ if (this_ru_col <= 1) {
+ p = (char_u *)"<"; // No room for file name!
+ len = 1;
+ } else {
+ int clen = 0, i;
+
+ // Count total number of display cells.
+ clen = (int)mb_string2cells((char *)p);
+
+ // Find first character that will fit.
+ // Going from start to end is much faster for DBCS.
+ for (i = 0; p[i] != NUL && clen >= this_ru_col - 1;
+ i += utfc_ptr2len((char *)p + i)) {
+ clen -= utf_ptr2cells((char *)p + i);
+ }
+ len = clen;
+ if (i > 0) {
+ p = p + i - 1;
+ *p = '<';
+ len++;
+ }
+ }
+
+ row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
+ col = is_stl_global ? 0 : wp->w_wincol;
+ grid_puts(&default_grid, (char *)p, row, col, attr);
+ grid_fill(&default_grid, row, row + 1, len + col,
+ this_ru_col + col, fillchar, fillchar, attr);
+
+ if (get_keymap_str(wp, "<%s>", (char *)NameBuff, MAXPATHL)
+ && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) {
+ grid_puts(&default_grid, NameBuff, row,
+ (int)((size_t)this_ru_col - STRLEN(NameBuff) - 1), attr);
+ }
+
+ win_redr_ruler(wp, true);
+ }
+
+ // May need to draw the character below the vertical separator.
+ if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) {
+ if (stl_connected(wp)) {
+ fillchar = fillchar_status(&attr, wp);
+ } else {
+ fillchar = fillchar_vsep(wp, &attr);
+ }
+ grid_putchar(&default_grid, fillchar, W_ENDROW(wp), W_ENDCOL(wp), attr);
+ }
+ busy = false;
+}
+
+void win_redr_winbar(win_T *wp)
+{
+ static bool entered = false;
+
+ // Return when called recursively. This can happen when the winbar contains an expression
+ // that triggers a redraw.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ if (wp->w_winbar_height == 0 || !redrawing()) {
+ // Do nothing.
+ } else if (*p_wbr != NUL || *wp->w_p_wbr != NUL) {
+ int saved_did_emsg = did_emsg;
+
+ did_emsg = false;
+ win_redr_custom(wp, true, false);
+ if (did_emsg) {
+ // When there is an error disable the winbar, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct("winbar", -1, "",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ }
+ entered = false;
+}
+
+void win_redr_ruler(win_T *wp, bool always)
+{
+ bool is_stl_global = global_stl_height() > 0;
+ static bool did_show_ext_ruler = false;
+
+ // If 'ruler' off, don't do anything
+ if (!p_ru) {
+ return;
+ }
+
+ // Check if cursor.lnum is valid, since win_redr_ruler() may be called
+ // after deleting lines, before cursor.lnum is corrected.
+ if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) {
+ return;
+ }
+
+ // Don't draw the ruler while doing insert-completion, it might overwrite
+ // the (long) mode message.
+ if (wp == lastwin && lastwin->w_status_height == 0 && !is_stl_global) {
+ if (edit_submode != NULL) {
+ return;
+ }
+ }
+
+ if (*p_ruf && p_ch > 0 && !ui_has(kUIMessages)) {
+ const int called_emsg_before = called_emsg;
+ win_redr_custom(wp, false, true);
+ if (called_emsg > called_emsg_before) {
+ set_string_option_direct("rulerformat", -1, "", OPT_FREE, SID_ERROR);
+ }
+ return;
+ }
+
+ // Check if not in Insert mode and the line is empty (will show "0-1").
+ int empty_line = false;
+ if ((State & MODE_INSERT) == 0 && *ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, false) == NUL) {
+ empty_line = true;
+ }
+
+ // Only draw the ruler when something changed.
+ validate_virtcol_win(wp);
+ if (redraw_cmdline
+ || always
+ || wp->w_cursor.lnum != wp->w_ru_cursor.lnum
+ || wp->w_cursor.col != wp->w_ru_cursor.col
+ || wp->w_virtcol != wp->w_ru_virtcol
+ || wp->w_cursor.coladd != wp->w_ru_cursor.coladd
+ || wp->w_topline != wp->w_ru_topline
+ || wp->w_buffer->b_ml.ml_line_count != wp->w_ru_line_count
+ || wp->w_topfill != wp->w_ru_topfill
+ || empty_line != wp->w_ru_empty) {
+ int width;
+ int row;
+ int fillchar;
+ int attr;
+ int off;
+ bool part_of_status = false;
+
+ if (wp->w_status_height) {
+ row = W_ENDROW(wp);
+ fillchar = fillchar_status(&attr, wp);
+ off = wp->w_wincol;
+ width = wp->w_width;
+ part_of_status = true;
+ } else if (is_stl_global) {
+ row = Rows - (int)p_ch - 1;
+ fillchar = fillchar_status(&attr, wp);
+ off = 0;
+ width = Columns;
+ part_of_status = true;
+ } else {
+ row = Rows - 1;
+ fillchar = ' ';
+ attr = HL_ATTR(HLF_MSG);
+ width = Columns;
+ off = 0;
+ }
+
+ if (!part_of_status && !ui_has_messages()) {
+ return;
+ }
+
+ // In list mode virtcol needs to be recomputed
+ colnr_T virtcol = wp->w_virtcol;
+ if (wp->w_p_list && wp->w_p_lcs_chars.tab1 == NUL) {
+ wp->w_p_list = false;
+ getvvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL);
+ wp->w_p_list = true;
+ }
+
+#define RULER_BUF_LEN 70
+ char buffer[RULER_BUF_LEN];
+
+ // Some sprintfs return the length, some return a pointer.
+ // To avoid portability problems we use strlen() here.
+ vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
+ (wp->w_buffer->b_ml.ml_flags &
+ ML_EMPTY) ? (int64_t)0L : (int64_t)wp->w_cursor.lnum);
+ size_t len = STRLEN(buffer);
+ col_print(buffer + len, RULER_BUF_LEN - len,
+ empty_line ? 0 : (int)wp->w_cursor.col + 1,
+ (int)virtcol + 1);
+
+ // Add a "50%" if there is room for it.
+ // On the last line, don't print in the last column (scrolls the
+ // screen up on some terminals).
+ int i = (int)STRLEN(buffer);
+ get_rel_pos(wp, buffer + i + 1, RULER_BUF_LEN - i - 1);
+ int o = i + vim_strsize(buffer + i + 1);
+ if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
+ o++;
+ }
+ int this_ru_col = ru_col - (Columns - width);
+ if (this_ru_col < 0) {
+ this_ru_col = 0;
+ }
+ // Never use more than half the window/screen width, leave the other half
+ // for the filename.
+ if (this_ru_col < (width + 1) / 2) {
+ this_ru_col = (width + 1) / 2;
+ }
+ if (this_ru_col + o < width) {
+ // Need at least 3 chars left for get_rel_pos() + NUL.
+ while (this_ru_col + o < width && RULER_BUF_LEN > i + 4) {
+ i += utf_char2bytes(fillchar, buffer + i);
+ o++;
+ }
+ get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
+ }
+
+ if (ui_has(kUIMessages) && !part_of_status) {
+ MAXSIZE_TEMP_ARRAY(content, 1);
+ MAXSIZE_TEMP_ARRAY(chunk, 2);
+ ADD_C(chunk, INTEGER_OBJ(attr));
+ ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer)));
+ ADD_C(content, ARRAY_OBJ(chunk));
+ ui_call_msg_ruler(content);
+ did_show_ext_ruler = true;
+ } else {
+ if (did_show_ext_ruler) {
+ ui_call_msg_ruler((Array)ARRAY_DICT_INIT);
+ did_show_ext_ruler = false;
+ }
+ // Truncate at window boundary.
+ o = 0;
+ for (i = 0; buffer[i] != NUL; i += utfc_ptr2len(buffer + i)) {
+ o += utf_ptr2cells(buffer + i);
+ if (this_ru_col + o > width) {
+ buffer[i] = NUL;
+ break;
+ }
+ }
+
+ ScreenGrid *grid = part_of_status ? &default_grid : &msg_grid_adj;
+ grid_puts(grid, buffer, row, this_ru_col + off, attr);
+ grid_fill(grid, row, row + 1,
+ this_ru_col + off + (int)STRLEN(buffer), off + width, fillchar,
+ fillchar, attr);
+ }
+
+ wp->w_ru_cursor = wp->w_cursor;
+ wp->w_ru_virtcol = wp->w_virtcol;
+ wp->w_ru_empty = (char)empty_line;
+ wp->w_ru_topline = wp->w_topline;
+ wp->w_ru_line_count = wp->w_buffer->b_ml.ml_line_count;
+ wp->w_ru_topfill = wp->w_topfill;
+ }
+}
+
+/// Get the character to use in a status line. Get its attributes in "*attr".
+int fillchar_status(int *attr, win_T *wp)
+{
+ int fill;
+ bool is_curwin = (wp == curwin);
+ if (is_curwin) {
+ *attr = win_hl_attr(wp, HLF_S);
+ fill = wp->w_p_fcs_chars.stl;
+ } else {
+ *attr = win_hl_attr(wp, HLF_SNC);
+ fill = wp->w_p_fcs_chars.stlnc;
+ }
+ // Use fill when there is highlighting, and highlighting of current
+ // window differs, or the fillchars differ, or this is not the
+ // current window
+ if (*attr != 0 && ((win_hl_attr(wp, HLF_S) != win_hl_attr(wp, HLF_SNC)
+ || !is_curwin || ONE_WINDOW)
+ || (wp->w_p_fcs_chars.stl != wp->w_p_fcs_chars.stlnc))) {
+ return fill;
+ }
+ if (is_curwin) {
+ return '^';
+ }
+ return '=';
+}
+
+/// Redraw the status line according to 'statusline' and take care of any
+/// errors encountered.
+void redraw_custom_statusline(win_T *wp)
+{
+ static bool entered = false;
+ int saved_did_emsg = did_emsg;
+
+ // When called recursively return. This can happen when the statusline
+ // contains an expression that triggers a redraw.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ did_emsg = false;
+ win_redr_custom(wp, false, false);
+ if (did_emsg) {
+ // When there is an error disable the statusline, otherwise the
+ // display is messed up with errors and a redraw triggers the problem
+ // again and again.
+ set_string_option_direct("statusline", -1, "",
+ OPT_FREE | (*wp->w_p_stl != NUL
+ ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR);
+ }
+ did_emsg |= saved_did_emsg;
+ entered = false;
+}
+
+/// Redraw the status line, window bar or ruler of window "wp".
+/// When "wp" is NULL redraw the tab pages line from 'tabline'.
+void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler)
+{
+ static bool entered = false;
+ int attr;
+ int curattr;
+ int row;
+ int col = 0;
+ int maxwidth;
+ int width;
+ int n;
+ int len;
+ int fillchar;
+ char buf[MAXPATHL];
+ char_u *stl;
+ char *p;
+ stl_hlrec_t *hltab;
+ StlClickRecord *tabtab;
+ int use_sandbox = false;
+ win_T *ewp;
+ int p_crb_save;
+ bool is_stl_global = global_stl_height() > 0;
+
+ ScreenGrid *grid = &default_grid;
+
+ // There is a tiny chance that this gets called recursively: When
+ // redrawing a status line triggers redrawing the ruler or tabline.
+ // Avoid trouble by not allowing recursion.
+ if (entered) {
+ return;
+ }
+ entered = true;
+
+ // setup environment for the task at hand
+ if (wp == NULL) {
+ // Use 'tabline'. Always at the first line of the screen.
+ stl = (char_u *)p_tal;
+ row = 0;
+ fillchar = ' ';
+ attr = HL_ATTR(HLF_TPF);
+ maxwidth = Columns;
+ use_sandbox = was_set_insecurely(wp, "tabline", 0);
+ } else if (draw_winbar) {
+ stl = (char_u *)((*wp->w_p_wbr != NUL) ? wp->w_p_wbr : p_wbr);
+ row = -1; // row zero is first row of text
+ col = 0;
+ grid = &wp->w_grid;
+ grid_adjust(&grid, &row, &col);
+
+ if (row < 0) {
+ return;
+ }
+
+ fillchar = wp->w_p_fcs_chars.wbr;
+ attr = (wp == curwin) ? win_hl_attr(wp, HLF_WBR) : win_hl_attr(wp, HLF_WBRNC);
+ maxwidth = wp->w_width_inner;
+ use_sandbox = was_set_insecurely(wp, "winbar", 0);
+
+ stl_clear_click_defs(wp->w_winbar_click_defs, (long)wp->w_winbar_click_defs_size);
+ // Allocate / resize the click definitions array for winbar if needed.
+ if (wp->w_winbar_height && wp->w_winbar_click_defs_size < (size_t)maxwidth) {
+ xfree(wp->w_winbar_click_defs);
+ wp->w_winbar_click_defs_size = (size_t)maxwidth;
+ wp->w_winbar_click_defs = xcalloc(wp->w_winbar_click_defs_size, sizeof(StlClickRecord));
+ }
+ } else {
+ row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp);
+ fillchar = fillchar_status(&attr, wp);
+ maxwidth = is_stl_global ? Columns : wp->w_width;
+
+ stl_clear_click_defs(wp->w_status_click_defs, (long)wp->w_status_click_defs_size);
+ // Allocate / resize the click definitions array for statusline if needed.
+ if (wp->w_status_click_defs_size < (size_t)maxwidth) {
+ xfree(wp->w_status_click_defs);
+ wp->w_status_click_defs_size = (size_t)maxwidth;
+ wp->w_status_click_defs = xcalloc(wp->w_status_click_defs_size, sizeof(StlClickRecord));
+ }
+
+ if (draw_ruler) {
+ stl = (char_u *)p_ruf;
+ // advance past any leading group spec - implicit in ru_col
+ if (*stl == '%') {
+ if (*++stl == '-') {
+ stl++;
+ }
+ if (atoi((char *)stl)) {
+ while (ascii_isdigit(*stl)) {
+ stl++;
+ }
+ }
+ if (*stl++ != '(') {
+ stl = (char_u *)p_ruf;
+ }
+ }
+ col = ru_col - (Columns - maxwidth);
+ if (col < (maxwidth + 1) / 2) {
+ col = (maxwidth + 1) / 2;
+ }
+ maxwidth = maxwidth - col;
+ if (!wp->w_status_height && !is_stl_global) {
+ grid = &msg_grid_adj;
+ row = Rows - 1;
+ maxwidth--; // writing in last column may cause scrolling
+ fillchar = ' ';
+ attr = HL_ATTR(HLF_MSG);
+ }
+
+ use_sandbox = was_set_insecurely(wp, "rulerformat", 0);
+ } else {
+ if (*wp->w_p_stl != NUL) {
+ stl = (char_u *)wp->w_p_stl;
+ } else {
+ stl = (char_u *)p_stl;
+ }
+ use_sandbox = was_set_insecurely(wp, "statusline", *wp->w_p_stl == NUL ? 0 : OPT_LOCAL);
+ }
+
+ col += is_stl_global ? 0 : wp->w_wincol;
+ }
+
+ if (maxwidth <= 0) {
+ goto theend;
+ }
+
+ // Temporarily reset 'cursorbind', we don't want a side effect from moving
+ // the cursor away and back.
+ ewp = wp == NULL ? curwin : wp;
+ p_crb_save = ewp->w_p_crb;
+ ewp->w_p_crb = false;
+
+ // Make a copy, because the statusline may include a function call that
+ // might change the option value and free the memory.
+ stl = vim_strsave(stl);
+ width =
+ build_stl_str_hl(ewp, buf, sizeof(buf), (char *)stl, use_sandbox,
+ fillchar, maxwidth, &hltab, &tabtab);
+ xfree(stl);
+ ewp->w_p_crb = p_crb_save;
+
+ // Make all characters printable.
+ p = transstr(buf, true);
+ len = (int)STRLCPY(buf, p, sizeof(buf));
+ len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1;
+ xfree(p);
+
+ // fill up with "fillchar"
+ while (width < maxwidth && len < (int)sizeof(buf) - 1) {
+ len += utf_char2bytes(fillchar, buf + len);
+ width++;
+ }
+ buf[len] = NUL;
+
+ // Draw each snippet with the specified highlighting.
+ grid_puts_line_start(grid, row);
+
+ curattr = attr;
+ p = buf;
+ for (n = 0; hltab[n].start != NULL; n++) {
+ int textlen = (int)(hltab[n].start - p);
+ grid_puts_len(grid, p, textlen, row, col, curattr);
+ col += vim_strnsize(p, textlen);
+ p = hltab[n].start;
+
+ if (hltab[n].userhl == 0) {
+ curattr = attr;
+ } else if (hltab[n].userhl < 0) {
+ curattr = syn_id2attr(-hltab[n].userhl);
+ } else if (wp != NULL && wp != curwin && wp->w_status_height != 0) {
+ curattr = highlight_stlnc[hltab[n].userhl - 1];
+ } else {
+ curattr = highlight_user[hltab[n].userhl - 1];
+ }
+ }
+ // Make sure to use an empty string instead of p, if p is beyond buf + len.
+ grid_puts(grid, p >= buf + len ? "" : p, row, col, curattr);
+
+ grid_puts_line_flush(false);
+
+ // Fill the tab_page_click_defs, w_status_click_defs or w_winbar_click_defs array for clicking
+ // in the tab page line, status line or window bar
+ StlClickDefinition *click_defs = (wp == NULL) ? tab_page_click_defs
+ : draw_winbar ? wp->w_winbar_click_defs
+ : wp->w_status_click_defs;
+
+ if (click_defs == NULL) {
+ goto theend;
+ }
+
+ col = 0;
+ len = 0;
+ p = buf;
+ StlClickDefinition cur_click_def = {
+ .type = kStlClickDisabled,
+ };
+ for (n = 0; tabtab[n].start != NULL; n++) {
+ len += vim_strnsize(p, (int)(tabtab[n].start - p));
+ while (col < len) {
+ click_defs[col++] = cur_click_def;
+ }
+ p = (char *)tabtab[n].start;
+ cur_click_def = tabtab[n].def;
+ if ((wp != NULL) && !(cur_click_def.type == kStlClickDisabled
+ || cur_click_def.type == kStlClickFuncRun)) {
+ // window bar and status line only support click functions
+ cur_click_def.type = kStlClickDisabled;
+ }
+ }
+ while (col < maxwidth) {
+ click_defs[col++] = cur_click_def;
+ }
+
+theend:
+ entered = false;
+}
+
+/// Build a string from the status line items in "fmt".
+/// Return length of string in screen cells.
+///
+/// Normally works for window "wp", except when working for 'tabline' then it
+/// is "curwin".
+///
+/// Items are drawn interspersed with the text that surrounds it
+/// Specials: %-<wid>(xxx%) => group, %= => separation marker, %< => truncation
+/// Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
+///
+/// If maxwidth is not zero, the string will be filled at any middle marker
+/// or truncated if too long, fillchar is used for all whitespace.
+///
+/// @param wp The window to build a statusline for
+/// @param out The output buffer to write the statusline to
+/// Note: This should not be NameBuff
+/// @param outlen The length of the output buffer
+/// @param fmt The statusline format string
+/// @param use_sandbox Use a sandboxed environment when evaluating fmt
+/// @param fillchar Character to use when filling empty space in the statusline
+/// @param maxwidth The maximum width to make the statusline
+/// @param hltab HL attributes (can be NULL)
+/// @param tabtab Tab clicks definition (can be NULL).
+///
+/// @return The final width of the statusline
+int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_sandbox, int fillchar,
+ int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
+{
+ static size_t stl_items_len = 20; // Initial value, grows as needed.
+ static stl_item_t *stl_items = NULL;
+ static int *stl_groupitems = NULL;
+ static stl_hlrec_t *stl_hltab = NULL;
+ static StlClickRecord *stl_tabtab = NULL;
+ static int *stl_separator_locations = NULL;
+
+#define TMPLEN 70
+ char buf_tmp[TMPLEN];
+ char win_tmp[TMPLEN];
+ char *usefmt = fmt;
+ const int save_must_redraw = must_redraw;
+ const int save_redr_type = curwin->w_redr_type;
+
+ if (stl_items == NULL) {
+ stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
+ stl_groupitems = xmalloc(sizeof(int) * stl_items_len);
+
+ // Allocate one more, because the last element is used to indicate the
+ // end of the list.
+ stl_hltab = xmalloc(sizeof(stl_hlrec_t) * (stl_items_len + 1));
+ stl_tabtab = xmalloc(sizeof(StlClickRecord) * (stl_items_len + 1));
+
+ stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
+ }
+
+ // When the format starts with "%!" then evaluate it as an expression and
+ // use the result as the actual format string.
+ if (fmt[0] == '%' && fmt[1] == '!') {
+ typval_T tv = {
+ .v_type = VAR_NUMBER,
+ .vval.v_number = wp->handle,
+ };
+ set_var(S_LEN("g:statusline_winid"), &tv, false);
+
+ usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
+ if (usefmt == NULL) {
+ usefmt = fmt;
+ }
+
+ do_unlet(S_LEN("g:statusline_winid"), true);
+ }
+
+ if (fillchar == 0) {
+ fillchar = ' ';
+ }
+
+ // The cursor in windows other than the current one isn't always
+ // up-to-date, esp. because of autocommands and timers.
+ linenr_T lnum = wp->w_cursor.lnum;
+ if (lnum > wp->w_buffer->b_ml.ml_line_count) {
+ lnum = wp->w_buffer->b_ml.ml_line_count;
+ wp->w_cursor.lnum = lnum;
+ }
+
+ // Get line & check if empty (cursorpos will show "0-1").
+ const char *line_ptr = (char *)ml_get_buf(wp->w_buffer, lnum, false);
+ bool empty_line = (*line_ptr == NUL);
+
+ // Get the byte value now, in case we need it below. This is more
+ // efficient than making a copy of the line.
+ int byteval;
+ const size_t len = STRLEN(line_ptr);
+ if (wp->w_cursor.col > (colnr_T)len) {
+ // Line may have changed since checking the cursor column, or the lnum
+ // was adjusted above.
+ wp->w_cursor.col = (colnr_T)len;
+ wp->w_cursor.coladd = 0;
+ byteval = 0;
+ } else {
+ byteval = utf_ptr2char(line_ptr + wp->w_cursor.col);
+ }
+
+ int groupdepth = 0;
+ int evaldepth = 0;
+
+ int curitem = 0;
+ bool prevchar_isflag = true;
+ bool prevchar_isitem = false;
+
+ // out_p is the current position in the output buffer
+ char *out_p = out;
+
+ // out_end_p is the last valid character in the output buffer
+ // Note: The null termination character must occur here or earlier,
+ // so any user-visible characters must occur before here.
+ char *out_end_p = (out + outlen) - 1;
+
+ // Proceed character by character through the statusline format string
+ // fmt_p is the current position in the input buffer
+ for (char *fmt_p = usefmt; *fmt_p != NUL;) {
+ if (curitem == (int)stl_items_len) {
+ size_t new_len = stl_items_len * 3 / 2;
+
+ stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len);
+ stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len);
+ stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1));
+ stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1));
+ stl_separator_locations =
+ xrealloc(stl_separator_locations, sizeof(int) * new_len);
+
+ stl_items_len = new_len;
+ }
+
+ if (*fmt_p != '%') {
+ prevchar_isflag = prevchar_isitem = false;
+ }
+
+ // Copy the formatting verbatim until we reach the end of the string
+ // or find a formatting item (denoted by `%`)
+ // or run out of room in our output buffer.
+ while (*fmt_p != NUL && *fmt_p != '%' && out_p < out_end_p) {
+ *out_p++ = *fmt_p++;
+ }
+
+ // If we have processed the entire format string or run out of
+ // room in our output buffer, exit the loop.
+ if (*fmt_p == NUL || out_p >= out_end_p) {
+ break;
+ }
+
+ // The rest of this loop will handle a single `%` item.
+ // Note: We increment here to skip over the `%` character we are currently
+ // on so we can process the item's contents.
+ fmt_p++;
+
+ // Ignore `%` at the end of the format string
+ if (*fmt_p == NUL) {
+ break;
+ }
+
+ // Two `%` in a row is the escape sequence to print a
+ // single `%` in the output buffer.
+ if (*fmt_p == '%') {
+ *out_p++ = *fmt_p++;
+ prevchar_isflag = prevchar_isitem = false;
+ continue;
+ }
+
+ // STL_SEPARATE: Separation place between left and right aligned items.
+ if (*fmt_p == STL_SEPARATE) {
+ fmt_p++;
+ // Ignored when we are inside of a grouping
+ if (groupdepth > 0) {
+ continue;
+ }
+ stl_items[curitem].type = Separate;
+ stl_items[curitem++].start = out_p;
+ continue;
+ }
+
+ // STL_TRUNCMARK: Where to begin truncating if the statusline is too long.
+ if (*fmt_p == STL_TRUNCMARK) {
+ fmt_p++;
+ stl_items[curitem].type = Trunc;
+ stl_items[curitem++].start = out_p;
+ continue;
+ }
+
+ // The end of a grouping
+ if (*fmt_p == ')') {
+ fmt_p++;
+ // Ignore if we are not actually inside a group currently
+ if (groupdepth < 1) {
+ continue;
+ }
+ groupdepth--;
+
+ // Determine how long the group is.
+ // Note: We set the current output position to null
+ // so `vim_strsize` will work.
+ char *t = stl_items[stl_groupitems[groupdepth]].start;
+ *out_p = NUL;
+ long group_len = vim_strsize(t);
+
+ // If the group contained internal items
+ // and the group did not have a minimum width,
+ // and if there were no normal items in the group,
+ // move the output pointer back to where the group started.
+ // Note: This erases any non-item characters that were in the group.
+ // Otherwise there would be no reason to do this step.
+ if (curitem > stl_groupitems[groupdepth] + 1
+ && stl_items[stl_groupitems[groupdepth]].minwid == 0) {
+ // remove group if all items are empty and highlight group
+ // doesn't change
+ int group_start_userhl = 0;
+ int group_end_userhl = 0;
+ int n;
+ for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) {
+ if (stl_items[n].type == Highlight) {
+ group_start_userhl = group_end_userhl = stl_items[n].minwid;
+ break;
+ }
+ }
+ for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
+ if (stl_items[n].type == Normal) {
+ break;
+ }
+ if (stl_items[n].type == Highlight) {
+ group_end_userhl = stl_items[n].minwid;
+ }
+ }
+ if (n == curitem && group_start_userhl == group_end_userhl) {
+ // empty group
+ out_p = t;
+ group_len = 0;
+ for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
+ // do not use the highlighting from the removed group
+ if (stl_items[n].type == Highlight) {
+ stl_items[n].type = Empty;
+ }
+ // adjust the start position of TabPage to the next
+ // item position
+ if (stl_items[n].type == TabPage) {
+ stl_items[n].start = out_p;
+ }
+ }
+ }
+ }
+
+ // If the group is longer than it is allowed to be
+ // truncate by removing bytes from the start of the group text.
+ if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) {
+ // { Determine the number of bytes to remove
+
+ // Find the first character that should be included.
+ long n = 0;
+ while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) {
+ group_len -= ptr2cells(t + n);
+ n += utfc_ptr2len(t + n);
+ }
+ // }
+
+ // Prepend the `<` to indicate that the output was truncated.
+ *t = '<';
+
+ // { Move the truncated output
+ memmove(t + 1, t + n, (size_t)(out_p - (t + n)));
+ out_p = out_p - n + 1;
+ // Fill up space left over by half a double-wide char.
+ while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
+ MB_CHAR2BYTES(fillchar, out_p);
+ }
+ // }
+
+ // correct the start of the items for the truncation
+ for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) {
+ // Shift everything back by the number of removed bytes
+ // Minus one for the leading '<' added above.
+ stl_items[idx].start -= n - 1;
+
+ // If the item was partially or completely truncated, set its
+ // start to the start of the group
+ if (stl_items[idx].start < t) {
+ stl_items[idx].start = t;
+ }
+ }
+ // If the group is shorter than the minimum width, add padding characters.
+ } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
+ long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
+ // If the group is left-aligned, add characters to the right.
+ if (min_group_width < 0) {
+ min_group_width = 0 - min_group_width;
+ while (group_len++ < min_group_width && out_p < out_end_p) {
+ MB_CHAR2BYTES(fillchar, out_p);
+ }
+ // If the group is right-aligned, shift everything to the right and
+ // prepend with filler characters.
+ } else {
+ // { Move the group to the right
+ group_len = (min_group_width - group_len) * utf_char2len(fillchar);
+ memmove(t + group_len, t, (size_t)(out_p - t));
+ if (out_p + group_len >= (out_end_p + 1)) {
+ group_len = (long)(out_end_p - out_p);
+ }
+ out_p += group_len;
+ // }
+
+ // Adjust item start positions
+ for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
+ stl_items[n].start += group_len;
+ }
+
+ // Prepend the fill characters
+ for (; group_len > 0; group_len--) {
+ MB_CHAR2BYTES(fillchar, t);
+ }
+ }
+ }
+ continue;
+ }
+ int minwid = 0;
+ int maxwid = 9999;
+ bool left_align = false;
+
+ // Denotes that numbers should be left-padded with zeros
+ bool zeropad = (*fmt_p == '0');
+ if (zeropad) {
+ fmt_p++;
+ }
+
+ // Denotes that the item should be left-aligned.
+ // This is tracked by using a negative length.
+ if (*fmt_p == '-') {
+ fmt_p++;
+ left_align = true;
+ }
+
+ // The first digit group is the item's min width
+ if (ascii_isdigit(*fmt_p)) {
+ minwid = getdigits_int(&fmt_p, false, 0);
+ }
+
+ // User highlight groups override the min width field
+ // to denote the styling to use.
+ if (*fmt_p == STL_USER_HL) {
+ stl_items[curitem].type = Highlight;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = minwid > 9 ? 1 : minwid;
+ fmt_p++;
+ curitem++;
+ continue;
+ }
+
+ // TABPAGE pairs are used to denote a region that when clicked will
+ // either switch to or close a tab.
+ //
+ // Ex: tabline=%0Ttab\ zero%X
+ // This tabline has a TABPAGENR item with minwid `0`,
+ // which is then closed with a TABCLOSENR item.
+ // Clicking on this region with mouse enabled will switch to tab 0.
+ // Setting the minwid to a different value will switch
+ // to that tab, if it exists
+ //
+ // Ex: tabline=%1Xtab\ one%X
+ // This tabline has a TABCLOSENR item with minwid `1`,
+ // which is then closed with a TABCLOSENR item.
+ // Clicking on this region with mouse enabled will close tab 0.
+ // This is determined by the following formula:
+ // tab to close = (1 - minwid)
+ // This is because for TABPAGENR we use `minwid` = `tab number`.
+ // For TABCLOSENR we store the tab number as a negative value.
+ // Because 0 is a valid TABPAGENR value, we have to
+ // start our numbering at `-1`.
+ // So, `-1` corresponds to us wanting to close tab `0`
+ //
+ // Note: These options are only valid when creating a tabline.
+ if (*fmt_p == STL_TABPAGENR || *fmt_p == STL_TABCLOSENR) {
+ if (*fmt_p == STL_TABCLOSENR) {
+ if (minwid == 0) {
+ // %X ends the close label, go back to the previous tab label nr.
+ for (long n = curitem - 1; n >= 0; n--) {
+ if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) {
+ minwid = stl_items[n].minwid;
+ break;
+ }
+ }
+ } else {
+ // close nrs are stored as negative values
+ minwid = -minwid;
+ }
+ }
+ stl_items[curitem].type = TabPage;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = minwid;
+ fmt_p++;
+ curitem++;
+ continue;
+ }
+
+ if (*fmt_p == STL_CLICK_FUNC) {
+ fmt_p++;
+ char *t = fmt_p;
+ while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
+ fmt_p++;
+ }
+ if (*fmt_p != STL_CLICK_FUNC) {
+ break;
+ }
+ stl_items[curitem].type = ClickFunc;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].cmd = xmemdupz(t, (size_t)(fmt_p - t));
+ stl_items[curitem].minwid = minwid;
+ fmt_p++;
+ curitem++;
+ continue;
+ }
+
+ // Denotes the end of the minwid
+ // the maxwid may follow immediately after
+ if (*fmt_p == '.') {
+ fmt_p++;
+ if (ascii_isdigit(*fmt_p)) {
+ maxwid = getdigits_int(&fmt_p, false, 50);
+ }
+ }
+
+ // Bound the minimum width at 50.
+ // Make the number negative to denote left alignment of the item
+ minwid = (minwid > 50 ? 50 : minwid) * (left_align ? -1 : 1);
+
+ // Denotes the start of a new group
+ if (*fmt_p == '(') {
+ stl_groupitems[groupdepth++] = curitem;
+ stl_items[curitem].type = Group;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = minwid;
+ stl_items[curitem].maxwid = maxwid;
+ fmt_p++;
+ curitem++;
+ continue;
+ }
+
+ // Denotes end of expanded %{} block
+ if (*fmt_p == '}' && evaldepth > 0) {
+ fmt_p++;
+ evaldepth--;
+ continue;
+ }
+
+ // An invalid item was specified.
+ // Continue processing on the next character of the format string.
+ if (vim_strchr(STL_ALL, *fmt_p) == NULL) {
+ fmt_p++;
+ continue;
+ }
+
+ // The status line item type
+ char opt = *fmt_p++;
+
+ // OK - now for the real work
+ NumberBase base = kNumBaseDecimal;
+ bool itemisflag = false;
+ bool fillable = true;
+ long num = -1;
+ char *str = NULL;
+ switch (opt) {
+ case STL_FILEPATH:
+ case STL_FULLPATH:
+ case STL_FILENAME:
+ // Set fillable to false so that ' ' in the filename will not
+ // get replaced with the fillchar
+ fillable = false;
+ if (buf_spname(wp->w_buffer) != NULL) {
+ STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
+ } else {
+ char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
+ : wp->w_buffer->b_fname;
+ home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
+ }
+ trans_characters((char *)NameBuff, MAXPATHL);
+ if (opt != STL_FILENAME) {
+ str = (char *)NameBuff;
+ } else {
+ str = path_tail((char *)NameBuff);
+ }
+ break;
+ case STL_VIM_EXPR: // '{'
+ {
+ char *block_start = fmt_p - 1;
+ int reevaluate = (*fmt_p == '%');
+ itemisflag = true;
+
+ if (reevaluate) {
+ fmt_p++;
+ }
+
+ // Attempt to copy the expression to evaluate into
+ // the output buffer as a null-terminated string.
+ char *t = out_p;
+ while ((*fmt_p != '}' || (reevaluate && fmt_p[-1] != '%'))
+ && *fmt_p != NUL && out_p < out_end_p) {
+ *out_p++ = *fmt_p++;
+ }
+ if (*fmt_p != '}') { // missing '}' or out of space
+ break;
+ }
+ fmt_p++;
+ if (reevaluate) {
+ out_p[-1] = 0; // remove the % at the end of %{% expr %}
+ } else {
+ *out_p = 0;
+ }
+
+ // Move our position in the output buffer
+ // to the beginning of the expression
+ out_p = t;
+
+ // { Evaluate the expression
+
+ // Store the current buffer number as a string variable
+ vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
+ set_internal_string_var("g:actual_curbuf", buf_tmp);
+ vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
+ set_internal_string_var("g:actual_curwin", (char *)win_tmp);
+
+ buf_T *const save_curbuf = curbuf;
+ win_T *const save_curwin = curwin;
+ const int save_VIsual_active = VIsual_active;
+ curwin = wp;
+ curbuf = wp->w_buffer;
+ // Visual mode is only valid in the current window.
+ if (curwin != save_curwin) {
+ VIsual_active = false;
+ }
+
+ // Note: The result stored in `t` is unused.
+ str = eval_to_string_safe(out_p, &t, use_sandbox);
+
+ curwin = save_curwin;
+ curbuf = save_curbuf;
+ VIsual_active = save_VIsual_active;
+
+ // Remove the variable we just stored
+ do_unlet(S_LEN("g:actual_curbuf"), true);
+ do_unlet(S_LEN("g:actual_curwin"), true);
+
+ // }
+
+ // Check if the evaluated result is a number.
+ // If so, convert the number to an int and free the string.
+ if (str != NULL && *str != 0) {
+ if (*skipdigits(str) == NUL) {
+ num = atoi(str);
+ XFREE_CLEAR(str);
+ itemisflag = false;
+ }
+ }
+
+ // If the output of the expression needs to be evaluated
+ // replace the %{} block with the result of evaluation
+ if (reevaluate && str != NULL && *str != 0
+ && strchr((const char *)str, '%') != NULL
+ && evaldepth < MAX_STL_EVAL_DEPTH) {
+ size_t parsed_usefmt = (size_t)(block_start - usefmt);
+ size_t str_length = STRLEN(str);
+ size_t fmt_length = STRLEN(fmt_p);
+ size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3;
+ char *new_fmt = xmalloc(new_fmt_len * sizeof(char));
+ char *new_fmt_p = new_fmt;
+
+ new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt;
+ new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length;
+ new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2;
+ new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length;
+ *new_fmt_p = 0;
+ new_fmt_p = NULL;
+
+ if (usefmt != fmt) {
+ xfree(usefmt);
+ }
+ XFREE_CLEAR(str);
+ usefmt = new_fmt;
+ fmt_p = usefmt + parsed_usefmt;
+ evaldepth++;
+ continue;
+ }
+ break;
+ }
+
+ case STL_LINE:
+ num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
+ ? 0L : (long)(wp->w_cursor.lnum);
+ break;
+
+ case STL_NUMLINES:
+ num = wp->w_buffer->b_ml.ml_line_count;
+ break;
+
+ case STL_COLUMN:
+ num = (State & MODE_INSERT) == 0 && empty_line ? 0 : (int)wp->w_cursor.col + 1;
+ break;
+
+ case STL_VIRTCOL:
+ case STL_VIRTCOL_ALT: {
+ colnr_T virtcol = wp->w_virtcol + 1;
+ // Don't display %V if it's the same as %c.
+ if (opt == STL_VIRTCOL_ALT
+ && (virtcol == (colnr_T)((State & MODE_INSERT) == 0 && empty_line
+ ? 0 : (int)wp->w_cursor.col + 1))) {
+ break;
+ }
+ num = (long)virtcol;
+ break;
+ }
+
+ case STL_PERCENTAGE:
+ num = (int)(((long)wp->w_cursor.lnum * 100L) /
+ (long)wp->w_buffer->b_ml.ml_line_count);
+ break;
+
+ case STL_ALTPERCENT:
+ // Store the position percentage in our temporary buffer.
+ // Note: We cannot store the value in `num` because
+ // `get_rel_pos` can return a named position. Ex: "Top"
+ get_rel_pos(wp, buf_tmp, TMPLEN);
+ str = buf_tmp;
+ break;
+
+ case STL_ARGLISTSTAT:
+ fillable = false;
+
+ // Note: This is important because `append_arg_number` starts appending
+ // at the end of the null-terminated string.
+ // Setting the first byte to null means it will place the argument
+ // number string at the beginning of the buffer.
+ buf_tmp[0] = 0;
+
+ // Note: The call will only return true if it actually
+ // appended data to the `buf_tmp` buffer.
+ if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
+ str = buf_tmp;
+ }
+ break;
+
+ case STL_KEYMAP:
+ fillable = false;
+ if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
+ str = buf_tmp;
+ }
+ break;
+ case STL_PAGENUM:
+ num = printer_page_num;
+ break;
+
+ case STL_BUFNO:
+ num = wp->w_buffer->b_fnum;
+ break;
+
+ case STL_OFFSET_X:
+ base = kNumBaseHexadecimal;
+ FALLTHROUGH;
+ case STL_OFFSET: {
+ long l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL,
+ false);
+ num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ?
+ 0L : l + 1 + ((State & MODE_INSERT) == 0 && empty_line ?
+ 0 : (int)wp->w_cursor.col);
+ break;
+ }
+ case STL_BYTEVAL_X:
+ base = kNumBaseHexadecimal;
+ FALLTHROUGH;
+ case STL_BYTEVAL:
+ num = byteval;
+ if (num == NL) {
+ num = 0;
+ } else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) {
+ num = NL;
+ }
+ break;
+
+ case STL_ROFLAG:
+ case STL_ROFLAG_ALT:
+ itemisflag = true;
+ if (wp->w_buffer->b_p_ro) {
+ str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
+ }
+ break;
+
+ case STL_HELPFLAG:
+ case STL_HELPFLAG_ALT:
+ itemisflag = true;
+ if (wp->w_buffer->b_help) {
+ str = (opt == STL_HELPFLAG_ALT) ? ",HLP" : _("[Help]");
+ }
+ break;
+
+ case STL_FILETYPE:
+ // Copy the filetype if it is not null and the formatted string will fit
+ // in the temporary buffer
+ // (including the brackets and null terminating character)
+ if (*wp->w_buffer->b_p_ft != NUL
+ && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
+ vim_snprintf(buf_tmp, sizeof(buf_tmp), "[%s]",
+ wp->w_buffer->b_p_ft);
+ str = buf_tmp;
+ }
+ break;
+
+ case STL_FILETYPE_ALT:
+ itemisflag = true;
+ // Copy the filetype if it is not null and the formatted string will fit
+ // in the temporary buffer
+ // (including the comma and null terminating character)
+ if (*wp->w_buffer->b_p_ft != NUL
+ && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
+ vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft);
+ // Uppercase the file extension
+ for (char *t = buf_tmp; *t != 0; t++) {
+ *t = (char)TOUPPER_LOC(*t);
+ }
+ str = buf_tmp;
+ }
+ break;
+ case STL_PREVIEWFLAG:
+ case STL_PREVIEWFLAG_ALT:
+ itemisflag = true;
+ if (wp->w_p_pvw) {
+ str = (opt == STL_PREVIEWFLAG_ALT) ? ",PRV" : _("[Preview]");
+ }
+ break;
+
+ case STL_QUICKFIX:
+ if (bt_quickfix(wp->w_buffer)) {
+ str = wp->w_llist_ref ? _(msg_loclist) : _(msg_qflist);
+ }
+ break;
+
+ case STL_MODIFIED:
+ case STL_MODIFIED_ALT:
+ itemisflag = true;
+ switch ((opt == STL_MODIFIED_ALT)
+ + bufIsChanged(wp->w_buffer) * 2
+ + (!MODIFIABLE(wp->w_buffer)) * 4) {
+ case 2:
+ str = "[+]"; break;
+ case 3:
+ str = ",+"; break;
+ case 4:
+ str = "[-]"; break;
+ case 5:
+ str = ",-"; break;
+ case 6:
+ str = "[+-]"; break;
+ case 7:
+ str = ",+-"; break;
+ }
+ break;
+
+ case STL_HIGHLIGHT: {
+ // { The name of the highlight is surrounded by `#`
+ char *t = fmt_p;
+ while (*fmt_p != '#' && *fmt_p != NUL) {
+ fmt_p++;
+ }
+ // }
+
+ // Create a highlight item based on the name
+ if (*fmt_p == '#') {
+ stl_items[curitem].type = Highlight;
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].minwid = -syn_name2id_len(t, (size_t)(fmt_p - t));
+ curitem++;
+ fmt_p++;
+ }
+ continue;
+ }
+ }
+
+ // If we made it this far, the item is normal and starts at
+ // our current position in the output buffer.
+ // Non-normal items would have `continued`.
+ stl_items[curitem].start = out_p;
+ stl_items[curitem].type = Normal;
+
+ // Copy the item string into the output buffer
+ if (str != NULL && *str) {
+ // { Skip the leading `,` or ` ` if the item is a flag
+ // and the proper conditions are met
+ char *t = str;
+ if (itemisflag) {
+ if ((t[0] && t[1])
+ && ((!prevchar_isitem && *t == ',')
+ || (prevchar_isflag && *t == ' '))) {
+ t++;
+ }
+ prevchar_isflag = true;
+ }
+ // }
+
+ long l = vim_strsize(t);
+
+ // If this item is non-empty, record that the last thing
+ // we put in the output buffer was an item
+ if (l > 0) {
+ prevchar_isitem = true;
+ }
+
+ // If the item is too wide, truncate it from the beginning
+ if (l > maxwid) {
+ while (l >= maxwid) {
+ l -= ptr2cells(t);
+ t += utfc_ptr2len(t);
+ }
+
+ // Early out if there isn't enough room for the truncation marker
+ if (out_p >= out_end_p) {
+ break;
+ }
+
+ // Add the truncation marker
+ *out_p++ = '<';
+ }
+
+ // If the item is right aligned and not wide enough,
+ // pad with fill characters.
+ if (minwid > 0) {
+ for (; l < minwid && out_p < out_end_p; l++) {
+ // Don't put a "-" in front of a digit.
+ if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
+ *out_p++ = ' ';
+ } else {
+ MB_CHAR2BYTES(fillchar, out_p);
+ }
+ }
+ minwid = 0;
+ } else {
+ // Note: The negative value denotes a left aligned item.
+ // Here we switch the minimum width back to a positive value.
+ minwid *= -1;
+ }
+
+ // { Copy the string text into the output buffer
+ for (; *t && out_p < out_end_p; t++) {
+ // Change a space by fillchar, unless fillchar is '-' and a
+ // digit follows.
+ if (fillable && *t == ' '
+ && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
+ MB_CHAR2BYTES(fillchar, out_p);
+ } else {
+ *out_p++ = *t;
+ }
+ }
+ // }
+
+ // For left-aligned items, fill any remaining space with the fillchar
+ for (; l < minwid && out_p < out_end_p; l++) {
+ MB_CHAR2BYTES(fillchar, out_p);
+ }
+
+ // Otherwise if the item is a number, copy that to the output buffer.
+ } else if (num >= 0) {
+ if (out_p + 20 > out_end_p) {
+ break; // not sufficient space
+ }
+ prevchar_isitem = true;
+
+ // { Build the formatting string
+ char nstr[20];
+ char *t = nstr;
+ if (opt == STL_VIRTCOL_ALT) {
+ *t++ = '-';
+ minwid--;
+ }
+ *t++ = '%';
+ if (zeropad) {
+ *t++ = '0';
+ }
+
+ // Note: The `*` means we take the width as one of the arguments
+ *t++ = '*';
+ *t++ = base == kNumBaseHexadecimal ? 'X' : 'd';
+ *t = 0;
+ // }
+
+ // { Determine how many characters the number will take up when printed
+ // Note: We have to cast the base because the compiler uses
+ // unsigned ints for the enum values.
+ long num_chars = 1;
+ for (long n = num; n >= (int)base; n /= (int)base) {
+ num_chars++;
+ }
+
+ // VIRTCOL_ALT takes up an extra character because
+ // of the `-` we added above.
+ if (opt == STL_VIRTCOL_ALT) {
+ num_chars++;
+ }
+ // }
+
+ assert(out_end_p >= out_p);
+ size_t remaining_buf_len = (size_t)(out_end_p - out_p) + 1;
+
+ // If the number is going to take up too much room
+ // Figure out the approximate number in "scientific" type notation.
+ // Ex: 14532 with maxwid of 4 -> '14>3'
+ if (num_chars > maxwid) {
+ // Add two to the width because the power piece will take
+ // two extra characters
+ num_chars += 2;
+
+ // How many extra characters there are
+ long n = num_chars - maxwid;
+
+ // { Reduce the number by base^n
+ while (num_chars-- > maxwid) {
+ num /= (long)base;
+ }
+ // }
+
+ // { Add the format string for the exponent bit
+ *t++ = '>';
+ *t++ = '%';
+ // Use the same base as the first number
+ *t = t[-3];
+ *++t = 0;
+ // }
+
+ vim_snprintf(out_p, remaining_buf_len, nstr, 0, num, n);
+ } else {
+ vim_snprintf(out_p, remaining_buf_len, nstr, minwid, num);
+ }
+
+ // Advance the output buffer position to the end of the
+ // number we just printed
+ out_p += STRLEN(out_p);
+
+ // Otherwise, there was nothing to print so mark the item as empty
+ } else {
+ stl_items[curitem].type = Empty;
+ }
+
+ // Only free the string buffer if we allocated it.
+ // Note: This is not needed if `str` is pointing at `tmp`
+ if (opt == STL_VIM_EXPR) {
+ XFREE_CLEAR(str);
+ }
+
+ if (num >= 0 || (!itemisflag && str && *str)) {
+ prevchar_isflag = false; // Item not NULL, but not a flag
+ }
+
+ // Item processed, move to the next
+ curitem++;
+ }
+
+ *out_p = NUL;
+ int itemcnt = curitem;
+
+ // Free the format buffer if we allocated it internally
+ if (usefmt != fmt) {
+ xfree(usefmt);
+ }
+
+ // We have now processed the entire statusline format string.
+ // What follows is post-processing to handle alignment and highlighting.
+
+ int width = vim_strsize(out);
+ if (maxwidth > 0 && width > maxwidth) {
+ // Result is too long, must truncate somewhere.
+ int item_idx = 0;
+ char *trunc_p;
+
+ // If there are no items, truncate from beginning
+ if (itemcnt == 0) {
+ trunc_p = out;
+
+ // Otherwise, look for the truncation item
+ } else {
+ // Default to truncating at the first item
+ trunc_p = stl_items[0].start;
+ item_idx = 0;
+
+ for (int i = 0; i < itemcnt; i++) {
+ if (stl_items[i].type == Trunc) {
+ // Truncate at %< stl_items.
+ trunc_p = stl_items[i].start;
+ item_idx = i;
+ break;
+ }
+ }
+ }
+
+ // If the truncation point we found is beyond the maximum
+ // length of the string, truncate the end of the string.
+ if (width - vim_strsize(trunc_p) >= maxwidth) {
+ // Walk from the beginning of the
+ // string to find the last character that will fit.
+ trunc_p = out;
+ width = 0;
+ for (;;) {
+ width += ptr2cells(trunc_p);
+ if (width >= maxwidth) {
+ break;
+ }
+
+ // Note: Only advance the pointer if the next
+ // character will fit in the available output space
+ trunc_p += utfc_ptr2len(trunc_p);
+ }
+
+ // Ignore any items in the statusline that occur after
+ // the truncation point
+ for (int i = 0; i < itemcnt; i++) {
+ if (stl_items[i].start > trunc_p) {
+ itemcnt = i;
+ break;
+ }
+ }
+
+ // Truncate the output
+ *trunc_p++ = '>';
+ *trunc_p = 0;
+
+ // Truncate at the truncation point we found
+ } else {
+ // { Determine how many bytes to remove
+ long trunc_len = 0;
+ while (width >= maxwidth) {
+ width -= ptr2cells(trunc_p + trunc_len);
+ trunc_len += utfc_ptr2len(trunc_p + trunc_len);
+ }
+ // }
+
+ // { Truncate the string
+ char *trunc_end_p = trunc_p + trunc_len;
+ STRMOVE(trunc_p + 1, trunc_end_p);
+
+ // Put a `<` to mark where we truncated at
+ *trunc_p = '<';
+
+ if (width + 1 < maxwidth) {
+ // Advance the pointer to the end of the string
+ trunc_p = trunc_p + STRLEN(trunc_p);
+ }
+
+ // Fill up for half a double-wide character.
+ while (++width < maxwidth) {
+ MB_CHAR2BYTES(fillchar, trunc_p);
+ *trunc_p = NUL;
+ }
+ // }
+
+ // { Change the start point for items based on
+ // their position relative to our truncation point
+
+ // Note: The offset is one less than the truncation length because
+ // the truncation marker `<` is not counted.
+ long item_offset = trunc_len - 1;
+
+ for (int i = item_idx; i < itemcnt; i++) {
+ // Items starting at or after the end of the truncated section need
+ // to be moved backwards.
+ if (stl_items[i].start >= trunc_end_p) {
+ stl_items[i].start -= item_offset;
+ // Anything inside the truncated area is set to start
+ // at the `<` truncation character.
+ } else {
+ stl_items[i].start = trunc_p;
+ }
+ }
+ // }
+ }
+ width = maxwidth;
+
+ // If there is room left in our statusline, and room left in our buffer,
+ // add characters at the separate marker (if there is one) to
+ // fill up the available space.
+ } else if (width < maxwidth
+ && STRLEN(out) + (size_t)(maxwidth - width) + 1 < outlen) {
+ // Find how many separators there are, which we will use when
+ // figuring out how many groups there are.
+ int num_separators = 0;
+ for (int i = 0; i < itemcnt; i++) {
+ if (stl_items[i].type == Separate) {
+ // Create an array of the start location for each
+ // separator mark.
+ stl_separator_locations[num_separators] = i;
+ num_separators++;
+ }
+ }
+
+ // If we have separated groups, then we deal with it now
+ if (num_separators) {
+ int standard_spaces = (maxwidth - width) / num_separators;
+ int final_spaces = (maxwidth - width) -
+ standard_spaces * (num_separators - 1);
+
+ for (int i = 0; i < num_separators; i++) {
+ int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces;
+ dislocation *= utf_char2len(fillchar);
+ char *start = stl_items[stl_separator_locations[i]].start;
+ char *seploc = start + dislocation;
+ STRMOVE(seploc, start);
+ for (char *s = start; s < seploc;) {
+ MB_CHAR2BYTES(fillchar, s);
+ }
+
+ for (int item_idx = stl_separator_locations[i] + 1;
+ item_idx < itemcnt;
+ item_idx++) {
+ stl_items[item_idx].start += dislocation;
+ }
+ }
+
+ width = maxwidth;
+ }
+ }
+
+ // Store the info about highlighting.
+ if (hltab != NULL) {
+ *hltab = stl_hltab;
+ stl_hlrec_t *sp = stl_hltab;
+ for (long l = 0; l < itemcnt; l++) {
+ if (stl_items[l].type == Highlight) {
+ sp->start = stl_items[l].start;
+ sp->userhl = stl_items[l].minwid;
+ sp++;
+ }
+ }
+ sp->start = NULL;
+ sp->userhl = 0;
+ }
+
+ // Store the info about tab pages labels.
+ if (tabtab != NULL) {
+ *tabtab = stl_tabtab;
+ StlClickRecord *cur_tab_rec = stl_tabtab;
+ for (long l = 0; l < itemcnt; l++) {
+ if (stl_items[l].type == TabPage) {
+ cur_tab_rec->start = stl_items[l].start;
+ if (stl_items[l].minwid == 0) {
+ cur_tab_rec->def.type = kStlClickDisabled;
+ cur_tab_rec->def.tabnr = 0;
+ } else {
+ int tabnr = stl_items[l].minwid;
+ if (stl_items[l].minwid > 0) {
+ cur_tab_rec->def.type = kStlClickTabSwitch;
+ } else {
+ cur_tab_rec->def.type = kStlClickTabClose;
+ tabnr = -tabnr;
+ }
+ cur_tab_rec->def.tabnr = tabnr;
+ }
+ cur_tab_rec->def.func = NULL;
+ cur_tab_rec++;
+ } else if (stl_items[l].type == ClickFunc) {
+ cur_tab_rec->start = stl_items[l].start;
+ cur_tab_rec->def.type = kStlClickFuncRun;
+ cur_tab_rec->def.tabnr = stl_items[l].minwid;
+ cur_tab_rec->def.func = stl_items[l].cmd;
+ cur_tab_rec++;
+ }
+ }
+ cur_tab_rec->start = NULL;
+ cur_tab_rec->def.type = kStlClickDisabled;
+ cur_tab_rec->def.tabnr = 0;
+ cur_tab_rec->def.func = NULL;
+ }
+
+ // When inside update_screen we do not want redrawing a statusline, ruler,
+ // title, etc. to trigger another redraw, it may cause an endless loop.
+ if (updating_screen) {
+ must_redraw = save_must_redraw;
+ curwin->w_redr_type = save_redr_type;
+ }
+
+ return width;
+}
diff --git a/src/nvim/statusline.h b/src/nvim/statusline.h
new file mode 100644
index 0000000000..357a9a821f
--- /dev/null
+++ b/src/nvim/statusline.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_STATUSLINE_H
+#define NVIM_STATUSLINE_H
+
+#include "nvim/buffer_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "statusline.h.generated.h"
+#endif
+
+#endif // NVIM_STATUSLINE_H
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 22effaade0..16691d0ded 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -104,10 +104,10 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, c
p += l - 1;
continue;
}
- if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
+ if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash((char *)p))) {
length++; // count a backslash
}
- ++length; // count an ordinary char
+ length++; // count an ordinary char
}
char_u *escaped_string = xmalloc(length);
@@ -120,7 +120,7 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, c
p += l - 1; // skip multibyte char
continue;
}
- if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash(p))) {
+ if (vim_strchr((char *)esc_chars, *p) != NULL || (bsl && rem_backslash((char *)p))) {
*p2++ = cc;
}
*p2++ = *p;
@@ -191,7 +191,7 @@ char *vim_strnsave_unquoted(const char *const string, const size_t length)
char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_newline)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
- char_u *d;
+ char *d;
char_u *escaped_string;
size_t l;
int csh_like;
@@ -222,13 +222,13 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
}
if ((*p == '\n' && (csh_like || do_newline))
|| (*p == '!' && (csh_like || do_special))) {
- ++length; // insert backslash
+ length++; // insert backslash
if (csh_like && do_special) {
- ++length; // insert backslash
+ length++; // insert backslash
}
}
if (do_special && find_cmdline_var(p, &l) >= 0) {
- ++length; // insert backslash
+ length++; // insert backslash
p += l - 1;
}
if (*p == '\\' && fish_like) {
@@ -238,7 +238,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
// Allocate memory for the result and fill it.
escaped_string = xmalloc(length);
- d = escaped_string;
+ d = (char *)escaped_string;
// add opening quote
#ifdef WIN32
@@ -248,7 +248,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
#endif
*d++ = '\'';
- for (const char_u *p = string; *p != NUL;) {
+ for (const char *p = (char *)string; *p != NUL;) {
#ifdef WIN32
if (!p_ssl) {
if (*p == '"') {
@@ -264,7 +264,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
*d++ = '\\';
*d++ = '\'';
*d++ = '\'';
- ++p;
+ p++;
continue;
}
if ((*p == '\n' && (csh_like || do_newline))
@@ -276,7 +276,7 @@ char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_n
*d++ = *p++;
continue;
}
- if (do_special && find_cmdline_var(p, &l) >= 0) {
+ if (do_special && find_cmdline_var((char_u *)p, &l) >= 0) {
*d++ = '\\'; // insert backslash
while (--l != SIZE_MAX) { // copy the var
*d++ = *p++;
@@ -431,8 +431,8 @@ int vim_stricmp(const char *s1, const char *s2)
if (*s1 == NUL) {
break; // strings match until NUL
}
- ++s1;
- ++s2;
+ s1++;
+ s2++;
}
return 0; // strings match
}
@@ -457,9 +457,9 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len)
if (*s1 == NUL) {
break; // strings match until NUL
}
- ++s1;
- ++s2;
- --len;
+ s1++;
+ s2++;
+ len--;
}
return 0; // strings match
}
@@ -503,7 +503,7 @@ static int sort_compare(const void *s1, const void *s2)
void sort_strings(char **files, int count)
{
- qsort((void *)files, (size_t)count, sizeof(char_u *), sort_compare);
+ qsort((void *)files, (size_t)count, sizeof(char *), sort_compare);
}
/*
@@ -516,7 +516,7 @@ bool has_non_ascii(const char_u *s)
const char_u *p;
if (s != NULL) {
- for (p = s; *p != NUL; ++p) {
+ for (p = s; *p != NUL; p++) {
if (*p >= 128) {
return true;
}
@@ -540,14 +540,12 @@ bool has_non_ascii_len(const char *const s, const size_t len)
return false;
}
-/*
- * Concatenate two strings and return the result in allocated memory.
- */
-char_u *concat_str(const char_u *restrict str1, const char_u *restrict str2)
+/// Concatenate two strings and return the result in allocated memory.
+char *concat_str(const char *restrict str1, const char *restrict str2)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL
{
size_t l = STRLEN(str1);
- char_u *dest = xmalloc(l + STRLEN(str2) + 1);
+ char *dest = xmalloc(l + STRLEN(str2) + 1);
STRCPY(dest, str1);
STRCPY(dest + l, str2);
return dest;
@@ -1511,15 +1509,15 @@ int kv_do_printf(StringBuilder *str, const char *fmt, ...)
/// Reverse text into allocated memory.
///
/// @return the allocated string.
-char_u *reverse_text(char_u *s)
+char *reverse_text(char *s)
FUNC_ATTR_NONNULL_RET
{
// Reverse the pattern.
size_t len = STRLEN(s);
- char_u *rev = xmalloc(len + 1);
+ char *rev = xmalloc(len + 1);
size_t rev_i = len;
for (size_t s_i = 0; s_i < len; s_i++) {
- const int mb_len = utfc_ptr2len((char *)s + s_i);
+ const int mb_len = utfc_ptr2len(s + s_i);
rev_i -= (size_t)mb_len;
memmove(rev + rev_i, s + s_i, (size_t)mb_len);
s_i += (size_t)mb_len - 1;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4ec4a57d68..f055cc9b0e 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -14,12 +14,13 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor_shape.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
@@ -36,13 +37,14 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/profile.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/sign.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
@@ -378,7 +380,7 @@ void syntax_start(win_T *wp, linenr_T lnum)
&& current_lnum < syn_buf->b_ml.ml_line_count) {
(void)syn_finish_line(false);
if (!current_state_stored) {
- ++current_lnum;
+ current_lnum++;
(void)store_current_state();
}
@@ -607,7 +609,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
/*
* Skip lines that end in a backslash.
*/
- for (; start_lnum > 1; --start_lnum) {
+ for (; start_lnum > 1; start_lnum--) {
line = ml_get(start_lnum - 1);
if (*line == NUL || *(line + STRLEN(line) - 1) != '\\') {
break;
@@ -685,7 +687,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
*/
validate_current_state();
- for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum) {
+ for (current_lnum = lnum; current_lnum < end_lnum; current_lnum++) {
syn_start_line();
for (;;) {
had_sync_point = syn_finish_line(true);
@@ -723,7 +725,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
} else if (found_m_endpos.col > current_col) {
current_col = found_m_endpos.col;
} else {
- ++current_col;
+ current_col++;
}
// syn_current_attr() will have skipped the check for
@@ -731,7 +733,7 @@ static void syn_sync(win_T *wp, linenr_T start_lnum, synstate_T *last_valid)
// careful not to go past the NUL.
prev_current_col = current_col;
if (syn_getcurline()[current_col] != NUL) {
- ++current_col;
+ current_col++;
}
check_state_ends();
current_col = prev_current_col;
@@ -810,9 +812,7 @@ static void restore_chartab(char_u *chartab)
}
}
-/*
- * Return TRUE if the line-continuation pattern matches in line "lnum".
- */
+/// Return true if the line-continuation pattern matches in line "lnum".
static int syn_match_linecont(linenr_T lnum)
{
if (syn_block->b_syn_linecont_prog != NULL) {
@@ -830,7 +830,7 @@ static int syn_match_linecont(linenr_T lnum)
restore_chartab(buf_chartab);
return r;
}
- return FALSE;
+ return false;
}
/*
@@ -876,7 +876,7 @@ static void syn_update_ends(bool startofline)
cur_si->si_m_endpos.lnum = 0;
cur_si->si_m_endpos.col = 0;
cur_si->si_h_endpos = cur_si->si_m_endpos;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
}
}
}
@@ -892,7 +892,7 @@ static void syn_update_ends(bool startofline)
*/
int i = current_state.ga_len - 1;
if (keepend_level >= 0) {
- for (; i > keepend_level; --i) {
+ for (; i > keepend_level; i--) {
if (CUR_STATE(i).si_flags & HL_EXTEND) {
break;
}
@@ -1029,7 +1029,7 @@ static void syn_stack_alloc(void)
// Move the states from the old array to the new one.
for (from = syn_block->b_sst_first; from != NULL;
from = from->sst_next) {
- ++to;
+ to++;
*to = *from;
to->sst_next = to + 1;
}
@@ -1184,7 +1184,7 @@ static void syn_stack_free_entry(synblock_T *block, synstate_T *p)
clear_syn_state(p);
p->sst_next = block->b_sst_firstfree;
block->b_sst_firstfree = p;
- ++block->b_sst_freecount;
+ block->b_sst_freecount++;
}
/*
@@ -1223,7 +1223,7 @@ static synstate_T *store_current_state(void)
* If the current state contains a start or end pattern that continues
* from the previous line, we can't use it. Don't store it then.
*/
- for (i = current_state.ga_len - 1; i >= 0; --i) {
+ for (i = current_state.ga_len - 1; i >= 0; i--) {
cur_si = &CUR_STATE(i);
if (cur_si->si_h_startpos.lnum >= current_lnum
|| cur_si->si_m_endpos.lnum >= current_lnum
@@ -1271,7 +1271,7 @@ static synstate_T *store_current_state(void)
// list, after *sp
p = syn_block->b_sst_firstfree;
syn_block->b_sst_firstfree = p->sst_next;
- --syn_block->b_sst_freecount;
+ syn_block->b_sst_freecount--;
if (sp == NULL) {
// Insert in front of the list
p->sst_next = syn_block->b_sst_first;
@@ -1300,7 +1300,7 @@ static synstate_T *store_current_state(void)
} else {
bp = sp->sst_union.sst_stack;
}
- for (i = 0; i < sp->sst_stacksize; ++i) {
+ for (i = 0; i < sp->sst_stacksize; i++) {
bp[i].bs_idx = CUR_STATE(i).si_idx;
bp[i].bs_flags = (int)CUR_STATE(i).si_flags;
bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
@@ -1334,7 +1334,7 @@ static void load_current_state(synstate_T *from)
} else {
bp = from->sst_union.sst_stack;
}
- for (i = 0; i < from->sst_stacksize; ++i) {
+ for (i = 0; i < from->sst_stacksize; i++) {
CUR_STATE(i).si_idx = bp[i].bs_idx;
CUR_STATE(i).si_flags = bp[i].bs_flags;
CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
@@ -1343,7 +1343,7 @@ static void load_current_state(synstate_T *from)
if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND)) {
keepend_level = i;
}
- CUR_STATE(i).si_ends = FALSE;
+ CUR_STATE(i).si_ends = false;
CUR_STATE(i).si_m_lnum = 0;
if (CUR_STATE(i).si_idx >= 0) {
CUR_STATE(i).si_next_list =
@@ -1500,7 +1500,7 @@ bool syntax_check_changed(linenr_T lnum)
/*
* Store the current state in b_sst_array[] for later use.
*/
- ++current_lnum;
+ current_lnum++;
(void)store_current_state();
}
}
@@ -1549,7 +1549,7 @@ static bool syn_finish_line(const bool syncing)
/// "col" is normally 0 for the first use in a line, and increments by one each
/// time. It's allowed to skip characters and to stop before the end of the
/// line. But only a "col" after a previously used column is allowed.
-/// When "can_spell" is not NULL set it to TRUE when spell-checking should be
+/// When "can_spell" is not NULL set it to true when spell-checking should be
/// done.
///
/// @param keep_state keep state of char at "col"
@@ -1634,11 +1634,9 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
bool zero_width_next_list = false;
garray_T zero_width_next_ga;
- /*
- * No character, no attributes! Past end of line?
- * Do try matching with an empty line (could be the start of a region).
- */
- line = syn_getcurline();
+ // No character, no attributes! Past end of line?
+ // Do try matching with an empty line (could be the start of a region).
+ line = (char_u *)syn_getcurline();
if (line[current_col] == NUL && current_col != 0) {
/*
* If we found a match after the last column, use it.
@@ -1706,12 +1704,10 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
if (syn_block->b_syn_containedin || cur_si == NULL
|| cur_si->si_cont_list != NULL) {
- /*
- * 2. Check for keywords, if on a keyword char after a non-keyword
- * char. Don't do this when syncing.
- */
+ // 2. Check for keywords, if on a keyword char after a non-keyword
+ // char. Don't do this when syncing.
if (do_keywords) {
- line = syn_getcurline();
+ line = (char_u *)syn_getcurline();
const char_u *cur_pos = line + current_col;
if (vim_iswordp_buf(cur_pos, syn_buf)
&& (current_col == 0
@@ -1730,7 +1726,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
cur_si->si_m_endpos.col = endcol;
cur_si->si_h_endpos.lnum = current_lnum;
cur_si->si_h_endpos.col = endcol;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
cur_si->si_end_idx = 0;
cur_si->si_flags = flags;
cur_si->si_seqnr = next_seqnr++;
@@ -1979,13 +1975,11 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
* Handle searching for nextgroup match.
*/
if (current_next_list != NULL && !keep_next_list) {
- /*
- * If a nextgroup was not found, continue looking for one if:
- * - this is an empty line and the "skipempty" option was given
- * - we are on white space and the "skipwhite" option was given
- */
+ // If a nextgroup was not found, continue looking for one if:
+ // - this is an empty line and the "skipempty" option was given
+ // - we are on white space and the "skipwhite" option was given
if (!found_match) {
- line = syn_getcurline();
+ line = (char_u *)syn_getcurline();
if (((current_next_flags & HL_SKIPWHITE)
&& ascii_iswhite(line[current_col]))
|| ((current_next_flags & HL_SKIPEMPTY)
@@ -2022,7 +2016,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
current_flags = 0;
current_seqnr = 0;
if (cur_si != NULL) {
- for (int idx = current_state.ga_len - 1; idx >= 0; --idx) {
+ for (int idx = current_state.ga_len - 1; idx >= 0; idx--) {
sip = &CUR_STATE(idx);
if ((current_lnum > sip->si_h_startpos.lnum
|| (current_lnum == sip->si_h_startpos.lnum
@@ -2044,10 +2038,8 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
if (can_spell != NULL) {
struct sp_syn sps;
- /*
- * set "can_spell" to TRUE if spell checking is supposed to be
- * done in the current item.
- */
+ // set "can_spell" to true if spell checking is supposed to be
+ // done in the current item.
if (syn_block->b_spell_cluster_id == 0) {
// There is no @Spell cluster: Do spelling for items without
// @NoSpell cluster.
@@ -2095,9 +2087,9 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
check_state_ends();
if (!GA_EMPTY(&current_state)
&& syn_getcurline()[current_col] != NUL) {
- ++current_col;
+ current_col++;
check_state_ends();
- --current_col;
+ current_col--;
}
}
} else if (can_spell != NULL) {
@@ -2110,7 +2102,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con
// nextgroup ends at end of line, unless "skipnl" or "skipempty" present
if (current_next_list != NULL
- && (line = syn_getcurline())[current_col] != NUL
+ && (line = (char_u *)syn_getcurline())[current_col] != NUL
&& line[current_col + 1] == NUL
&& !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))) {
current_next_list = NULL;
@@ -2190,7 +2182,7 @@ static stateitem_T *push_next_match(void)
} else {
cur_si->si_m_endpos = next_match_m_endpos;
cur_si->si_h_endpos = next_match_h_endpos;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
cur_si->si_flags |= next_match_flags;
cur_si->si_eoe_pos = next_match_eoe_pos;
cur_si->si_end_idx = next_match_end_idx;
@@ -2214,7 +2206,7 @@ static stateitem_T *push_next_match(void)
cur_si->si_m_lnum = current_lnum;
cur_si->si_m_endpos = next_match_eos_pos;
cur_si->si_h_endpos = next_match_eos_pos;
- cur_si->si_ends = TRUE;
+ cur_si->si_ends = true;
cur_si->si_end_idx = 0;
cur_si->si_flags = HL_MATCH;
cur_si->si_seqnr = next_seqnr++;
@@ -2404,7 +2396,7 @@ static void check_keepend(void)
* won't do anything. If there is no "extend" item "i" will be
* "keepend_level" and all "keepend" items will work normally.
*/
- for (i = current_state.ga_len - 1; i > keepend_level; --i) {
+ for (i = current_state.ga_len - 1; i > keepend_level; i--) {
if (CUR_STATE(i).si_flags & HL_EXTEND) {
break;
}
@@ -2414,13 +2406,13 @@ static void check_keepend(void)
maxpos.col = 0;
maxpos_h.lnum = 0;
maxpos_h.col = 0;
- for (; i < current_state.ga_len; ++i) {
+ for (; i < current_state.ga_len; i++) {
sip = &CUR_STATE(i);
if (maxpos.lnum != 0) {
limit_pos_zero(&sip->si_m_endpos, &maxpos);
limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
limit_pos_zero(&sip->si_eoe_pos, &maxpos);
- sip->si_ends = TRUE;
+ sip->si_ends = true;
}
if (sip->si_ends && (sip->si_flags & HL_KEEPEND)) {
if (maxpos.lnum == 0
@@ -2480,12 +2472,12 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
// No end pattern matched.
if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE) {
// a "oneline" never continues in the next line
- sip->si_ends = TRUE;
+ sip->si_ends = true;
sip->si_m_endpos.lnum = current_lnum;
sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
} else {
// continues in the next line
- sip->si_ends = FALSE;
+ sip->si_ends = false;
sip->si_m_endpos.lnum = 0;
}
sip->si_h_endpos = sip->si_m_endpos;
@@ -2494,7 +2486,7 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
sip->si_m_endpos = endpos;
sip->si_h_endpos = hl_endpos;
sip->si_eoe_pos = end_endpos;
- sip->si_ends = TRUE;
+ sip->si_ends = true;
sip->si_end_idx = end_idx;
}
}
@@ -2506,7 +2498,7 @@ static void update_si_end(stateitem_T *sip, int startcol, bool force)
static void push_current_state(int idx)
{
stateitem_T *p = GA_APPEND_VIA_PTR(stateitem_T, &current_state);
- memset(p, 0, sizeof(*p));
+ CLEAR_POINTER(p);
p->si_idx = idx;
}
@@ -2517,7 +2509,7 @@ static void pop_current_state(void)
{
if (!GA_EMPTY(&current_state)) {
unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
- --current_state.ga_len;
+ current_state.ga_len--;
}
// after the end of a pattern, try matching a keyword or pattern
next_match_idx = -1;
@@ -2582,7 +2574,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
if (spp->sp_type != SPTYPE_START) {
break;
}
- ++idx;
+ idx++;
}
/*
@@ -2590,7 +2582,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
*/
if (spp->sp_type == SPTYPE_SKIP) {
spp_skip = spp;
- ++idx;
+ idx++;
} else {
spp_skip = NULL;
}
@@ -2611,7 +2603,7 @@ static void find_endpos(int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_
* Find end pattern that matches first after "matchcol".
*/
best_idx = -1;
- for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx) {
+ for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; idx++) {
int lc_col = matchcol;
spp = &(SYN_ITEMS(syn_block)[idx]);
@@ -2876,17 +2868,15 @@ static void syn_add_start_off(lpos_T *result, regmmatch_T *regmatch, synpat_T *s
result->col = col;
}
-/*
- * Get current line in syntax buffer.
- */
-static char_u *syn_getcurline(void)
+/// Get current line in syntax buffer.
+static char *syn_getcurline(void)
{
- return ml_get_buf(syn_buf, current_lnum, false);
+ return (char *)ml_get_buf(syn_buf, current_lnum, false);
}
/*
* Call vim_regexec() to find a match with "rmp" in "syn_buf".
- * Returns TRUE when there is a match.
+ * Returns true when there is a match.
*/
static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st)
{
@@ -2914,9 +2904,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
if (profile_cmp(pt, st->slowest) < 0) {
st->slowest = pt;
}
- ++st->count;
+ st->count++;
if (r > 0) {
- ++st->match;
+ st->match++;
}
}
if (timed_out && !syn_win->w_s->b_syn_slow) {
@@ -2927,9 +2917,9 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T
if (r > 0) {
rmp->startpos[0].lnum += lnum;
rmp->endpos[0].lnum += lnum;
- return TRUE;
+ return true;
}
- return FALSE;
+ return false;
}
/// Check one position in a line for a matching keyword.
@@ -3018,12 +3008,12 @@ static void syn_cmd_conceal(exarg_T *eap, int syncing)
char_u *arg = (char_u *)eap->arg;
char_u *next;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd((char *)arg);
if (eap->skip) {
return;
}
- next = skiptowhite(arg);
+ next = (char_u *)skiptowhite((char *)arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_conceal) {
msg("syntax conceal on");
@@ -3047,12 +3037,12 @@ static void syn_cmd_case(exarg_T *eap, int syncing)
char_u *arg = (char_u *)eap->arg;
char_u *next;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd((char *)arg);
if (eap->skip) {
return;
}
- next = skiptowhite(arg);
+ next = (char_u *)skiptowhite((char *)arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_ic) {
msg("syntax case ignore");
@@ -3074,7 +3064,7 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
char_u *arg = (char_u *)eap->arg;
char_u *arg_end;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd((char *)arg);
if (eap->skip) {
return;
}
@@ -3091,7 +3081,7 @@ static void syn_cmd_foldlevel(exarg_T *eap, int syncing)
return;
}
- arg_end = skiptowhite(arg);
+ arg_end = (char_u *)skiptowhite((char *)arg);
if (STRNICMP(arg, "start", 5) == 0 && arg_end - arg == 5) {
curwin->w_s->b_syn_foldlevel = SYNFLD_START;
} else if (STRNICMP(arg, "minimum", 7) == 0 && arg_end - arg == 7) {
@@ -3115,12 +3105,12 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
char_u *arg = (char_u *)eap->arg;
char_u *next;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd((char *)arg);
if (eap->skip) {
return;
}
- next = skiptowhite(arg);
+ next = (char_u *)skiptowhite((char *)arg);
if (*arg == NUL) {
if (curwin->w_s->b_syn_spell == SYNSPL_TOP) {
msg("syntax spell toplevel");
@@ -3141,7 +3131,7 @@ static void syn_cmd_spell(exarg_T *eap, int syncing)
}
// assume spell checking changed, force a redraw
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/// Handle ":syntax iskeyword" command.
@@ -3160,7 +3150,7 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
msg_puts("\n");
if (curwin->w_s->b_syn_isk != empty_option) {
msg_puts("syntax iskeyword ");
- msg_outtrans((char *)curwin->w_s->b_syn_isk);
+ msg_outtrans(curwin->w_s->b_syn_isk);
} else {
msg_outtrans(_("syntax iskeyword not set"));
}
@@ -3170,18 +3160,18 @@ static void syn_cmd_iskeyword(exarg_T *eap, int syncing)
clear_string_option(&curwin->w_s->b_syn_isk);
} else {
memmove(save_chartab, curbuf->b_chartab, (size_t)32);
- save_isk = curbuf->b_p_isk;
- curbuf->b_p_isk = vim_strsave(arg);
+ save_isk = (char_u *)curbuf->b_p_isk;
+ curbuf->b_p_isk = (char *)vim_strsave(arg);
buf_init_chartab(curbuf, false);
memmove(curwin->w_s->b_syn_chartab, curbuf->b_chartab, (size_t)32);
memmove(curbuf->b_chartab, save_chartab, (size_t)32);
clear_string_option(&curwin->w_s->b_syn_isk);
curwin->w_s->b_syn_isk = curbuf->b_p_isk;
- curbuf->b_p_isk = save_isk;
+ curbuf->b_p_isk = (char *)save_isk;
}
}
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
/*
@@ -3280,7 +3270,7 @@ static void syn_remove_pattern(synblock_T *block, int idx)
spp = &(SYN_ITEMS(block)[idx]);
if (spp->sp_flags & HL_FOLD) {
- --block->b_syn_folditems;
+ block->b_syn_folditems--;
}
syn_clear_pattern(block, idx);
memmove(spp, spp + 1, sizeof(synpat_T) * (size_t)(block->b_syn_patterns.ga_len - idx - 1));
@@ -3322,7 +3312,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
char_u *arg_end;
int id;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd((char *)arg);
if (eap->skip) {
return;
}
@@ -3355,7 +3345,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
* Clear the group IDs that are in the argument.
*/
while (!ends_excmd(*arg)) {
- arg_end = skiptowhite(arg);
+ arg_end = (char_u *)skiptowhite((char *)arg);
if (*arg == '@') {
id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
if (id == 0) {
@@ -3381,7 +3371,7 @@ static void syn_cmd_clear(exarg_T *eap, int syncing)
arg = (char_u *)skipwhite((char *)arg_end);
}
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
@@ -3469,13 +3459,13 @@ void syn_maybe_enable(void)
/// Handle ":syntax [list]" command: list current syntax words.
///
-/// @param syncing when TRUE: list syncing items
+/// @param syncing when true: list syncing items
static void syn_cmd_list(exarg_T *eap, int syncing)
{
char_u *arg = (char_u *)eap->arg;
char_u *arg_end;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd((char *)arg);
if (eap->skip) {
return;
}
@@ -3524,7 +3514,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
for (int id = 1; id <= highlight_num_groups() && !got_int; id++) {
syn_list_one(id, syncing, false);
}
- for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id) {
+ for (int id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; id++) {
syn_list_cluster(id);
}
} else {
@@ -3532,7 +3522,7 @@ static void syn_cmd_list(exarg_T *eap, int syncing)
* List the group IDs and syntax clusters that are in the argument.
*/
while (!ends_excmd(*arg) && !got_int) {
- arg_end = skiptowhite(arg);
+ arg_end = (char_u *)skiptowhite((char *)arg);
if (*arg == '@') {
int id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
if (id == 0) {
@@ -3653,7 +3643,7 @@ static void syn_list_one(const int id, const bool syncing, const bool link_only)
&& SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END) {
put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
}
- --idx;
+ idx--;
msg_putchar(' ');
}
syn_list_flags(namelist1, spp->sp_flags, attr);
@@ -3700,7 +3690,7 @@ static void syn_list_flags(struct name_list *nlist, int flags, int attr)
{
int i;
- for (i = 0; nlist[i].flag != 0; ++i) {
+ for (i = 0; nlist[i].flag != 0; i++) {
if (flags & nlist[i].flag) {
msg_puts_attr(nlist[i].name, attr);
msg_putchar(' ');
@@ -3923,11 +3913,11 @@ static void syn_clear_keyword(int id, hashtab_T *ht)
hash_lock(ht);
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ for (hi = ht->ht_array; todo > 0; hi++) {
if (HASHITEM_EMPTY(hi)) {
continue;
}
- --todo;
+ todo--;
kp_prev = NULL;
for (kp = HI2KE(hi); kp != NULL;) {
if (kp->k_syn.id == id) {
@@ -3965,9 +3955,9 @@ static void clear_keywtab(hashtab_T *ht)
keyentry_T *kp_next;
todo = (int)ht->ht_used;
- for (hi = ht->ht_array; todo > 0; ++hi) {
+ for (hi = ht->ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) {
- --todo;
+ todo--;
for (kp = HI2KE(hi); kp != NULL; kp = kp_next) {
kp_next = kp->ke_next;
xfree(kp->next_list);
@@ -4004,7 +3994,7 @@ static void add_keyword(char_u *const name, const int id, const int flags,
kp->k_char = conceal_char;
kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
if (cont_in_list != NULL) {
- curwin->w_s->b_syn_containedin = TRUE;
+ curwin->w_s->b_syn_containedin = true;
}
kp->next_list = copy_id_list(next_list);
@@ -4037,17 +4027,13 @@ static void add_keyword(char_u *const name, const int id, const int flags,
///
/// @return a pointer to the first argument.
/// Return NULL if the end of the command was found instead of further args.
-static char_u *get_group_name(char_u *arg, char_u **name_end)
+static char *get_group_name(char *arg, char **name_end)
{
- char_u *rest;
-
*name_end = skiptowhite(arg);
- rest = (char_u *)skipwhite((char *)(*name_end));
+ char *rest = skipwhite(*name_end);
- /*
- * Check if there are enough arguments. The first argument may be a
- * pattern, where '|' is allowed, so only check for NUL.
- */
+ // Check if there are enough arguments. The first argument may be a
+ // pattern, where '|' is allowed, so only check for NUL.
if (ends_excmd(*arg) || *rest == NUL) {
return NULL;
}
@@ -4061,11 +4047,11 @@ static char_u *get_group_name(char_u *arg, char_u **name_end)
///
/// @param arg next argument to be checked
/// @param opt various things
-/// @param skip TRUE if skipping over command
+/// @param skip true if skipping over command
///
/// @return a pointer to the next argument (which isn't an option).
/// Return NULL for any error;
-static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_char, int skip)
+static char *get_syn_options(char *arg, syn_opt_arg_T *opt, int *conceal_char, int skip)
{
char_u *gname_start, *gname;
int syn_id;
@@ -4118,8 +4104,8 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
for (fidx = ARRAY_SIZE(flagtab); --fidx >= 0;) {
p = flagtab[fidx].name;
int i;
- for (i = 0, len = 0; p[i] != NUL; i += 2, ++len) {
- if (arg[len] != (char_u)p[i] && arg[len] != (char_u)p[i + 1]) {
+ for (i = 0, len = 0; p[i] != NUL; i += 2, len++) {
+ if (arg[len] != p[i] && arg[len] != p[i + 1]) {
break;
}
}
@@ -4159,16 +4145,16 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
}
} else if (flagtab[fidx].argtype == 11 && arg[5] == '=') {
// cchar=?
- *conceal_char = utf_ptr2char((char *)arg + 6);
- arg += utfc_ptr2len((char *)arg + 6) - 1;
+ *conceal_char = utf_ptr2char(arg + 6);
+ arg += utfc_ptr2len(arg + 6) - 1;
if (!vim_isprintc_strict(*conceal_char)) {
emsg(_("E844: invalid cchar value"));
return NULL;
}
- arg = (char_u *)skipwhite((char *)arg + 7);
+ arg = skipwhite(arg + 7);
} else {
opt->flags |= flagtab[fidx].flags;
- arg = (char_u *)skipwhite((char *)arg + len);
+ arg = skipwhite(arg + len);
if (flagtab[fidx].flags == HL_SYNC_HERE
|| flagtab[fidx].flags == HL_SYNC_THERE) {
@@ -4176,12 +4162,12 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
emsg(_("E393: group[t]here not accepted here"));
return NULL;
}
- gname_start = arg;
+ gname_start = (char_u *)arg;
arg = skiptowhite(arg);
- if (gname_start == arg) {
+ if (gname_start == (char_u *)arg) {
return NULL;
}
- gname = vim_strnsave(gname_start, (size_t)(arg - gname_start));
+ gname = vim_strnsave(gname_start, (size_t)((char_u *)arg - gname_start));
if (STRCMP(gname, "NONE") == 0) {
*opt->sync_idx = NONE_IDX;
} else {
@@ -4202,7 +4188,7 @@ static char_u *get_syn_options(char_u *arg, syn_opt_arg_T *opt, int *conceal_cha
}
xfree(gname);
- arg = (char_u *)skipwhite((char *)arg);
+ arg = skipwhite(arg);
} else if (flagtab[fidx].flags == HL_FOLD
&& foldmethodIsSyntax(curwin)) {
// Need to update folds later.
@@ -4242,33 +4228,33 @@ static void syn_incl_toplevel(int id, int *flagsp)
*/
static void syn_cmd_include(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
+ char *arg = eap->arg;
int sgl_id = 1;
- char_u *group_name_end;
- char_u *rest;
+ char *group_name_end;
+ char *rest;
char *errormsg = NULL;
int prev_toplvl_grp;
int prev_syn_inc_tag;
bool source = false;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
if (arg[0] == '@') {
- ++arg;
+ arg++;
rest = get_group_name(arg, &group_name_end);
if (rest == NULL) {
emsg(_("E397: Filename required"));
return;
}
- sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
+ sgl_id = syn_check_cluster((char_u *)arg, (int)(group_name_end - arg));
if (sgl_id == 0) {
return;
}
// separate_nextcmd() and expand_filename() depend on this
- eap->arg = (char *)rest;
+ eap->arg = rest;
}
/*
@@ -4316,13 +4302,13 @@ static void syn_cmd_include(exarg_T *eap, int syncing)
*/
static void syn_cmd_keyword(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
+ char *arg = eap->arg;
+ char *group_name_end;
int syn_id;
- char_u *rest;
- char_u *keyword_copy = NULL;
- char_u *p;
- char_u *kw;
+ char *rest;
+ char *keyword_copy = NULL;
+ char *p;
+ char *kw;
syn_opt_arg_T syn_opt_arg;
int cnt;
int conceal_char = NUL;
@@ -4333,7 +4319,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
if (eap->skip) {
syn_id = -1;
} else {
- syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg));
+ syn_id = syn_check_group(arg, (size_t)(group_name_end - arg));
}
if (syn_id != 0) {
// Allocate a buffer, for removing backslashes in the keyword.
@@ -4352,7 +4338,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
// 1: collect the options and copy the keywords to keyword_copy.
cnt = 0;
p = keyword_copy;
- for (; rest != NULL && !ends_excmd(*rest); rest = (char_u *)skipwhite((char *)rest)) {
+ for (; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest)) {
rest = get_syn_options(rest, &syn_opt_arg, &conceal_char, eap->skip);
if (rest == NULL || ends_excmd(*rest)) {
break;
@@ -4374,11 +4360,11 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
// 2: Add an entry for each keyword.
for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1) {
- for (p = (char_u *)vim_strchr((char *)kw, '[');;) {
+ for (p = vim_strchr(kw, '[');;) {
if (p != NULL) {
*p = NUL;
}
- add_keyword(kw, syn_id, syn_opt_arg.flags,
+ add_keyword((char_u *)kw, syn_id, syn_opt_arg.flags,
syn_opt_arg.cont_in_list,
syn_opt_arg.next_list, conceal_char);
if (p == NULL) {
@@ -4397,7 +4383,7 @@ static void syn_cmd_keyword(exarg_T *eap, int syncing)
kw = p + 1;
break; // skip over the "]"
}
- const int l = utfc_ptr2len((char *)p + 1);
+ const int l = utfc_ptr2len(p + 1);
memmove(p, p + 1, (size_t)l);
p += l;
@@ -4413,12 +4399,12 @@ error:
}
if (rest != NULL) {
- eap->nextcmd = (char *)check_nextcmd(rest);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)rest);
} else {
semsg(_(e_invarg2), arg);
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
@@ -4426,12 +4412,11 @@ error:
///
/// Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
///
-/// @param syncing TRUE for ":syntax sync match .. "
+/// @param syncing true for ":syntax sync match .. "
static void syn_cmd_match(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
- char_u *rest;
+ char *arg = eap->arg;
+ char *group_name_end;
synpat_T item; // the item found in the line
int syn_id;
syn_opt_arg_T syn_opt_arg;
@@ -4439,7 +4424,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
int conceal_char = NUL;
// Isolate the group name, check for validity
- rest = get_group_name(arg, &group_name_end);
+ char *rest = get_group_name(arg, &group_name_end);
// Get options before the pattern
syn_opt_arg.flags = 0;
@@ -4453,8 +4438,8 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
// get the pattern.
init_syn_patterns();
- memset(&item, 0, sizeof(item));
- rest = get_syn_pattern(rest, &item);
+ CLEAR_FIELD(item);
+ rest = get_syn_pattern((char_u *)rest, &item);
if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL)) {
syn_opt_arg.flags |= HL_HAS_EOL;
}
@@ -4466,11 +4451,11 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
/*
* Check for trailing command and illegal trailing arguments.
*/
- eap->nextcmd = (char *)check_nextcmd(rest);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)rest);
if (!ends_excmd(*rest) || eap->skip) {
rest = NULL;
} else {
- if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) {
+ if ((syn_id = syn_check_group(arg, (size_t)(group_name_end - arg))) != 0) {
syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
/*
* Store the pattern in the syn_items list
@@ -4488,7 +4473,7 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
spp->sp_syn.cont_in_list = syn_opt_arg.cont_in_list;
spp->sp_cchar = conceal_char;
if (syn_opt_arg.cont_in_list != NULL) {
- curwin->w_s->b_syn_containedin = TRUE;
+ curwin->w_s->b_syn_containedin = true;
}
spp->sp_next_list = syn_opt_arg.next_list;
@@ -4497,10 +4482,10 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
curwin->w_s->b_syn_sync_flags |= SF_MATCH;
}
if (syn_opt_arg.flags & HL_FOLD) {
- ++curwin->w_s->b_syn_folditems;
+ curwin->w_s->b_syn_folditems++;
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
return; // don't free the progs and patterns now
}
@@ -4524,15 +4509,15 @@ static void syn_cmd_match(exarg_T *eap, int syncing)
/// Handle ":syntax region {group-name} [matchgroup={group-name}]
/// start {start} .. [skip {skip}] end {end} .. [{options}]".
///
-/// @param syncing TRUE for ":syntax sync region .."
+/// @param syncing true for ":syntax sync region .."
static void syn_cmd_region(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
- char_u *rest; // next arg, NULL on error
- char_u *key_end;
- char_u *key = NULL;
- char_u *p;
+ char *arg = eap->arg;
+ char *group_name_end;
+ char *rest; // next arg, NULL on error
+ char *key_end;
+ char *key = NULL;
+ char *p;
int item;
#define ITEM_START 0
#define ITEM_SKIP 1
@@ -4583,10 +4568,10 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
// must be a pattern or matchgroup then
key_end = rest;
while (*key_end && !ascii_iswhite(*key_end) && *key_end != '=') {
- ++key_end;
+ key_end++;
}
xfree(key);
- key = vim_strnsave_up(rest, (size_t)(key_end - rest));
+ key = (char *)vim_strnsave_up((char_u *)rest, (size_t)(key_end - rest));
if (STRCMP(key, "MATCHGROUP") == 0) {
item = ITEM_MATCHGROUP;
} else if (STRCMP(key, "START") == 0) {
@@ -4602,13 +4587,13 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
} else {
break;
}
- rest = (char_u *)skipwhite((char *)key_end);
+ rest = skipwhite(key_end);
if (*rest != '=') {
rest = NULL;
semsg(_("E398: Missing '=': %s"), arg);
break;
}
- rest = (char_u *)skipwhite((char *)rest + 1);
+ rest = skipwhite(rest + 1);
if (*rest == NUL) {
not_enough = true;
break;
@@ -4619,13 +4604,13 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip) {
matchgroup_id = 0;
} else {
- matchgroup_id = syn_check_group((char *)rest, (size_t)(p - rest));
+ matchgroup_id = syn_check_group(rest, (size_t)(p - rest));
if (matchgroup_id == 0) {
illegal = true;
break;
}
}
- rest = (char_u *)skipwhite((char *)p);
+ rest = skipwhite(p);
} else {
/*
* Allocate room for a syn_pattern, and link it in the list of
@@ -4646,7 +4631,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
assert(item == ITEM_SKIP || item == ITEM_END);
reg_do_extmatch = REX_USE;
}
- rest = get_syn_pattern(rest, ppp->pp_synp);
+ rest = get_syn_pattern((char_u *)rest, ppp->pp_synp);
reg_do_extmatch = 0;
if (item == ITEM_END && vim_regcomp_had_eol()
&& !(syn_opt_arg.flags & HL_EXCLUDENL)) {
@@ -4673,18 +4658,18 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
* Check for trailing garbage or command.
* If OK, add the item.
*/
- eap->nextcmd = (char *)check_nextcmd(rest);
+ eap->nextcmd = (char *)check_nextcmd((char_u *)rest);
if (!ends_excmd(*rest) || eap->skip) {
rest = NULL;
} else {
ga_grow(&(curwin->w_s->b_syn_patterns), pat_count);
- if ((syn_id = syn_check_group((char *)arg, (size_t)(group_name_end - arg))) != 0) {
+ if ((syn_id = syn_check_group(arg, (size_t)(group_name_end - arg))) != 0) {
syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
/*
* Store the start/skip/end in the syn_items list
*/
int idx = curwin->w_s->b_syn_patterns.ga_len;
- for (item = ITEM_START; item <= ITEM_END; ++item) {
+ for (item = ITEM_START; item <= ITEM_END; item++) {
for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next) {
SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
@@ -4703,20 +4688,20 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
syn_opt_arg.cont_in_list;
if (syn_opt_arg.cont_in_list != NULL) {
- curwin->w_s->b_syn_containedin = TRUE;
+ curwin->w_s->b_syn_containedin = true;
}
SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
syn_opt_arg.next_list;
}
- ++curwin->w_s->b_syn_patterns.ga_len;
- ++idx;
+ curwin->w_s->b_syn_patterns.ga_len++;
+ idx++;
if (syn_opt_arg.flags & HL_FOLD) {
- ++curwin->w_s->b_syn_folditems;
+ curwin->w_s->b_syn_folditems++;
}
}
}
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
success = true; // don't free the progs and patterns now
}
@@ -4726,7 +4711,7 @@ static void syn_cmd_region(exarg_T *eap, int syncing)
/*
* Free the allocated memory.
*/
- for (item = ITEM_START; item <= ITEM_END; ++item) {
+ for (item = ITEM_START; item <= ITEM_END; item++) {
for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next) {
if (!success && ppp->pp_synp != NULL) {
vim_regfree(ppp->pp_synp->sp_prog);
@@ -4945,7 +4930,7 @@ static int syn_add_cluster(char_u *name)
syn_cluster_T *scp = GA_APPEND_VIA_PTR(syn_cluster_T,
&curwin->w_s->b_syn_clusters);
- memset(scp, 0, sizeof(*scp));
+ CLEAR_POINTER(scp);
scp->scl_name = name;
scp->scl_name_u = vim_strsave_up(name);
scp->scl_list = NULL;
@@ -4966,14 +4951,14 @@ static int syn_add_cluster(char_u *name)
*/
static void syn_cmd_cluster(exarg_T *eap, int syncing)
{
- char_u *arg = (char_u *)eap->arg;
- char_u *group_name_end;
- char_u *rest;
+ char *arg = eap->arg;
+ char *group_name_end;
+ char *rest;
bool got_clstr = false;
int opt_len;
int list_op;
- eap->nextcmd = (char *)find_nextcmd(arg);
+ eap->nextcmd = find_nextcmd(arg);
if (eap->skip) {
return;
}
@@ -4981,7 +4966,7 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing)
rest = get_group_name(arg, &group_name_end);
if (rest != NULL) {
- int scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
+ int scl_id = syn_check_cluster((char_u *)arg, (int)(group_name_end - arg));
if (scl_id == 0) {
return;
}
@@ -5019,7 +5004,7 @@ static void syn_cmd_cluster(exarg_T *eap, int syncing)
}
if (got_clstr) {
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all.
}
}
@@ -5041,14 +5026,13 @@ static void init_syn_patterns(void)
ga_set_growsize(&curwin->w_s->b_syn_patterns, 10);
}
-/*
- * Get one pattern for a ":syntax match" or ":syntax region" command.
- * Stores the pattern and program in a synpat_T.
- * Returns a pointer to the next argument, or NULL in case of an error.
- */
-static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
+/// Get one pattern for a ":syntax match" or ":syntax region" command.
+/// Stores the pattern and program in a synpat_T.
+///
+/// @return a pointer to the next argument, or NULL in case of an error.
+static char *get_syn_pattern(char_u *arg, synpat_T *ci)
{
- char_u *end;
+ char *end;
int *p;
int idx;
char *cpo_save;
@@ -5058,17 +5042,17 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
return NULL;
}
- end = skip_regexp(arg + 1, *arg, TRUE, NULL);
- if (*end != *arg) { // end delimiter not found
+ end = skip_regexp((char *)arg + 1, *arg, true, NULL);
+ if (*end != (char)(*arg)) { // end delimiter not found
semsg(_("E401: Pattern delimiter not found: %s"), arg);
return NULL;
}
// store the pattern and compiled regexp program
- ci->sp_pattern = vim_strnsave(arg + 1, (size_t)(end - arg) - 1);
+ ci->sp_pattern = vim_strnsave(arg + 1, (size_t)(end - (char *)arg) - 1);
// Make 'cpoptions' empty, to avoid the 'l' flag
cpo_save = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
ci->sp_prog = vim_regcomp((char *)ci->sp_pattern, RE_MAGIC);
p_cpo = cpo_save;
@@ -5081,7 +5065,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
/*
* Check for a match, highlight or region offset.
*/
- ++end;
+ end++;
do {
for (idx = SPO_COUNT; --idx >= 0;) {
if (STRNCMP(end, spo_name_tab[idx], 3) == 0) {
@@ -5106,7 +5090,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
ci->sp_off_flags |= (int16_t)(1 << idx);
if (idx == SPO_LC_OFF) { // lc=99
end += 3;
- *p = getdigits_int((char **)&end, true, 0);
+ *p = getdigits_int(&end, true, 0);
// "lc=" offset automatically sets "ms=" offset
if (!(ci->sp_off_flags & (1 << SPO_MS_OFF))) {
@@ -5117,16 +5101,16 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
end += 4;
if (*end == '+') {
end++;
- *p = getdigits_int((char **)&end, true, 0); // positive offset
+ *p = getdigits_int(&end, true, 0); // positive offset
} else if (*end == '-') {
end++;
- *p = -getdigits_int((char **)&end, true, 0); // negative offset
+ *p = -getdigits_int(&end, true, 0); // negative offset
}
}
if (*end != ',') {
break;
}
- ++end;
+ end++;
}
}
} while (idx >= 0);
@@ -5135,7 +5119,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
semsg(_("E402: Garbage after pattern: %s"), arg);
return NULL;
}
- return (char_u *)skipwhite((char *)end);
+ return skipwhite(end);
}
/*
@@ -5144,7 +5128,7 @@ static char_u *get_syn_pattern(char_u *arg, synpat_T *ci)
static void syn_cmd_sync(exarg_T *eap, int syncing)
{
char_u *arg_start = (char_u *)eap->arg;
- char_u *arg_end;
+ char *arg_end;
char_u *key = NULL;
char_u *next_arg;
int illegal = false;
@@ -5152,26 +5136,26 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
char *cpo_save;
if (ends_excmd(*arg_start)) {
- syn_cmd_list(eap, TRUE);
+ syn_cmd_list(eap, true);
return;
}
while (!ends_excmd(*arg_start)) {
- arg_end = skiptowhite(arg_start);
- next_arg = (char_u *)skipwhite((char *)arg_end);
+ arg_end = skiptowhite((char *)arg_start);
+ next_arg = (char_u *)skipwhite(arg_end);
xfree(key);
- key = vim_strnsave_up(arg_start, (size_t)(arg_end - arg_start));
+ key = vim_strnsave_up(arg_start, (size_t)(arg_end - (char *)arg_start));
if (STRCMP(key, "CCOMMENT") == 0) {
if (!eap->skip) {
curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
}
if (!ends_excmd(*next_arg)) {
- arg_end = skiptowhite(next_arg);
+ arg_end = skiptowhite((char *)next_arg);
if (!eap->skip) {
curwin->w_s->b_syn_sync_id =
- (int16_t)syn_check_group((char *)next_arg, (size_t)(arg_end - next_arg));
+ (int16_t)syn_check_group((char *)next_arg, (size_t)(arg_end - (char *)next_arg));
}
- next_arg = (char_u *)skipwhite((char *)arg_end);
+ next_arg = (char_u *)skipwhite(arg_end);
} else if (!eap->skip) {
curwin->w_s->b_syn_sync_id = (int16_t)syn_name2id("Comment");
}
@@ -5180,17 +5164,17 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
|| STRNCMP(key, "MAXLINES", 8) == 0
|| STRNCMP(key, "LINEBREAKS", 10) == 0) {
if (key[4] == 'S') {
- arg_end = key + 6;
+ arg_end = (char *)key + 6;
} else if (key[0] == 'L') {
- arg_end = key + 11;
+ arg_end = (char *)key + 11;
} else {
- arg_end = key + 9;
+ arg_end = (char *)key + 9;
}
if (arg_end[-1] != '=' || !ascii_isdigit(*arg_end)) {
- illegal = TRUE;
+ illegal = true;
break;
}
- linenr_T n = getdigits_int32((char **)&arg_end, false, 0);
+ linenr_T n = getdigits_int32(&arg_end, false, 0);
if (!eap->skip) {
if (key[4] == 'B') {
curwin->w_s->b_syn_sync_linebreaks = n;
@@ -5212,26 +5196,26 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
}
if (curwin->w_s->b_syn_linecont_pat != NULL) {
emsg(_("E403: syntax sync: line continuations pattern specified twice"));
- finished = TRUE;
+ finished = true;
break;
}
- arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
- if (*arg_end != *next_arg) { // end delimiter not found
- illegal = TRUE;
+ arg_end = skip_regexp((char *)next_arg + 1, *next_arg, true, NULL);
+ if (*arg_end != (char)(*next_arg)) { // end delimiter not found
+ illegal = true;
break;
}
if (!eap->skip) {
// store the pattern and compiled regexp program
curwin->w_s->b_syn_linecont_pat =
- vim_strnsave(next_arg + 1, (size_t)(arg_end - next_arg) - 1);
+ (char *)vim_strnsave(next_arg + 1, (size_t)(arg_end - (char *)next_arg) - 1);
curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
// Make 'cpoptions' empty, to avoid the 'l' flag
cpo_save = p_cpo;
- p_cpo = "";
+ p_cpo = empty_option;
curwin->w_s->b_syn_linecont_prog =
- vim_regcomp((char *)curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
+ vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
p_cpo = cpo_save;
syn_clear_time(&curwin->w_s->b_syn_linecont_time);
@@ -5241,19 +5225,19 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
break;
}
}
- next_arg = (char_u *)skipwhite((char *)arg_end + 1);
+ next_arg = (char_u *)skipwhite(arg_end + 1);
} else {
eap->arg = (char *)next_arg;
if (STRCMP(key, "MATCH") == 0) {
- syn_cmd_match(eap, TRUE);
+ syn_cmd_match(eap, true);
} else if (STRCMP(key, "REGION") == 0) {
- syn_cmd_region(eap, TRUE);
+ syn_cmd_region(eap, true);
} else if (STRCMP(key, "CLEAR") == 0) {
- syn_cmd_clear(eap, TRUE);
+ syn_cmd_clear(eap, true);
} else {
- illegal = TRUE;
+ illegal = true;
}
- finished = TRUE;
+ finished = true;
break;
}
arg_start = next_arg;
@@ -5263,7 +5247,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
semsg(_("E404: Illegal arguments: %s"), arg_start);
} else if (!finished) {
eap->nextcmd = (char *)check_nextcmd(arg_start);
- redraw_curbuf_later(SOME_VALID);
+ redraw_curbuf_later(UPD_SOME_VALID);
syn_stack_free_all(curwin->w_s); // Need to recompute all syntax.
}
}
@@ -5277,7 +5261,7 @@ static void syn_cmd_sync(exarg_T *eap, int syncing)
/// @param list where to store the resulting list, if not NULL, the list is silently skipped!
///
/// @return FAIL for some error, OK for success.
-static int get_id_list(char_u **const arg, const int keylen, int16_t **const list, const bool skip)
+static int get_id_list(char **const arg, const int keylen, int16_t **const list, const bool skip)
{
char *p = NULL;
char *end;
@@ -5294,7 +5278,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
// grow when a regexp is used. In that case round 1 is done once again.
for (int round = 1; round <= 2; round++) {
// skip "contains"
- p = skipwhite((char *)(*arg) + keylen);
+ p = skipwhite(*arg + keylen);
if (*p != '=') {
semsg(_("E405: Missing equal sign: %s"), *arg);
break;
@@ -5362,7 +5346,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
break;
}
- regmatch.rm_ic = TRUE;
+ regmatch.rm_ic = true;
id = 0;
for (int i = highlight_num_groups(); --i >= 0;) {
if (vim_regexec(&regmatch, (char *)highlight_group_name(i), (colnr_T)0)) {
@@ -5401,7 +5385,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
retval[count] = (int16_t)id;
}
}
- ++count;
+ count++;
}
p = skipwhite(end);
if (*p != ',') {
@@ -5419,7 +5403,7 @@ static int get_id_list(char_u **const arg, const int keylen, int16_t **const lis
}
}
- *arg = (char_u *)p;
+ *arg = p;
if (failed || retval == NULL) {
xfree(retval);
return FAIL;
@@ -5470,26 +5454,26 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
static int depth = 0;
int r;
- // If ssp has a "containedin" list and "cur_si" is in it, return TRUE.
+ // If ssp has a "containedin" list and "cur_si" is in it, return true.
if (cur_si != NULL && ssp->cont_in_list != NULL
&& !(cur_si->si_flags & HL_MATCH)) {
// Ignore transparent items without a contains argument. Double check
// that we don't go back past the first one.
while ((cur_si->si_flags & HL_TRANS_CONT)
&& cur_si > (stateitem_T *)(current_state.ga_data)) {
- --cur_si;
+ cur_si--;
}
// cur_si->si_idx is -1 for keywords, these never contain anything.
if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
&(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags &
HL_CONTAINED)) {
- return TRUE;
+ return true;
}
}
if (list == NULL) {
- return FALSE;
+ return false;
}
/*
@@ -5500,33 +5484,31 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
return !contained;
}
- /*
- * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
- * contains list. We also require that "id" is at the same ":syn include"
- * level as the list.
- */
+ // If the first item is "ALLBUT", return true if "id" is NOT in the
+ // contains list. We also require that "id" is at the same ":syn include"
+ // level as the list.
item = *list;
if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER) {
if (item < SYNID_TOP) {
// ALL or ALLBUT: accept all groups in the same file
if (item - SYNID_ALLBUT != ssp->inc_tag) {
- return FALSE;
+ return false;
}
} else if (item < SYNID_CONTAINED) {
// TOP: accept all not-contained groups in the same file
if (item - SYNID_TOP != ssp->inc_tag || contained) {
- return FALSE;
+ return false;
}
} else {
// CONTAINED: accept all contained groups in the same file
if (item - SYNID_CONTAINED != ssp->inc_tag || !contained) {
- return FALSE;
+ return false;
}
}
item = *++list;
- retval = FALSE;
+ retval = false;
} else {
- retval = TRUE;
+ retval = true;
}
/*
@@ -5541,9 +5523,9 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in
// restrict recursiveness to 30 to avoid an endless loop for a
// cluster that includes itself (indirectly)
if (scl_list != NULL && depth < 30) {
- ++depth;
+ depth++;
r = in_id_list(NULL, scl_list, ssp, contained);
- --depth;
+ depth--;
if (r) {
return retval;
}
@@ -5614,7 +5596,7 @@ void ex_syntax(exarg_T *eap)
}
xfree(subcmd_name);
if (eap->skip) {
- --emsg_skip;
+ emsg_skip--;
}
}
@@ -5624,8 +5606,7 @@ void ex_ownsyntax(exarg_T *eap)
char_u *new_value;
if (curwin->w_s == &curwin->w_buffer->b_s) {
- curwin->w_s = xmalloc(sizeof(synblock_T));
- memset(curwin->w_s, 0, sizeof(synblock_T));
+ curwin->w_s = xcalloc(1, sizeof(synblock_T));
hash_init(&curwin->w_s->b_keywtab);
hash_init(&curwin->w_s->b_keywtab_ic);
// TODO: Keep the spell checking as it was. NOLINT(readability/todo)
@@ -5674,7 +5655,8 @@ static enum {
EXP_SUBCMD, // expand ":syn" sub-commands
EXP_CASE, // expand ":syn case" arguments
EXP_SPELL, // expand ":syn spell" arguments
- EXP_SYNC, // expand ":syn sync" arguments
+ EXP_SYNC, // expand ":syn sync" arguments
+ EXP_CLUSTER, // expand ":syn list @cluster" arguments
} expand_what;
/*
@@ -5711,10 +5693,10 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
// (part of) subcommand already typed
if (*arg != NUL) {
- const char *p = (const char *)skiptowhite((const char_u *)arg);
+ const char *p = (const char *)skiptowhite(arg);
if (*p != NUL) { // Past first word.
xp->xp_pattern = skipwhite(p);
- if (*skiptowhite((char_u *)xp->xp_pattern) != NUL) {
+ if (*skiptowhite(xp->xp_pattern) != NUL) {
xp->xp_context = EXPAND_NOTHING;
} else if (STRNICMP(arg, "case", p - arg) == 0) {
expand_what = EXP_CASE;
@@ -5722,10 +5704,16 @@ void set_context_in_syntax_cmd(expand_T *xp, const char *arg)
expand_what = EXP_SPELL;
} else if (STRNICMP(arg, "sync", p - arg) == 0) {
expand_what = EXP_SYNC;
+ } else if (STRNICMP(arg, "list", p - arg) == 0) {
+ p = skipwhite(p);
+ if (*p == '@') {
+ expand_what = EXP_CLUSTER;
+ } else {
+ xp->xp_context = EXPAND_HIGHLIGHT;
+ }
} else if (STRNICMP(arg, "keyword", p - arg) == 0
|| STRNICMP(arg, "region", p - arg) == 0
- || STRNICMP(arg, "match", p - arg) == 0
- || STRNICMP(arg, "list", p - arg) == 0) {
+ || STRNICMP(arg, "match", p - arg) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
} else {
xp->xp_context = EXPAND_NOTHING;
@@ -5759,6 +5747,14 @@ char *get_syntax_name(expand_T *xp, int idx)
"maxlines=", "minlines=", "region", NULL };
return sync_args[idx];
}
+ case EXP_CLUSTER:
+ if (idx < curwin->w_s->b_syn_clusters.ga_len) {
+ vim_snprintf(xp->xp_buf, EXPAND_BUF_LEN, "@%s",
+ SYN_CLSTR(curwin->w_s)[idx].scl_name);
+ return xp->xp_buf;
+ } else {
+ return NULL;
+ }
}
return NULL;
}
@@ -5831,7 +5827,7 @@ int syn_get_stack_item(int i)
{
if (i >= current_state.ga_len) {
// Need to invalidate the state, because we didn't properly finish it
- // for the last character, "keep_state" was TRUE.
+ // for the last character, "keep_state" was true.
invalidate_current_state();
current_col = MAXCOL;
return -1;
@@ -5926,7 +5922,7 @@ static void syntime_clear(void)
msg(_(msg_no_items));
return;
}
- for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) {
+ for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; idx++) {
spp = &(SYN_ITEMS(curwin->w_s)[idx]);
syn_clear_time(&spp->sp_time);
}
@@ -5975,7 +5971,7 @@ static void syntime_report(void)
proftime_T total_total = profile_zero();
int total_count = 0;
time_entry_T *p;
- for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx) {
+ for (int idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; idx++) {
synpat_T *spp = &(SYN_ITEMS(curwin->w_s)[idx]);
if (spp->sp_time.count > 0) {
p = GA_APPEND_VIA_PTR(time_entry_T, &ga);
@@ -6001,7 +5997,7 @@ static void syntime_report(void)
msg_puts_title(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
msg_puts("\n");
- for (int idx = 0; idx < ga.ga_len && !got_int; ++idx) {
+ for (int idx = 0; idx < ga.ga_len && !got_int; idx++) {
p = ((time_entry_T *)ga.ga_data) + idx;
msg_puts(profile_msg(p->total));
@@ -6032,7 +6028,7 @@ static void syntime_report(void)
if (len > (int)STRLEN(p->pattern)) {
len = (int)STRLEN(p->pattern);
}
- msg_outtrans_len(p->pattern, len);
+ msg_outtrans_len((char *)p->pattern, len);
msg_puts("\n");
}
ga_clear(&ga);
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 5b799be381..5270412382 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -13,17 +13,19 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/help.h"
#include "nvim/if_cscope.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
@@ -33,6 +35,7 @@
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
@@ -40,7 +43,7 @@
#include "nvim/path.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/tag.h"
@@ -54,6 +57,7 @@
typedef struct tag_pointers {
// filled in by parse_tag_line():
char_u *tagname; // start of tag name (skip "file:")
+ //
char_u *tagname_end; // char after tag name
char_u *fname; // first char of file name
char_u *fname_end; // char after file name
@@ -199,7 +203,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
prev_num_matches = num_matches;
- free_string_option(nofile_fname);
+ free_string_option((char *)nofile_fname);
nofile_fname = NULL;
clearpos(&saved_fmark.mark); // shutup gcc 4.0
@@ -212,7 +216,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
new_tag = true;
if (g_do_tagpreview != 0) {
tagstack_clear_entry(&ptag_entry);
- ptag_entry.tagname = vim_strsave(tag);
+ ptag_entry.tagname = (char *)vim_strsave(tag);
}
} else {
if (g_do_tagpreview != 0) {
@@ -236,7 +240,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
cur_fnum = ptag_entry.cur_fnum;
} else {
tagstack_clear_entry(&ptag_entry);
- ptag_entry.tagname = vim_strsave(tag);
+ ptag_entry.tagname = (char *)vim_strsave(tag);
}
} else {
/*
@@ -258,7 +262,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
}
// put the tag name in the tag stack
- tagstack[tagstackidx].tagname = vim_strsave(tag);
+ tagstack[tagstackidx].tagname = (char *)vim_strsave(tag);
curwin->w_tagstacklen = tagstacklen;
@@ -437,9 +441,9 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// When desired match not found yet, try to find it (and others).
if (use_tagstack) {
- name = tagstack[tagstackidx].tagname;
+ name = (char_u *)tagstack[tagstackidx].tagname;
} else if (g_do_tagpreview != 0) {
- name = ptag_entry.tagname;
+ name = (char_u *)ptag_entry.tagname;
} else {
name = tag;
}
@@ -465,7 +469,7 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
// when the argument starts with '/', use it as a regexp
if (!no_regexp && *name == '/') {
flags = TAG_REGEXP;
- ++name;
+ name++;
} else {
flags = TAG_NOIC;
}
@@ -480,8 +484,8 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
flags |= TAG_NO_TAGFUNC;
}
- if (find_tags(name, &new_num_matches, &new_matches, flags,
- max_num_matches, buf_ffname) == OK
+ if (find_tags((char *)name, &new_num_matches, &new_matches, flags,
+ max_num_matches, (char *)buf_ffname) == OK
&& new_num_matches < max_num_matches) {
max_num_matches = MAXCOL; // If less than max_num_matches
// found: all matches found.
@@ -584,9 +588,9 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
if (use_tfu && parse_match((char_u *)matches[cur_match], &tagp2) == OK
&& tagp2.user_data) {
XFREE_CLEAR(tagstack[tagstackidx].user_data);
- tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data,
- (size_t)(tagp2.user_data_end -
- tagp2.user_data));
+ tagstack[tagstackidx].user_data = (char *)vim_strnsave(tagp2.user_data,
+ (size_t)(tagp2.user_data_end -
+ tagp2.user_data));
}
tagstackidx++;
@@ -653,13 +657,13 @@ bool do_tag(char_u *tag, int type, int count, int forceit, int verbose)
|| cur_match < num_matches - 1))) {
error_cur_match = cur_match;
if (use_tagstack) {
- --tagstackidx;
+ tagstackidx--;
}
if (type == DT_PREV) {
- --cur_match;
+ cur_match--;
} else {
type = DT_NEXT;
- ++cur_match;
+ cur_match++;
}
continue;
}
@@ -734,7 +738,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
mt_names[matches[i][0] & MT_MASK]);
msg_puts((char *)IObuff);
if (tagp.tagkind != NULL) {
- msg_outtrans_len(tagp.tagkind,
+ msg_outtrans_len((char *)tagp.tagkind,
(int)(tagp.tagkind_end - tagp.tagkind));
}
msg_advance(13);
@@ -748,7 +752,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
// it and put "..." in the middle
p = tag_full_fname(&tagp);
if (p != NULL) {
- msg_outtrans_attr(p, HL_ATTR(HLF_D));
+ msg_outtrans_attr((char *)p, HL_ATTR(HLF_D));
XFREE_CLEAR(p);
}
if (msg_col > 0) {
@@ -790,7 +794,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
}
msg_advance(15);
}
- p = msg_outtrans_one(p, attr);
+ p = (char_u *)msg_outtrans_one((char *)p, attr);
if (*p == TAB) {
msg_puts_attr(" ", attr);
break;
@@ -848,7 +852,7 @@ static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char
msg_putchar(' ');
p++;
} else {
- p = msg_outtrans_one(p, 0);
+ p = (char_u *)msg_outtrans_one((char *)p, 0);
}
// don't display the "$/;\"" and "$?;\""
@@ -1033,7 +1037,7 @@ void do_tags(exarg_T *eap)
// Highlight title
msg_puts_title(_("\n # TO tag FROM line in file/text"));
- for (i = 0; i < tagstacklen; ++i) {
+ for (i = 0; i < tagstacklen; i++) {
if (tagstack[i].tagname != NULL) {
name = fm_getname(&(tagstack[i].fmark), 30);
if (name == NULL) { // file name not available
@@ -1048,7 +1052,7 @@ void do_tags(exarg_T *eap)
tagstack[i].tagname,
tagstack[i].fmark.mark.lnum);
msg_outtrans((char *)IObuff);
- msg_outtrans_attr(name, tagstack[i].fmark.fnum == curbuf->b_fnum
+ msg_outtrans_attr((char *)name, tagstack[i].fmark.fnum == curbuf->b_fnum
? HL_ATTR(HLF_D) : 0);
xfree(name);
}
@@ -1076,9 +1080,9 @@ static int tag_strnicmp(char_u *s1, char_u *s2, size_t len)
if (*s1 == NUL) {
break; // strings match until NUL
}
- ++s1;
- ++s2;
- --len;
+ s1++;
+ s2++;
+ len--;
}
return 0; // strings match
}
@@ -1181,7 +1185,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
flags & TAG_REGEXP ? "r": "");
save_pos = curwin->w_cursor;
- result = call_vim_function((char *)curbuf->b_p_tfu, 3, args, &rettv);
+ result = call_vim_function(curbuf->b_p_tfu, 3, args, &rettv);
curwin->w_cursor = save_pos; // restore the cursor position
d->dv_refcount--;
@@ -1320,7 +1324,7 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
// Add all matches because tagfunc should do filtering.
ga_grow(ga, 1);
- ((char_u **)(ga->ga_data))[ga->ga_len++] = mfp;
+ ((char **)(ga->ga_data))[ga->ga_len++] = (char *)mfp;
ntags++;
result = OK;
});
@@ -1362,8 +1366,8 @@ static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int fl
/// @param matchesp return: array of matches found
/// @param mincount MAXCOL: find all matches other: minimal number of matches */
/// @param buf_ffname name of buffer for priority
-int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mincount,
- char_u *buf_ffname)
+int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int mincount,
+ char *buf_ffname)
{
FILE *fp;
char_u *lbuf; // line buffer
@@ -1406,12 +1410,12 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
int cmplen;
int match; // matches
- int match_no_ic = 0; // matches with rm_ic == FALSE
+ int match_no_ic = 0; // matches with rm_ic == false
int match_re; // match with regexp
int matchoff = 0;
int save_emsg_off;
- char_u *mfp;
+ char *mfp;
garray_T ga_match[MT_COUNT]; // stores matches in sequence
hashtab_T ht_match[MT_COUNT]; // stores matches by key
hash_T hash = 0;
@@ -1438,7 +1442,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
int help_only = (flags & TAG_HELP);
int name_only = (flags & TAG_NAMES);
int noic = (flags & TAG_NOIC);
- int get_it_again = FALSE;
+ int get_it_again = false;
int use_cscope = (flags & TAG_CSCOPE);
int verbose = (flags & TAG_VERBOSE);
int use_tfu = ((flags & TAG_NO_TAGFUNC) == 0);
@@ -1456,17 +1460,17 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
p_ic = false;
break;
case TC_FOLLOWSCS:
- p_ic = ignorecase(pat);
+ p_ic = ignorecase((char_u *)pat);
break;
case TC_SMART:
- p_ic = ignorecase_opt(pat, true, true);
+ p_ic = ignorecase_opt((char_u *)pat, true, true);
break;
default:
abort();
}
help_save = curbuf->b_help;
- orgpat.pat = pat;
+ orgpat.pat = (char_u *)pat;
orgpat.regmatch.regprog = NULL;
vimconv.vc_type = CONV_NONE;
@@ -1476,7 +1480,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
lbuf = xmalloc((size_t)lbuf_size);
tag_fname = xmalloc(MAXPATHL + 1);
for (mtt = 0; mtt < MT_COUNT; mtt++) {
- ga_init(&ga_match[mtt], sizeof(char_u *), 100);
+ ga_init(&ga_match[mtt], sizeof(char *), 100);
hash_init(&ht_match[mtt]);
}
@@ -1500,8 +1504,8 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
if (orgpat.len > 3 && pat[orgpat.len - 3] == '@'
&& ASCII_ISALPHA(pat[orgpat.len - 2])
&& ASCII_ISALPHA(pat[orgpat.len - 1])) {
- saved_pat = vim_strnsave(pat, (size_t)orgpat.len - 3);
- help_lang_find = &pat[orgpat.len - 2];
+ saved_pat = vim_strnsave((char_u *)pat, (size_t)orgpat.len - 3);
+ help_lang_find = (char_u *)&pat[orgpat.len - 2];
orgpat.pat = saved_pat;
orgpat.len -= 3;
}
@@ -1511,7 +1515,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
}
save_emsg_off = emsg_off;
- emsg_off = TRUE; // don't want error for invalid RE here
+ emsg_off = true; // don't want error for invalid RE here
prepare_pats(&orgpat, has_re);
emsg_off = save_emsg_off;
if (has_re && orgpat.regmatch.regprog == NULL) {
@@ -1520,12 +1524,12 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
// This is only to avoid a compiler warning for using search_info
// uninitialised.
- memset(&search_info, 0, 1); // -V512
+ CLEAR_FIELD(search_info);
if (*curbuf->b_p_tfu != NUL && use_tfu && !tfu_in_use) {
tfu_in_use = true;
- retval = find_tagfunc_tags(pat, &ga_match[0], &match_count,
- flags, buf_ffname);
+ retval = find_tagfunc_tags((char_u *)pat, &ga_match[0], &match_count, flags,
+ (char_u *)buf_ffname);
tfu_in_use = false;
if (retval != NOTDONE) {
goto findtag_end;
@@ -1552,12 +1556,12 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
}
orgpat.regmatch.rm_ic = ((p_ic || !noic)
&& (findall || orgpat.headlen == 0 || !p_tbs));
- for (round = 1; round <= 2; ++round) {
+ for (round = 1; round <= 2; round++) {
linear = (orgpat.headlen == 0 || !p_tbs || round == 2);
// Try tag file names from tags option one by one.
for (first_file = true;
- use_cscope || get_tagfname(&tn, first_file, tag_fname) == OK;
+ use_cscope || get_tagfname(&tn, first_file, (char *)tag_fname) == OK;
first_file = false) {
// A file that doesn't exist is silently ignored. Only when not a
// single file is found, an error message is given (further on).
@@ -1598,7 +1602,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
help_pri = 0;
} else {
help_pri = 1;
- for (s = p_hlg; *s != NUL; ++s) {
+ for (s = p_hlg; *s != NUL; s++) {
if (STRNICMP(s, help_lang, 2) == 0) {
break;
}
@@ -1612,7 +1616,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
// unless found already.
help_pri++;
if (STRICMP(help_lang, "en") != 0) {
- ++help_pri;
+ help_pri++;
}
}
}
@@ -1701,7 +1705,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
eof = vim_fgets(lbuf, lbuf_size, fp);
}
// skip empty and blank lines
- while (!eof && vim_isblankline(lbuf)) {
+ while (!eof && vim_isblankline((char *)lbuf)) {
search_info.curr_offset = vim_ftell(fp);
eof = vim_fgets(lbuf, lbuf_size, fp);
}
@@ -1722,7 +1726,7 @@ int find_tags(char_u *pat, int *num_matches, char ***matchesp, int flags, int mi
eof = use_cscope
? cs_fgets(lbuf, lbuf_size)
: vim_fgets(lbuf, lbuf_size, fp);
- } while (!eof && vim_isblankline(lbuf));
+ } while (!eof && vim_isblankline((char *)lbuf));
if (eof) {
break; // end of file
@@ -1737,7 +1741,7 @@ line_read_in:
// Convert every line. Converting the pattern from 'enc' to
// the tags file encoding doesn't work, because characters are
// not recognized.
- conv_line = string_convert(&vimconv, lbuf, NULL);
+ conv_line = (char_u *)string_convert(&vimconv, (char *)lbuf, NULL);
if (conv_line != NULL) {
// Copy or swap lbuf and conv_line.
len = (int)STRLEN(conv_line) + 1;
@@ -1777,7 +1781,7 @@ line_read_in:
// encoding to 'encoding'.
for (p = lbuf + 20; *p > ' ' && *p < 127; p++) {}
*p = NUL;
- convert_setup(&vimconv, lbuf + 20, p_enc);
+ convert_setup(&vimconv, (char *)lbuf + 20, p_enc);
}
// Read the next line. Unrecognized flags are ignored.
@@ -1860,7 +1864,7 @@ parse_line:
// For "normal" tags: Do a quick check if the tag matches.
// This speeds up tag searching a lot!
if (orgpat.headlen) {
- memset(&tagp, 0, sizeof(tagp));
+ CLEAR_FIELD(tagp);
tagp.tagname = lbuf;
tagp.tagname_end = (char_u *)vim_strchr((char *)lbuf, TAB);
if (tagp.tagname_end == NULL) {
@@ -2006,7 +2010,7 @@ parse_line:
}
// if tag length does not match, don't try comparing
if (orgpat.len != cmplen) {
- match = FALSE;
+ match = false;
} else {
if (orgpat.regmatch.rm_ic) {
assert(cmplen >= 0);
@@ -2023,7 +2027,7 @@ parse_line:
/*
* Has a regexp: Also find tags matching regexp.
*/
- match_re = FALSE;
+ match_re = false;
if (!match && orgpat.regmatch.regprog != NULL) {
int cc;
@@ -2051,7 +2055,8 @@ parse_line:
mtt = MT_GL_OTH;
} else {
// Decide in which array to store this match.
- is_current = test_for_current(tagp.fname, tagp.fname_end, tag_fname,
+ is_current = test_for_current((char *)tagp.fname, (char *)tagp.fname_end,
+ (char *)tag_fname,
buf_ffname);
is_static = test_for_static(&tagp);
@@ -2088,9 +2093,9 @@ parse_line:
// The format is {tagname}@{lang}NUL{heuristic}NUL
*tagp.tagname_end = NUL;
len = (size_t)(tagp.tagname_end - tagp.tagname);
- mfp = xmalloc(sizeof(char_u) + len + 10 + ML_EXTRA + 1);
+ mfp = xmalloc(sizeof(char) + len + 10 + ML_EXTRA + 1);
- p = mfp;
+ p = (char_u *)mfp;
STRCPY(p, tagp.tagname);
p[len] = '@';
STRCPY(p + len + 1, help_lang);
@@ -2122,7 +2127,7 @@ parse_line:
get_it_again = false;
} else {
len = (size_t)(tagp.tagname_end - tagp.tagname);
- mfp = xmalloc(sizeof(char_u) + len + 1);
+ mfp = xmalloc(sizeof(char) + len + 1);
STRLCPY(mfp, tagp.tagname, len + 1);
// if wanted, re-read line to get long form too
@@ -2140,8 +2145,8 @@ parse_line:
// without Emacs tags: <mtt><tag_fname><0x02><lbuf><NUL>
// Here <mtt> is the "mtt" value plus 1 to avoid NUL.
len = tag_fname_len + STRLEN(lbuf) + 3;
- mfp = xmalloc(sizeof(char_u) + len + 1);
- p = mfp;
+ mfp = xmalloc(sizeof(char) + len + 1);
+ p = (char_u *)mfp;
p[0] = (char_u)(mtt + 1);
STRCPY(p + 1, tag_fname);
#ifdef BACKSLASH_IN_FILENAME
@@ -2166,15 +2171,14 @@ parse_line:
if (use_cscope) {
hash++;
} else {
- hash = hash_hash(mfp);
+ hash = hash_hash((char_u *)mfp);
}
hi = hash_lookup(&ht_match[mtt], (const char *)mfp,
STRLEN(mfp), hash);
if (HASHITEM_EMPTY(hi)) {
- hash_add_item(&ht_match[mtt], hi, mfp, hash);
+ hash_add_item(&ht_match[mtt], hi, (char_u *)mfp, hash);
ga_grow(&ga_match[mtt], 1);
- ((char_u **)(ga_match[mtt].ga_data))
- [ga_match[mtt].ga_len++] = mfp;
+ ((char **)(ga_match[mtt].ga_data))[ga_match[mtt].ga_len++] = mfp;
match_count++;
} else {
// duplicate tag, drop it
@@ -2234,7 +2238,7 @@ parse_line:
if (use_cscope) {
break;
}
- orgpat.regmatch.rm_ic = TRUE; // try another time while ignoring case
+ orgpat.regmatch.rm_ic = true; // try another time while ignoring case
}
if (!stop_searching) {
@@ -2258,29 +2262,29 @@ findtag_end:
}
if (match_count > 0) {
- matches = xmalloc((size_t)match_count * sizeof(char_u *));
+ matches = xmalloc((size_t)match_count * sizeof(char *));
} else {
matches = NULL;
}
match_count = 0;
for (mtt = 0; mtt < MT_COUNT; mtt++) {
for (i = 0; i < ga_match[mtt].ga_len; i++) {
- mfp = ((char_u **)(ga_match[mtt].ga_data))[i];
+ mfp = ((char **)(ga_match[mtt].ga_data))[i];
if (matches == NULL) {
xfree(mfp);
} else {
if (!name_only) {
// Change mtt back to zero-based.
- *mfp = (char_u)(*mfp - 1);
+ *mfp = (char)(*mfp - 1);
// change the TAG_SEP back to NUL
- for (p = mfp + 1; *p != NUL; p++) {
+ for (p = (char_u *)mfp + 1; *p != NUL; p++) {
if (*p == TAG_SEP) {
*p = NUL;
}
}
}
- matches[match_count++] = (char *)mfp;
+ matches[match_count++] = mfp;
}
}
@@ -2332,17 +2336,17 @@ void free_tag_stuff(void)
/// For help files, use "tags" file only.
///
/// @param tnp holds status info
-/// @param first TRUE when first file name is wanted
+/// @param first true when first file name is wanted
/// @param buf pointer to buffer of MAXPATHL chars
///
/// @return FAIL if no more tag file names, OK otherwise.
-int get_tagfname(tagname_T *tnp, int first, char_u *buf)
+int get_tagfname(tagname_T *tnp, int first, char *buf)
{
- char_u *fname = NULL;
- char_u *r_ptr;
+ char *fname = NULL;
+ char *r_ptr;
if (first) {
- memset(tnp, 0, sizeof(tagname_T));
+ CLEAR_POINTER(tnp);
}
if (curbuf->b_help) {
@@ -2353,7 +2357,7 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
*/
if (first) {
ga_clear_strings(&tag_fnames);
- ga_init(&tag_fnames, (int)sizeof(char_u *), 10);
+ ga_init(&tag_fnames, (int)sizeof(char *), 10);
do_in_runtimepath("doc/tags doc/tags-??", DIP_ALL,
found_tagfile_cb, NULL);
}
@@ -2364,22 +2368,21 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) {
return FAIL;
}
- ++tnp->tn_hf_idx;
+ tnp->tn_hf_idx++;
STRCPY(buf, p_hf);
STRCPY(path_tail((char *)buf), "tags");
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(buf);
#endif
- simplify_filename(buf);
+ simplify_filename((char_u *)buf);
for (int i = 0; i < tag_fnames.ga_len; i++) {
- if (STRCMP(buf, ((char_u **)(tag_fnames.ga_data))[i]) == 0) {
+ if (STRCMP(buf, ((char **)(tag_fnames.ga_data))[i]) == 0) {
return FAIL; // avoid duplicate file names
}
}
} else {
- STRLCPY(buf, ((char_u **)(tag_fnames.ga_data))[tnp->tn_hf_idx++],
- MAXPATHL);
+ STRLCPY(buf, ((char **)(tag_fnames.ga_data))[tnp->tn_hf_idx++], MAXPATHL);
}
return OK;
}
@@ -2387,25 +2390,24 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
if (first) {
// Init. We make a copy of 'tags', because autocommands may change
// the value without notifying us.
- tnp->tn_tags = vim_strsave((*curbuf->b_p_tags != NUL)
- ? curbuf->b_p_tags : p_tags);
- tnp->tn_np = tnp->tn_tags;
+ tnp->tn_tags = vim_strsave((*curbuf->b_p_tags != NUL) ? (char_u *)curbuf->b_p_tags : p_tags);
+ tnp->tn_np = (char *)tnp->tn_tags;
}
/*
* Loop until we have found a file name that can be used.
* There are two states:
- * tnp->tn_did_filefind_init == FALSE: setup for next part in 'tags'.
- * tnp->tn_did_filefind_init == TRUE: find next file in this part.
+ * tnp->tn_did_filefind_init == false: setup for next part in 'tags'.
+ * tnp->tn_did_filefind_init == true: find next file in this part.
*/
for (;;) {
if (tnp->tn_did_filefind_init) {
- fname = vim_findfile(tnp->tn_search_ctx);
+ fname = (char *)vim_findfile(tnp->tn_search_ctx);
if (fname != NULL) {
break;
}
- tnp->tn_did_filefind_init = FALSE;
+ tnp->tn_did_filefind_init = false;
} else {
char_u *filename = NULL;
@@ -2420,22 +2422,22 @@ int get_tagfname(tagname_T *tnp, int first, char_u *buf)
* Copy next file name into buf.
*/
buf[0] = NUL;
- (void)copy_option_part((char **)&tnp->tn_np, (char *)buf, MAXPATHL - 1, " ,");
+ (void)copy_option_part(&tnp->tn_np, buf, MAXPATHL - 1, " ,");
- r_ptr = vim_findfile_stopdir(buf);
+ r_ptr = (char *)vim_findfile_stopdir((char_u *)buf);
// move the filename one char forward and truncate the
// filepath with a NUL
- filename = (char_u *)path_tail((char *)buf);
+ filename = (char_u *)path_tail(buf);
STRMOVE(filename + 1, filename);
*filename++ = NUL;
- tnp->tn_search_ctx = vim_findfile_init(buf, filename,
- r_ptr, 100,
- FALSE, // don't free visited list
+ tnp->tn_search_ctx = vim_findfile_init((char_u *)buf, filename,
+ (char_u *)r_ptr, 100,
+ false, // don't free visited list
FINDFILE_FILE, // we search for a file
tnp->tn_search_ctx, true, (char_u *)curbuf->b_ffname);
if (tnp->tn_search_ctx != NULL) {
- tnp->tn_did_filefind_init = TRUE;
+ tnp->tn_did_filefind_init = true;
}
}
}
@@ -2459,7 +2461,7 @@ void tagname_free(tagname_T *tnp)
/// Parse one line from the tags file. Find start/end of tag name, start/end of
/// file name and start of search pattern.
///
-/// If is_etag is TRUE, tagp->fname and tagp->fname_end are not set.
+/// If is_etag is true, tagp->fname and tagp->fname_end are not set.
///
/// @param lbuf line to be parsed
///
@@ -2478,7 +2480,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
// Isolate file name, from first to second white space
if (*p != NUL) {
- ++p;
+ p++;
}
tagp->fname = p;
p = (char_u *)vim_strchr((char *)p, TAB);
@@ -2489,7 +2491,7 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
// find start of search command, after second white space
if (*p != NUL) {
- ++p;
+ p++;
}
if (*p == NUL) {
return FAIL;
@@ -2510,8 +2512,8 @@ static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp)
* Static tags produced by the new ctags program have the format:
* 'tag file /pattern/;"<Tab>file:' "
*
- * Return TRUE if it is a static tag and adjust *tagname to the real tag.
- * Return FALSE if it is not a static tag.
+ * Return true if it is a static tag and adjust *tagname to the real tag.
+ * Return false if it is not a static tag.
*/
static bool test_for_static(tagptrs_T *tagp)
{
@@ -2522,11 +2524,11 @@ static bool test_for_static(tagptrs_T *tagp)
while ((p = (char_u *)vim_strchr((char *)p, '\t')) != NULL) {
p++;
if (STRNCMP(p, "file:", 5) == 0) {
- return TRUE;
+ return true;
}
}
- return FALSE;
+ return false;
}
// Returns the length of a matching tag line.
@@ -2640,7 +2642,7 @@ static char_u *tag_full_fname(tagptrs_T *tagp)
///
/// @param lbuf_arg line from the tags file for this tag
/// @param forceit :ta with !
-/// @param keep_help keep help flag (FALSE for cscope)
+/// @param keep_help keep help flag (false for cscope)
///
/// @return OK for success, NOTAGFILE when file not found, FAIL otherwise.
static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
@@ -2720,7 +2722,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
goto erret;
}
- ++RedrawingDisabled;
+ RedrawingDisabled++;
if (l_g_do_tagpreview != 0) {
postponed_split = 0; // don't split again below
@@ -2732,7 +2734,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
* into a fullpath
*/
if (!curwin->w_p_pvw) {
- full_fname = (char_u *)FullName_save((char *)fname, FALSE);
+ full_fname = (char_u *)FullName_save((char *)fname, false);
fname = full_fname;
/*
@@ -2825,15 +2827,15 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
*/
str = pbuf;
if (pbuf[0] == '/' || pbuf[0] == '?') {
- str = skip_regexp(pbuf + 1, pbuf[0], FALSE, NULL) + 1;
+ str = (char_u *)skip_regexp((char *)pbuf + 1, pbuf[0], false, NULL) + 1;
}
if (str > pbuf_end - 1) { // search command with nothing following
save_p_ws = p_ws;
save_p_ic = p_ic;
save_p_scs = p_scs;
p_ws = true; // need 'wrapscan' for backward searches
- p_ic = FALSE; // don't ignore case now
- p_scs = FALSE;
+ p_ic = false; // don't ignore case now
+ p_scs = false;
save_lnum = curwin->w_cursor.lnum;
if (tagp.tagline > 0) {
// start search before line from "line:" field
@@ -2944,7 +2946,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help)
&& curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor();
- redraw_later(curwin, VALID);
+ redraw_later(curwin, UPD_VALID);
win_enter(curwin_save, true);
}
@@ -3011,27 +3013,25 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bo
return retval;
}
-/*
- * Check if we have a tag for the buffer with name "buf_ffname".
- * This is a bit slow, because of the full path compare in path_full_compare().
- * Return TRUE if tag for file "fname" if tag file "tag_fname" is for current
- * file.
- */
-static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname, char_u *buf_ffname)
+/// Check if we have a tag for the buffer with name "buf_ffname".
+/// This is a bit slow, because of the full path compare in path_full_compare().
+///
+/// @return true if tag for file "fname" if tag file "tag_fname" is for current
+/// file.
+static int test_for_current(char *fname, char *fname_end, char *tag_fname, char *buf_ffname)
{
int c;
- int retval = FALSE;
- char_u *fullname;
+ int retval = false;
if (buf_ffname != NULL) { // if the buffer has a name
{
- c = *fname_end;
+ c = (unsigned char)(*fname_end);
*fname_end = NUL;
}
- fullname = expand_tag_fname(fname, tag_fname, true);
- retval = (path_full_compare((char *)fullname, (char *)buf_ffname, true, true) & kEqualFiles);
+ char *fullname = (char *)expand_tag_fname((char_u *)fname, (char_u *)tag_fname, true);
+ retval = (path_full_compare(fullname, buf_ffname, true, true) & kEqualFiles);
xfree(fullname);
- *fname_end = (char_u)c;
+ *fname_end = (char)c;
}
return retval;
@@ -3051,7 +3051,7 @@ static int find_extra(char_u **pp)
if (ascii_isdigit(*str)) {
str = (char_u *)skipdigits((char *)str + 1);
} else if (*str == '/' || *str == '?') {
- str = skip_regexp(str + 1, *str, false, NULL);
+ str = (char_u *)skip_regexp((char *)str + 1, *str, false, NULL);
if (*str != first_char) {
str = NULL;
} else {
@@ -3107,13 +3107,13 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
extra_flag = 0;
}
if (pat[0] == '/') {
- ret = find_tags(pat + 1, num_file, file,
+ ret = find_tags((char *)pat + 1, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC,
- TAG_MANY, (char_u *)curbuf->b_ffname);
+ TAG_MANY, curbuf->b_ffname);
} else {
- ret = find_tags(pat, num_file, file,
+ ret = find_tags((char *)pat, num_file, file,
TAG_REGEXP | extra_flag | TAG_VERBOSE | TAG_NO_TAGFUNC | TAG_NOIC,
- TAG_MANY, (char_u *)curbuf->b_ffname);
+ TAG_MANY, curbuf->b_ffname);
}
if (ret == OK && !tagnames) {
// Reorganize the tags for display and matching as strings of:
@@ -3150,8 +3150,7 @@ int expand_tags(int tagnames, char_u *pat, int *num_file, char ***file)
///
/// @param start start of the value
/// @param end after the value; can be NULL
-static int add_tag_field(dict_T *dict, const char *field_name, const char_u *start,
- const char_u *end)
+static int add_tag_field(dict_T *dict, const char *field_name, const char *start, const char *end)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
int len = 0;
@@ -3171,7 +3170,7 @@ static int add_tag_field(dict_T *dict, const char *field_name, const char_u *sta
if (end == NULL) {
end = start + STRLEN(start);
while (end > start && (end[-1] == '\r' || end[-1] == '\n')) {
- --end;
+ end--;
}
}
len = (int)(end - start);
@@ -3198,8 +3197,8 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
tagptrs_T tp;
bool is_static;
- ret = find_tags(pat, &num_matches, &matches,
- TAG_REGEXP | TAG_NOIC, MAXCOL, buf_fname);
+ ret = find_tags((char *)pat, &num_matches, &matches,
+ TAG_REGEXP | TAG_NOIC, MAXCOL, (char *)buf_fname);
if (ret == OK && num_matches > 0) {
for (i = 0; i < num_matches; i++) {
int parse_result = parse_match((char_u *)matches[i], &tp);
@@ -3220,11 +3219,11 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
tv_list_append_dict(list, dict);
full_fname = tag_full_fname(&tp);
- if (add_tag_field(dict, "name", tp.tagname, tp.tagname_end) == FAIL
- || add_tag_field(dict, "filename", full_fname, NULL) == FAIL
- || add_tag_field(dict, "cmd", tp.command, tp.command_end) == FAIL
- || add_tag_field(dict, "kind", tp.tagkind,
- tp.tagkind ? tp.tagkind_end : NULL) == FAIL
+ if (add_tag_field(dict, "name", (char *)tp.tagname, (char *)tp.tagname_end) == FAIL
+ || add_tag_field(dict, "filename", (char *)full_fname, NULL) == FAIL
+ || add_tag_field(dict, "cmd", (char *)tp.command, (char *)tp.command_end) == FAIL
+ || add_tag_field(dict, "kind", (char *)tp.tagkind,
+ tp.tagkind ? (char *)tp.tagkind_end : NULL) == FAIL
|| tv_dict_add_nr(dict, S_LEN("static"), is_static) == FAIL) {
ret = FAIL;
}
@@ -3250,23 +3249,23 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname)
// separated by Tabs.
n = p;
while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') {
- ++p;
+ p++;
}
len = (int)(p - n);
if (*p == ':' && len > 0) {
s = ++p;
while (*p != NUL && *p >= ' ') {
- ++p;
+ p++;
}
n[len] = NUL;
- if (add_tag_field(dict, (char *)n, s, p) == FAIL) {
+ if (add_tag_field(dict, (char *)n, (char *)s, (char *)p) == FAIL) {
ret = FAIL;
}
n[len] = ':';
} else {
// Skip field without colon.
while (*p != NUL && *p >= ' ') {
- ++p;
+ p++;
}
}
if (*p == NUL) {
@@ -3365,7 +3364,7 @@ static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur
}
wp->w_tagstacklen++;
- tagstack[idx].tagname = tagname;
+ tagstack[idx].tagname = (char *)tagname;
tagstack[idx].cur_fnum = cur_fnum;
tagstack[idx].cur_match = cur_match;
if (tagstack[idx].cur_match < 0) {
@@ -3373,7 +3372,7 @@ static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur
}
tagstack[idx].fmark.mark = mark;
tagstack[idx].fmark.fnum = fnum;
- tagstack[idx].user_data = user_data;
+ tagstack[idx].user_data = (char *)user_data;
}
// Add a list of items to the tag stack in the specified window
diff --git a/src/nvim/tag.h b/src/nvim/tag.h
index c8051e1dcc..0b4039afb6 100644
--- a/src/nvim/tag.h
+++ b/src/nvim/tag.h
@@ -35,7 +35,7 @@
// Structure used for get_tagfname().
typedef struct {
char_u *tn_tags; // value of 'tags' when starting
- char_u *tn_np; // current position in tn_tags
+ char *tn_np; // current position in tn_tags
int tn_did_filefind_init;
int tn_hf_idx;
void *tn_search_ctx;
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index eb7c83d317..90966bcfad 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -44,15 +44,16 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/edit.h"
+#include "nvim/drawscreen.h"
+#include "nvim/eval.h"
#include "nvim/event/loop.h"
#include "nvim/event/time.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
@@ -68,8 +69,8 @@
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
-#include "nvim/screen.h"
#include "nvim/state.h"
#include "nvim/terminal.h"
#include "nvim/ui.h"
@@ -417,7 +418,7 @@ bool terminal_enter(void)
// placed at end of buffer to "follow" output. #11072
handle_T save_curwin = curwin->handle;
bool save_w_p_cul = curwin->w_p_cul;
- char_u *save_w_p_culopt = NULL;
+ char *save_w_p_culopt = NULL;
char_u save_w_p_culopt_flags = curwin->w_p_culopt_flags;
int save_w_p_cuc = curwin->w_p_cuc;
long save_w_p_so = curwin->w_p_so;
@@ -425,7 +426,7 @@ bool terminal_enter(void)
if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) {
if (STRCMP(curwin->w_p_culopt, "number")) {
save_w_p_culopt = curwin->w_p_culopt;
- curwin->w_p_culopt = (char_u *)xstrdup("number");
+ curwin->w_p_culopt = xstrdup("number");
}
curwin->w_p_culopt_flags = CULOPT_NBR;
} else {
@@ -681,7 +682,7 @@ static bool is_filter_char(int c)
return !!(tpf_flags & flag);
}
-void terminal_paste(long count, char_u **y_array, size_t y_size)
+void terminal_paste(long count, char **y_array, size_t y_size)
{
if (y_size == 0) {
return;
@@ -702,7 +703,7 @@ void terminal_paste(long count, char_u **y_array, size_t y_size)
buff_len = len;
}
char_u *dst = buff;
- char_u *src = y_array[j];
+ char_u *src = (char_u *)y_array[j];
while (*src != '\0') {
len = (size_t)utf_ptr2len((char *)src);
int c = utf_ptr2char((char *)src);
@@ -1374,7 +1375,7 @@ static bool send_mouse_event(Terminal *term, int c)
curwin->w_redr_status = true;
curwin = save_curwin;
curbuf = curwin->w_buffer;
- redraw_later(mouse_win, NOT_VALID);
+ redraw_later(mouse_win, UPD_NOT_VALID);
invalidate_terminal(term, -1, -1);
// Only need to exit focus if the scrolled window is the terminal window
return mouse_win == curwin;
diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim
index 6b16e888a9..ce23141c7a 100644
--- a/src/nvim/testdir/runtest.vim
+++ b/src/nvim/testdir/runtest.vim
@@ -114,6 +114,13 @@ if has('win32')
let $PROMPT = '$P$G'
endif
+if has('mac')
+ " In MacOS, when starting a shell in a terminal, a bash deprecation warning
+ " message is displayed. This breaks the terminal test. Disable the warning
+ " message.
+ let $BASH_SILENCE_DEPRECATION_WARNING = 1
+endif
+
" Prepare for calling test_garbagecollect_now().
let v:testing = 1
@@ -257,6 +264,7 @@ endfunc
func EarlyExit(test)
" It's OK for the test we use to test the quit detection.
if a:test != 'Test_zz_quit_detected()'
+ call add(v:errors, v:errmsg)
call add(v:errors, 'Test caused Vim to exit: ' . a:test)
endif
@@ -417,7 +425,7 @@ for s:test in sort(s:tests)
set belloff=all
let prev_error = ''
let total_errors = []
- let run_nr = 1
+ let g:run_nr = 1
" A test can set g:test_is_flaky to retry running the test.
let g:test_is_flaky = 0
@@ -436,10 +444,10 @@ for s:test in sort(s:tests)
call add(s:messages, 'Found errors in ' . s:test . ':')
call extend(s:messages, v:errors)
- call add(total_errors, 'Run ' . run_nr . ':')
+ call add(total_errors, 'Run ' . g:run_nr . ':')
call extend(total_errors, v:errors)
- if run_nr == 5 || prev_error == v:errors[0]
+ if g:run_nr >= 5 || prev_error == v:errors[0]
call add(total_errors, 'Flaky test failed too often, giving up')
let v:errors = total_errors
break
@@ -454,7 +462,7 @@ for s:test in sort(s:tests)
let prev_error = v:errors[0]
let v:errors = []
- let run_nr += 1
+ let g:run_nr += 1
call RunTheTest(s:test)
diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim
index 6bc3607b69..472ed4ca14 100644
--- a/src/nvim/testdir/setup.vim
+++ b/src/nvim/testdir/setup.vim
@@ -5,7 +5,7 @@ if exists('s:did_load')
set directory&
set directory^=.
set display=
- set fillchars=vert:\|,fold:-
+ set fillchars=vert:\|,foldsep:\|,fold:-
set formatoptions=tcq
set fsync
set laststatus=1
diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim
index ca7c8574cb..521c3fcd57 100644
--- a/src/nvim/testdir/test_arglist.vim
+++ b/src/nvim/testdir/test_arglist.vim
@@ -87,6 +87,10 @@ func Test_argadd()
new
arga
call assert_equal(0, len(argv()))
+
+ if has('unix')
+ call assert_fails('argadd `Xdoes_not_exist`', 'E479:')
+ endif
endfunc
func Test_argadd_empty_curbuf()
@@ -408,6 +412,35 @@ func Test_argedit()
bw! x
endfunc
+" Test for the :argdedupe command
+func Test_argdedupe()
+ call Reset_arglist()
+ argdedupe
+ call assert_equal([], argv())
+ args a a a aa b b a b aa
+ argdedupe
+ call assert_equal(['a', 'aa', 'b'], argv())
+ args a b c
+ argdedupe
+ call assert_equal(['a', 'b', 'c'], argv())
+ args a
+ argdedupe
+ call assert_equal(['a'], argv())
+ args a A b B
+ argdedupe
+ if has('fname_case')
+ call assert_equal(['a', 'A', 'b', 'B'], argv())
+ else
+ call assert_equal(['a', 'b'], argv())
+ endif
+ args a b a c a b
+ last
+ argdedupe
+ next
+ call assert_equal('c', expand('%:t'))
+ %argd
+endfunc
+
" Test for the :argdelete command
func Test_argdelete()
call Reset_arglist()
@@ -420,6 +453,8 @@ func Test_argdelete()
call assert_equal(['b'], argv())
call assert_fails('argdelete', 'E610:')
call assert_fails('1,100argdelete', 'E16:')
+ call assert_fails('argdel /\)/', 'E55:')
+ call assert_fails('1argdel 1', 'E474:')
call Reset_arglist()
args a b c d
@@ -427,6 +462,8 @@ func Test_argdelete()
argdel
call Assert_argc(['a', 'c', 'd'])
%argdel
+
+ call assert_fails('argdel does_not_exist', 'E480:')
endfunc
func Test_argdelete_completion()
@@ -472,13 +509,16 @@ func Test_arglist_autocmd()
new
" redefine arglist; go to Xxx1
next! Xxx1 Xxx2 Xxx3
- " open window for all args
+ " open window for all args; Reading Xxx2 will change the arglist and the
+ " third window will get Xxx1:
+ " win 1: Xxx1
+ " win 2: Xxx2
+ " win 3: Xxx1
all
call assert_equal('test file Xxx1', getline(1))
wincmd w
wincmd w
call assert_equal('test file Xxx1', getline(1))
- " should now be in Xxx2
rewind
call assert_equal('test file Xxx2', getline(1))
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 1c2f86a584..3064b199d9 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -341,6 +341,39 @@ func Test_WinScrolled_close_curwin()
call delete('Xtestout')
endfunc
+func Test_WinScrolled_long_wrapped()
+ CheckRunVimInTerminal
+
+ let lines =<< trim END
+ set scrolloff=0
+ let height = winheight(0)
+ let width = winwidth(0)
+ let g:scrolled = 0
+ au WinScrolled * let g:scrolled += 1
+ call setline(1, repeat('foo', height * width))
+ call cursor(1, height * width)
+ END
+ call writefile(lines, 'Xtest_winscrolled_long_wrapped')
+ let buf = RunVimInTerminal('-S Xtest_winscrolled_long_wrapped', {'rows': 6})
+
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, 'gj')
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^1 ', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, '0')
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
+
+ call term_sendkeys(buf, '$')
+ call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+ call WaitForAssert({-> assert_match('^3 ', term_getline(buf, 6))}, 1000)
+
+ call delete('Xtest_winscrolled_long_wrapped')
+endfunc
+
func Test_WinClosed()
" Test that the pattern is matched against the closed window's ID, and both
" <amatch> and <afile> are set to it.
@@ -493,6 +526,26 @@ func Test_BufReadCmdHelpJump()
au! BufReadCmd
endfunc
+" BufReadCmd is triggered for a "nofile" buffer. Check all values.
+func Test_BufReadCmdNofile()
+ for val in ['nofile',
+ \ 'nowrite',
+ \ 'acwrite',
+ \ 'quickfix',
+ \ 'help',
+ \ 'prompt',
+ \ ]
+ new somefile
+ exe 'set buftype=' .. val
+ au BufReadCmd somefile call setline(1, 'triggered')
+ edit
+ call assert_equal('triggered', getline(1))
+
+ au! BufReadCmd
+ bwipe!
+ endfor
+endfunc
+
func Test_augroup_deleted()
" This caused a crash before E936 was introduced
augroup x
@@ -587,9 +640,26 @@ func Test_BufEnter()
" On MS-Windows we can't edit the directory, make sure we wipe the right
" buffer.
bwipe! Xdir
-
call delete('Xdir', 'd')
au! BufEnter
+
+ " Editing a "nofile" buffer doesn't read the file but does trigger BufEnter
+ " for historic reasons. Also test other 'buftype' values.
+ for val in ['nofile',
+ \ 'nowrite',
+ \ 'acwrite',
+ \ 'quickfix',
+ \ 'help',
+ \ 'prompt',
+ \ ]
+ new somefile
+ exe 'set buftype=' .. val
+ au BufEnter somefile call setline(1, 'some text')
+ edit
+ call assert_equal('some text', getline(1))
+ bwipe!
+ au! BufEnter
+ endfor
endfunc
" Closing a window might cause an endless loop
@@ -1766,6 +1836,21 @@ func Test_BufReadCmd()
au! BufWriteCmd
endfunc
+func Test_BufWriteCmd()
+ autocmd BufWriteCmd Xbufwritecmd let g:written = 1
+ new
+ file Xbufwritecmd
+ set buftype=acwrite
+ call mkdir('Xbufwritecmd')
+ write
+ " BufWriteCmd should be triggered even if a directory has the same name
+ call assert_equal(1, g:written)
+ call delete('Xbufwritecmd', 'd')
+ unlet g:written
+ au! BufWriteCmd
+ bwipe!
+endfunc
+
func SetChangeMarks(start, end)
exe a:start .. 'mark ['
exe a:end .. 'mark ]'
@@ -2614,6 +2699,28 @@ func Test_BufWrite_lockmarks()
call delete('Xtest2')
endfunc
+func Test_FileType_spell()
+ if !isdirectory('/tmp')
+ throw "Skipped: requires /tmp directory"
+ endif
+
+ " this was crashing with an invalid free()
+ setglobal spellfile=/tmp/en.utf-8.add
+ augroup crash
+ autocmd!
+ autocmd BufNewFile,BufReadPost crashfile setf somefiletype
+ autocmd BufNewFile,BufReadPost crashfile set ft=anotherfiletype
+ autocmd FileType anotherfiletype setlocal spell
+ augroup END
+ func! NoCrash() abort
+ edit /tmp/crashfile
+ endfunc
+ call NoCrash()
+
+ au! crash
+ setglobal spellfile=
+endfunc
+
" Test closing a window or editing another buffer from a FileChangedRO handler
" in a readonly buffer
func Test_FileChangedRO_winclose()
@@ -2702,6 +2809,30 @@ func Test_autocmd_FileReadCmd()
delfunc ReadFileCmd
endfunc
+" Test for passing invalid arguments to autocmd
+func Test_autocmd_invalid_args()
+ " Additional character after * for event
+ call assert_fails('autocmd *a Xfile set ff=unix', 'E215:')
+ augroup Test
+ augroup END
+ " Invalid autocmd event
+ call assert_fails('autocmd Bufabc Xfile set ft=vim', 'E216:')
+ " Invalid autocmd event in a autocmd group
+ call assert_fails('autocmd Test Bufabc Xfile set ft=vim', 'E216:')
+ augroup! Test
+ " Execute all autocmds
+ call assert_fails('doautocmd * BufEnter', 'E217:')
+ call assert_fails('augroup! x1a2b3', 'E367:')
+ call assert_fails('autocmd BufNew <buffer=999> pwd', 'E680:')
+endfunc
+
+" Test for deep nesting of autocmds
+func Test_autocmd_deep_nesting()
+ autocmd BufEnter Xfile doautocmd BufEnter Xfile
+ call assert_fails('doautocmd BufEnter Xfile', 'E218:')
+ autocmd! BufEnter Xfile
+endfunc
+
" Tests for SigUSR1 autocmd event, which is only available on posix systems.
func Test_autocmd_sigusr1()
CheckUnix
@@ -2715,6 +2846,59 @@ func Test_autocmd_sigusr1()
unlet g:sigusr1_passed
endfunc
+" Test for BufReadPre autocmd deleting the file
+func Test_BufReadPre_delfile()
+ augroup TestAuCmd
+ au!
+ autocmd BufReadPre Xfile call delete('Xfile')
+ augroup END
+ call writefile([], 'Xfile')
+ call assert_fails('new Xfile', 'E200:')
+ call assert_equal('Xfile', @%)
+ call assert_equal(1, &readonly)
+ call delete('Xfile')
+ augroup TestAuCmd
+ au!
+ augroup END
+ close!
+endfunc
+
+" Test for BufReadPre autocmd changing the current buffer
+func Test_BufReadPre_changebuf()
+ augroup TestAuCmd
+ au!
+ autocmd BufReadPre Xfile edit Xsomeotherfile
+ augroup END
+ call writefile([], 'Xfile')
+ call assert_fails('new Xfile', 'E201:')
+ call assert_equal('Xsomeotherfile', @%)
+ call assert_equal(1, &readonly)
+ call delete('Xfile')
+ augroup TestAuCmd
+ au!
+ augroup END
+ close!
+endfunc
+
+" Test for BufWipeouti autocmd changing the current buffer when reading a file
+" in an empty buffer with 'f' flag in 'cpo'
+func Test_BufDelete_changebuf()
+ new
+ augroup TestAuCmd
+ au!
+ autocmd BufWipeout * let bufnr = bufadd('somefile') | exe "b " .. bufnr
+ augroup END
+ let save_cpo = &cpo
+ set cpo+=f
+ call assert_fails('r Xfile', 'E484:')
+ call assert_equal('somefile', @%)
+ let &cpo = save_cpo
+ augroup TestAuCmd
+ au!
+ augroup END
+ close!
+endfunc
+
" Test for the temporary internal window used to execute autocmds
func Test_autocmd_window()
%bw!
diff --git a/src/nvim/testdir/test_blob.vim b/src/nvim/testdir/test_blob.vim
index af42b3857d..70529c14d5 100644
--- a/src/nvim/testdir/test_blob.vim
+++ b/src/nvim/testdir/test_blob.vim
@@ -294,7 +294,7 @@ func Test_blob_index()
call assert_equal(2, index(0zDEADBEEF, 0xBE))
call assert_equal(-1, index(0zDEADBEEF, 0))
call assert_equal(2, index(0z11111111, 0x11, 2))
- call assert_equal(3, index(0z11110111, 0x11, 2))
+ call assert_equal(3, 0z11110111->index(0x11, 2))
call assert_equal(2, index(0z11111111, 0x11, -2))
call assert_equal(3, index(0z11110111, 0x11, -2))
diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim
index 67be3e6747..4def3b5df9 100644
--- a/src/nvim/testdir/test_buffer.vim
+++ b/src/nvim/testdir/test_buffer.vim
@@ -154,6 +154,24 @@ func Test_bdelete_cmd()
set nobuflisted
enew
call assert_fails('bdelete ' .. bnr, 'E516:')
+
+ " Deleting more than one buffer
+ new Xbuf1
+ new Xbuf2
+ exe 'bdel ' .. bufnr('Xbuf2') .. ' ' .. bufnr('Xbuf1')
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xbuf1')[0].loaded)
+ call assert_equal(0, getbufinfo('Xbuf2')[0].loaded)
+
+ " Deleting more than one buffer and an invalid buffer
+ new Xbuf1
+ new Xbuf2
+ let cmd = "exe 'bdel ' .. bufnr('Xbuf2') .. ' xxx ' .. bufnr('Xbuf1')"
+ call assert_fails(cmd, 'E94:')
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, getbufinfo('Xbuf1')[0].loaded)
+ call assert_equal(0, getbufinfo('Xbuf2')[0].loaded)
+
%bwipe!
endfunc
@@ -168,6 +186,194 @@ func Test_buffer_error()
%bwipe
endfunc
+" Test for the status messages displayed when unloading, deleting or wiping
+" out buffers
+func Test_buffer_statusmsg()
+ CheckEnglish
+ set report=1
+ new Xbuf1
+ new Xbuf2
+ let bnr = bufnr()
+ exe "normal 2\<C-G>"
+ call assert_match('buf ' .. bnr .. ':', v:statusmsg)
+ bunload Xbuf1 Xbuf2
+ call assert_equal('2 buffers unloaded', v:statusmsg)
+ bdel Xbuf1 Xbuf2
+ call assert_equal('2 buffers deleted', v:statusmsg)
+ bwipe Xbuf1 Xbuf2
+ call assert_equal('2 buffers wiped out', v:statusmsg)
+ set report&
+endfunc
+
+" Test for quitting the 'swapfile exists' dialog with the split buffer
+" command.
+func Test_buffer_sbuf_cleanup()
+ call writefile([], 'Xfile')
+ " first open the file in a buffer
+ new Xfile
+ let bnr = bufnr()
+ close
+ " create the swap file
+ call writefile([], '.Xfile.swp')
+ " Remove the catch-all that runtest.vim adds
+ au! SwapExists
+ augroup BufTest
+ au!
+ autocmd SwapExists Xfile let v:swapchoice='q'
+ augroup END
+ exe 'sbuf ' . bnr
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xfile')[0].loaded)
+
+ " test for :sball
+ sball
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xfile')[0].loaded)
+
+ %bw!
+ set shortmess+=F
+ let v:statusmsg = ''
+ edit Xfile
+ call assert_equal('', v:statusmsg)
+ call assert_equal(1, winnr('$'))
+ call assert_equal(0, getbufinfo('Xfile')[0].loaded)
+ set shortmess&
+
+ call delete('Xfile')
+ call delete('.Xfile.swp')
+ augroup BufTest
+ au!
+ augroup END
+ augroup! BufTest
+endfunc
+
+" Test for deleting a modified buffer with :confirm
+func Test_bdel_with_confirm()
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/buffer_spec.lua'
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ new
+ call setline(1, 'test')
+ call assert_fails('bdel', 'E89:')
+ call feedkeys('c', 'L')
+ confirm bdel
+ call assert_equal(2, winnr('$'))
+ call assert_equal(1, &modified)
+ call feedkeys('n', 'L')
+ confirm bdel
+ call assert_equal(1, winnr('$'))
+endfunc
+
+" Test for editing another buffer from a modified buffer with :confirm
+func Test_goto_buf_with_confirm()
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/buffer_spec.lua'
+ CheckUnix
+ CheckNotGui
+ CheckFeature dialog_con
+ new Xfile
+ enew
+ call setline(1, 'test')
+ call assert_fails('b Xfile', 'E37:')
+ call feedkeys('c', 'L')
+ call assert_fails('confirm b Xfile', 'E37:')
+ call assert_equal(1, &modified)
+ call assert_equal('', @%)
+ call feedkeys('y', 'L')
+ call assert_fails('confirm b Xfile', 'E37:')
+ call assert_equal(1, &modified)
+ call assert_equal('', @%)
+ call feedkeys('n', 'L')
+ confirm b Xfile
+ call assert_equal('Xfile', @%)
+ close!
+endfunc
+
+" Test for splitting buffer with 'switchbuf'
+func Test_buffer_switchbuf()
+ new Xfile
+ wincmd w
+ set switchbuf=useopen
+ sbuf Xfile
+ call assert_equal(1, winnr())
+ call assert_equal(2, winnr('$'))
+ set switchbuf=usetab
+ tabnew
+ sbuf Xfile
+ call assert_equal(1, tabpagenr())
+ call assert_equal(2, tabpagenr('$'))
+ set switchbuf&
+ %bw
+endfunc
+
+" Test for BufAdd autocommand wiping out the buffer
+func Test_bufadd_autocmd_bwipe()
+ %bw!
+ augroup BufAdd_Wipe
+ au!
+ autocmd BufAdd Xfile %bw!
+ augroup END
+ edit Xfile
+ call assert_equal('', @%)
+ call assert_equal(0, bufexists('Xfile'))
+ augroup BufAdd_Wipe
+ au!
+ augroup END
+ augroup! BufAdd_Wipe
+endfunc
+
+" Test for trying to load a buffer with text locked
+" <C-\>e in the command line is used to lock the text
+func Test_load_buf_with_text_locked()
+ new Xfile1
+ edit Xfile2
+ let cmd = ":\<C-\>eexecute(\"normal \<C-O>\")\<CR>\<C-C>"
+ call assert_fails("call feedkeys(cmd, 'xt')", 'E565:')
+ %bw!
+endfunc
+
+" Test for using CTRL-^ to edit the alternative file keeping the cursor
+" position with 'nostartofline'. Also test using the 'buf' command.
+func Test_buffer_edit_altfile()
+ call writefile(repeat(['one two'], 50), 'Xfile1')
+ call writefile(repeat(['five six'], 50), 'Xfile2')
+ set nosol
+ edit Xfile1
+ call cursor(25, 5)
+ edit Xfile2
+ call cursor(30, 4)
+ exe "normal \<C-^>"
+ call assert_equal([0, 25, 5, 0], getpos('.'))
+ exe "normal \<C-^>"
+ call assert_equal([0, 30, 4, 0], getpos('.'))
+ buf Xfile1
+ call assert_equal([0, 25, 5, 0], getpos('.'))
+ buf Xfile2
+ call assert_equal([0, 30, 4, 0], getpos('.'))
+ set sol&
+ call delete('Xfile1')
+ call delete('Xfile2')
+endfunc
+
+" Test for running the :sball command with a maximum window count and a
+" modified buffer
+func Test_sball_with_count()
+ %bw!
+ edit Xfile1
+ call setline(1, ['abc'])
+ new Xfile2
+ new Xfile3
+ new Xfile4
+ 2sball
+ call assert_equal(bufnr('Xfile4'), winbufnr(1))
+ call assert_equal(bufnr('Xfile1'), winbufnr(2))
+ call assert_equal(0, getbufinfo('Xfile2')[0].loaded)
+ call assert_equal(0, getbufinfo('Xfile3')[0].loaded)
+ %bw!
+endfunc
+
func Test_badd_options()
new SomeNewBuffer
setlocal numberwidth=3
diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim
index ffb8e3facd..3b5bcbce89 100644
--- a/src/nvim/testdir/test_bufline.vim
+++ b/src/nvim/testdir/test_bufline.vim
@@ -19,7 +19,7 @@ func Test_setbufline_getbufline()
let b = bufnr('%')
wincmd w
call assert_equal(1, setbufline(b, 5, ['x']))
- call assert_equal(1, setbufline(1234, 1, ['x']))
+ call assert_equal(1, ['x']->setbufline(bufnr('$') + 1, 1))
call assert_equal(0, setbufline(b, 4, ['d', 'e']))
call assert_equal(['c'], b->getbufline(3))
call assert_equal(['d'], getbufline(b, 4))
@@ -187,4 +187,24 @@ func Test_deletebufline_select_mode()
bwipe!
endfunc
+func Test_setbufline_startup_nofile()
+ let before =<< trim [CODE]
+ set shortmess+=F
+ file Xresult
+ set buftype=nofile
+ call setbufline('', 1, 'success')
+ [CODE]
+ let after =<< trim [CODE]
+ set buftype=
+ write
+ quit
+ [CODE]
+
+ if !RunVim(before, after, '--clean')
+ return
+ endif
+ call assert_equal(['success'], readfile('Xresult'))
+ call delete('Xresult')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cd.vim b/src/nvim/testdir/test_cd.vim
index a1e53df774..d6d44d1901 100644
--- a/src/nvim/testdir/test_cd.vim
+++ b/src/nvim/testdir/test_cd.vim
@@ -113,7 +113,7 @@ func Test_chdir_func()
call assert_equal('z', fnamemodify(3->getcwd(2), ':t'))
tabnext | wincmd t
call assert_match('^\[tabpage\] .*/y$', trim(execute('verbose pwd')))
- call chdir('..')
+ eval '..'->chdir()
call assert_equal('Xdir', fnamemodify(getcwd(1, 2), ':t'))
call assert_equal('Xdir', fnamemodify(getcwd(2, 2), ':t'))
call assert_equal('z', fnamemodify(getcwd(3, 2), ':t'))
diff --git a/src/nvim/testdir/test_clientserver.vim b/src/nvim/testdir/test_clientserver.vim
index edf36b413b..66ee776a90 100644
--- a/src/nvim/testdir/test_clientserver.vim
+++ b/src/nvim/testdir/test_clientserver.vim
@@ -2,6 +2,11 @@
source check.vim
CheckFeature job
+
+if !has('clientserver')
+ call assert_fails('call remote_startserver("local")', 'E942:')
+endif
+
CheckFeature clientserver
source shared.vim
@@ -66,8 +71,9 @@ func Test_client_server()
call remote_send(name, ":gui -f\<CR>")
endif
" Wait for the server to be up and answering requests.
- sleep 100m
- call WaitForAssert({-> assert_true(name->remote_expr("v:version", "", 1) != "")})
+ " When using valgrind this can be very, very slow.
+ sleep 1
+ call WaitForAssert({-> assert_match('\d', name->remote_expr("v:version", "", 1))}, 10000)
call remote_send(name, ":let testvar = 'maybe'\<CR>")
call WaitForAssert({-> assert_equal('maybe', remote_expr(name, "testvar", "", 2))})
@@ -178,6 +184,7 @@ func Test_client_server()
call assert_fails("let x = remote_peek([])", 'E730:')
call assert_fails("let x = remote_read('vim10')", 'E277:')
+ call assert_fails("call server2client('abc', 'xyz')", 'E258:')
endfunc
" Uncomment this line to get a debugging log
diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim
index 7aac731709..4bfd22cb6c 100644
--- a/src/nvim/testdir/test_cmdline.vim
+++ b/src/nvim/testdir/test_cmdline.vim
@@ -3,6 +3,7 @@
source check.vim
source screendump.vim
source view_util.vim
+source shared.vim
func Test_complete_tab()
call writefile(['testfile'], 'Xtestfile')
@@ -126,6 +127,40 @@ func Test_wildmenu_screendump()
call delete('XTest_wildmenu')
endfunc
+func Test_changing_cmdheight()
+ CheckScreendump
+
+ let lines =<< trim END
+ set cmdheight=1 laststatus=2
+ END
+ call writefile(lines, 'XTest_cmdheight')
+
+ let buf = RunVimInTerminal('-S XTest_cmdheight', {'rows': 8})
+ call term_sendkeys(buf, ":resize -3\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_1', {})
+
+ " using the space available doesn't change the status line
+ call term_sendkeys(buf, ":set cmdheight+=3\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_2', {})
+
+ " using more space moves the status line up
+ call term_sendkeys(buf, ":set cmdheight+=1\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_3', {})
+
+ " reducing cmdheight moves status line down
+ call term_sendkeys(buf, ":set cmdheight-=2\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_4', {})
+
+ " reducing window size and then setting cmdheight
+ call term_sendkeys(buf, ":resize -1\<CR>")
+ call term_sendkeys(buf, ":set cmdheight=1\<CR>")
+ call VerifyScreenDump(buf, 'Test_changing_cmdheight_5', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_cmdheight')
+endfunc
+
func Test_map_completion()
if !has('cmdline_compl')
return
@@ -209,7 +244,7 @@ func Test_match_completion()
return
endif
hi Aardig ctermfg=green
- call feedkeys(":match \<Tab>\<Home>\"\<CR>", 'xt')
+ call feedkeys(":match A\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"match Aardig', getreg(':'))
call feedkeys(":match \<S-Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"match none', getreg(':'))
@@ -220,9 +255,7 @@ func Test_highlight_completion()
return
endif
hi Aardig ctermfg=green
- call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt')
- call assert_equal('"hi Aardig', getreg(':'))
- call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt')
+ call feedkeys(":hi default A\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"hi default Aardig', getreg(':'))
call feedkeys(":hi clear Aa\<Tab>\<Home>\"\<CR>", 'xt')
call assert_equal('"hi clear Aardig', getreg(':'))
@@ -480,6 +513,7 @@ func Test_getcompletion()
call delete('Xtags')
set tags&
+ call assert_fails("call getcompletion('\\\\@!\\\\@=', 'buffer')", 'E871:')
call assert_fails('call getcompletion("", "burp")', 'E475:')
call assert_fails('call getcompletion("abc", [])', 'E475:')
endfunc
@@ -912,12 +946,26 @@ func Test_cmdline_complete_various()
call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt')
call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:)
+ " completion of autocmd group after comma
+ call feedkeys(":doautocmd BufNew,BufEn\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"doautocmd BufNew,BufEnter", @:)
+
+ " completion of file name in :doautocmd
+ call writefile([], 'Xfile1')
+ call writefile([], 'Xfile2')
+ call feedkeys(":doautocmd BufEnter Xfi\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"doautocmd BufEnter Xfile1 Xfile2", @:)
+ call delete('Xfile1')
+ call delete('Xfile2')
+
" completion for the :augroup command
- augroup XTest
+ augroup XTest.test
augroup END
call feedkeys(":augroup X\<C-A>\<C-B>\"\<CR>", 'xt')
- call assert_equal("\"augroup XTest", @:)
- augroup! XTest
+ call assert_equal("\"augroup XTest.test", @:)
+ call feedkeys(":au X\<C-A>\<C-B>\"\<CR>", 'xt')
+ call assert_equal("\"au XTest.test", @:)
+ augroup! XTest.test
" completion for the :unlet command
call feedkeys(":unlet one two\<C-A>\<C-B>\"\<CR>", 'xt')
@@ -1212,6 +1260,16 @@ func Test_cmdline_overstrike()
let &encoding = encoding_save
endfunc
+func Test_cmdwin_bug()
+ let winid = win_getid()
+ sp
+ try
+ call feedkeys("q::call win_gotoid(" .. winid .. ")\<CR>:q\<CR>", 'x!')
+ catch /^Vim\%((\a\+)\)\=:E11/
+ endtry
+ bw!
+endfunc
+
func Test_cmdwin_restore()
CheckScreendump
@@ -1392,14 +1450,6 @@ func Test_cmdwin_jump_to_win()
call assert_equal(1, winnr('$'))
endfunc
-" Test for backtick expression in the command line
-func Test_cmd_backtick()
- %argd
- argadd `=['a', 'b', 'c']`
- call assert_equal(['a', 'b', 'c'], argv())
- %argd
-endfunc
-
func Test_cmdwin_tabpage()
tabedit
" v8.2.1919 isn't ported yet, so E492 is thrown after E11 here.
@@ -1412,11 +1462,48 @@ func Test_cmdwin_tabpage()
tabclose!
endfunc
+func Test_cmdwin_interrupted()
+ CheckScreendump
+
+ " aborting the :smile output caused the cmdline window to use the current
+ " buffer.
+ let lines =<< trim [SCRIPT]
+ au WinNew * smile
+ [SCRIPT]
+ call writefile(lines, 'XTest_cmdwin')
+
+ let buf = RunVimInTerminal('-S XTest_cmdwin', {'rows': 18})
+ " open cmdwin
+ call term_sendkeys(buf, "q:")
+ call WaitForAssert({-> assert_match('-- More --', term_getline(buf, 18))})
+ " quit more prompt for :smile command
+ call term_sendkeys(buf, "q")
+ call WaitForAssert({-> assert_match('^$', term_getline(buf, 18))})
+ " execute a simple command
+ call term_sendkeys(buf, "aecho 'done'\<CR>")
+ call VerifyScreenDump(buf, 'Test_cmdwin_interrupted', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete('XTest_cmdwin')
+endfunc
+
+" Test for backtick expression in the command line
+func Test_cmd_backtick()
+ CheckNotMSWindows " FIXME: see #19297
+ %argd
+ argadd `=['a', 'b', 'c']`
+ call assert_equal(['a', 'b', 'c'], argv())
+ %argd
+
+ argadd `echo abc def`
+ call assert_equal(['abc def'], argv())
+ %argd
+endfunc
+
" Test for the :! command
func Test_cmd_bang()
- if !has('unix')
- return
- endif
+ CheckUnix
let lines =<< trim [SCRIPT]
" Test for no previous command
@@ -1758,6 +1845,36 @@ func Test_read_shellcmd()
endif
endfunc
+" Test for going up and down the directory tree using 'wildmenu'
+func Test_wildmenu_dirstack()
+ CheckUnix
+ %bw!
+ call mkdir('Xdir1/dir2/dir3', 'p')
+ call writefile([], 'Xdir1/file1_1.txt')
+ call writefile([], 'Xdir1/file1_2.txt')
+ call writefile([], 'Xdir1/dir2/file2_1.txt')
+ call writefile([], 'Xdir1/dir2/file2_2.txt')
+ call writefile([], 'Xdir1/dir2/dir3/file3_1.txt')
+ call writefile([], 'Xdir1/dir2/dir3/file3_2.txt')
+ cd Xdir1/dir2/dir3
+ set wildmenu
+
+ call feedkeys(":e \<Tab>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e file3_1.txt', @:)
+ call feedkeys(":e \<Tab>\<Up>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../dir3/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../dir2/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../dir2/dir3/', @:)
+ call feedkeys(":e \<Tab>\<Up>\<Up>\<Down>\<Down>\<C-B>\"\<CR>", 'xt')
+ call assert_equal('"e ../../dir2/dir3/file3_1.txt', @:)
+
+ cd -
+ call delete('Xdir1', 'rf')
+ set wildmenu&
+endfunc
+
" Test for recalling newer or older cmdline from history with <Up>, <Down>,
" <S-Up>, <S-Down>, <PageUp>, <PageDown>, <C-p>, or <C-n>.
func Test_recalling_cmdline()
@@ -1806,6 +1923,199 @@ func Test_recalling_cmdline()
cunmap <Plug>(save-cmdline)
endfunc
+" Test for using a popup menu for the command line completion matches
+" (wildoptions=pum)
+func Test_wildmenu_pum()
+ CheckRunVimInTerminal
+
+ let commands =<< trim [CODE]
+ set wildmenu
+ set wildoptions=pum
+ set shm+=I
+ set noruler
+ set noshowcmd
+ [CODE]
+ call writefile(commands, 'Xtest')
+
+ let buf = RunVimInTerminal('-S Xtest', #{rows: 10})
+
+ call term_sendkeys(buf, ":sign \<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_01', {})
+
+ call term_sendkeys(buf, "\<Down>\<Down>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_02', {})
+
+ call term_sendkeys(buf, "\<C-N>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_03', {})
+
+ call term_sendkeys(buf, "\<C-P>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_04', {})
+
+ call term_sendkeys(buf, "\<Up>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_05', {})
+
+ " pressing <C-E> should end completion and go back to the original match
+ call term_sendkeys(buf, "\<C-E>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_06', {})
+
+ " pressing <C-Y> should select the current match and end completion
+ call term_sendkeys(buf, "\<Tab>\<C-P>\<C-P>\<C-Y>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_07', {})
+
+ " With 'wildmode' set to 'longest,full', completing a match should display
+ " the longest match, the wildmenu should not be displayed.
+ call term_sendkeys(buf, ":\<C-U>set wildmode=longest,full\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":sign u\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_08', {})
+
+ " pressing <Tab> should display the wildmenu
+ call term_sendkeys(buf, "\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_09', {})
+
+ " pressing <Tab> second time should select the next entry in the menu
+ call term_sendkeys(buf, "\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_10', {})
+
+ call term_sendkeys(buf, ":\<C-U>set wildmode=full\<CR>")
+ " " showing popup menu in different columns in the cmdline
+ call term_sendkeys(buf, ":sign define \<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_11', {})
+
+ call term_sendkeys(buf, " \<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_12', {})
+
+ call term_sendkeys(buf, " \<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_13', {})
+
+ " Directory name completion
+ call mkdir('Xdir/XdirA/XdirB', 'p')
+ call writefile([], 'Xdir/XfileA')
+ call writefile([], 'Xdir/XdirA/XfileB')
+ call writefile([], 'Xdir/XdirA/XdirB/XfileC')
+
+ call term_sendkeys(buf, "\<C-U>e Xdi\<Tab>\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_14', {})
+
+ " Pressing <Right> on a directory name should go into that directory
+ call term_sendkeys(buf, "\<Right>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_15', {})
+
+ " Pressing <Left> on a directory name should go to the parent directory
+ call term_sendkeys(buf, "\<Left>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_16', {})
+
+ " Pressing <C-A> when the popup menu is displayed should list all the
+ " matches and remove the popup menu
+ call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-A>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_17', {})
+
+ " Pressing <C-D> when the popup menu is displayed should remove the popup
+ " menu
+ call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-D>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_18', {})
+
+ " Pressing <S-Tab> should open the popup menu with the last entry selected
+ call term_sendkeys(buf, "\<C-U>\<CR>:sign \<S-Tab>\<C-P>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_19', {})
+
+ " Pressing <Esc> should close the popup menu and cancel the cmd line
+ call term_sendkeys(buf, "\<C-U>\<CR>:sign \<Tab>\<Esc>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_20', {})
+
+ " Typing a character when the popup is open, should close the popup
+ call term_sendkeys(buf, ":sign \<Tab>x")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_21', {})
+
+ " When the popup is open, entering the cmdline window should close the popup
+ call term_sendkeys(buf, "\<C-U>sign \<Tab>\<C-F>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_22', {})
+ call term_sendkeys(buf, ":q\<CR>")
+
+ " After the last popup menu item, <C-N> should show the original string
+ call term_sendkeys(buf, ":sign u\<Tab>\<C-N>\<C-N>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_23', {})
+
+ " Use the popup menu for the command name
+ call term_sendkeys(buf, "\<C-U>bu\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_24', {})
+
+ " Pressing the left arrow should remove the popup menu
+ call term_sendkeys(buf, "\<Left>\<Left>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_25', {})
+
+ " Pressing <BS> should remove the popup menu and erase the last character
+ call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<BS>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_26', {})
+
+ " Pressing <C-W> should remove the popup menu and erase the previous word
+ call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<C-W>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_27', {})
+
+ " Pressing <C-U> should remove the popup menu and erase the entire line
+ call term_sendkeys(buf, "\<C-E>\<C-U>sign \<Tab>\<C-U>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_28', {})
+
+ " Using <C-E> to cancel the popup menu and then pressing <Up> should recall
+ " the cmdline from history
+ call term_sendkeys(buf, "sign xyz\<Esc>:sign \<Tab>\<C-E>\<Up>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {})
+
+ " Check "list" still works
+ call term_sendkeys(buf, "\<C-U>set wildmode=longest,list\<CR>")
+ call term_sendkeys(buf, ":cn\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {})
+ call term_sendkeys(buf, "s")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {})
+
+ " Tests a directory name contained full-width characters.
+ call mkdir('Xdir/ã‚ã„ã†', 'p')
+ call writefile([], 'Xdir/ã‚ã„ã†/abc')
+ call writefile([], 'Xdir/ã‚ã„ã†/xyz')
+ call writefile([], 'Xdir/ã‚ã„ã†/123')
+
+ call term_sendkeys(buf, "\<C-U>set wildmode&\<CR>")
+ call term_sendkeys(buf, ":\<C-U>e Xdir/ã‚ã„ã†/\<Tab>")
+ call TermWait(buf)
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_32', {})
+
+ call term_sendkeys(buf, "\<C-U>\<CR>")
+ call StopVimInTerminal(buf)
+ call delete('Xtest')
+ call delete('Xdir', 'rf')
+endfunc
+
" this was going over the end of IObuff
func Test_report_error_with_composing()
let caught = 'no'
@@ -1875,4 +2185,67 @@ func Test_cmdline_redraw_tabline()
call delete('Xcmdline_redraw_tabline')
endfunc
+func Test_wildmenu_pum_disable_while_shown()
+ set wildoptions=pum
+ set wildmenu
+ cnoremap <F2> <Cmd>set nowildmenu<CR>
+ call feedkeys(":sign \<Tab>\<F2>\<Esc>", 'tx')
+ call assert_equal(0, pumvisible())
+ cunmap <F2>
+ set wildoptions& wildmenu&
+endfunc
+
+func Test_setcmdline()
+ func SetText(text, pos)
+ autocmd CmdlineChanged * let g:cmdtype = expand('<afile>')
+ call assert_equal(0, setcmdline(a:text))
+ call assert_equal(a:text, getcmdline())
+ call assert_equal(len(a:text) + 1, getcmdpos())
+ call assert_equal(getcmdtype(), g:cmdtype)
+ unlet g:cmdtype
+ autocmd! CmdlineChanged
+
+ call assert_equal(0, setcmdline(a:text, a:pos))
+ call assert_equal(a:text, getcmdline())
+ call assert_equal(a:pos, getcmdpos())
+
+ call assert_fails('call setcmdline("' .. a:text .. '", -1)', 'E487:')
+ call assert_fails('call setcmdline({}, 0)', 'E928:')
+ call assert_fails('call setcmdline("' .. a:text .. '", {})', 'E728:')
+
+ return ''
+ endfunc
+
+ call feedkeys(":\<C-R>=SetText('set rtp?', 2)\<CR>\<CR>", 'xt')
+ call assert_equal('set rtp?', @:)
+
+ call feedkeys(":let g:str = input('? ')\<CR>", 't')
+ call feedkeys("\<C-R>=SetText('foo', 4)\<CR>\<CR>", 'xt')
+ call assert_equal('foo', g:str)
+ unlet g:str
+
+ delfunc SetText
+
+ " setcmdline() returns 1 when not editing the command line.
+ call assert_equal(1, 'foo'->setcmdline())
+
+ " Called in custom function
+ func CustomComplete(A, L, P)
+ call assert_equal(0, setcmdline("DoCmd "))
+ return "January\nFebruary\nMars\n"
+ endfunc
+
+ com! -nargs=* -complete=custom,CustomComplete DoCmd :
+ call feedkeys(":DoCmd \<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_equal('"DoCmd January February Mars', @:)
+ delcom DoCmd
+ delfunc CustomComplete
+
+ " Called in <expr>
+ cnoremap <expr>a setcmdline('let foo=')
+ call feedkeys(":a\<CR>", 'tx')
+ call assert_equal('let foo=0', @:)
+ cunmap a
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_comments.vim b/src/nvim/testdir/test_comments.vim
new file mode 100644
index 0000000000..c34b85c42d
--- /dev/null
+++ b/src/nvim/testdir/test_comments.vim
@@ -0,0 +1,277 @@
+" Tests for the various flags in the 'comments' option
+
+" Test for the 'n' flag in 'comments'
+func Test_comment_nested()
+ new
+ setlocal comments=n:> fo+=ro
+ exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH"
+ exe "normal 5GOE\<C-C>6GoG"
+ let expected =<< trim END
+ > A
+ > B
+ > C
+ > D
+ >>>> E
+ >>>> F
+ >>>> G
+ >>>> H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'b' flag in 'comments'
+func Test_comment_blank()
+ new
+ setlocal comments=b:* fo+=ro
+ exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD"
+ let expected =<< trim END
+ A
+ *B
+ * C
+ * D
+ * E
+ * F
+ *G
+ H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'f' flag in 'comments' (only the first line has a comment
+" string)
+func Test_comment_firstline()
+ new
+ setlocal comments=f:- fo+=ro
+ exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
+ call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$'))
+ %d
+ setlocal comments=:-
+ exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
+ call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 's', 'm' and 'e' flags in 'comments'
+" Test for automatically adding comment leaders in insert mode
+func Test_comment_threepiece()
+ new
+ setlocal expandtab
+ call setline(1, ["\t/*"])
+ setlocal formatoptions=croql
+ call cursor(1, 3)
+ call feedkeys("A\<cr>\<cr>/", 'tnix')
+ call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
+
+ " If a comment ends in a single line, then don't add it in the next line
+ %d
+ call setline(1, '/* line1 */')
+ call feedkeys("A\<CR>next line", 'xt')
+ call assert_equal(['/* line1 */', 'next line'], getline(1, '$'))
+
+ %d
+ " Copy the trailing indentation from the leader comment to a new line
+ setlocal autoindent noexpandtab
+ call feedkeys("a\t/*\tone\ntwo\n/", 'xt')
+ call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'r' flag in 'comments' (right align comment)
+func Test_comment_rightalign()
+ new
+ setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro
+ exe "normal i=\<C-C>o\t /***\nD\n/"
+ exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG"
+ let expected =<< trim END
+ =
+ A
+ /***
+ ** B
+ ** C
+ ** D
+ ** E
+ ** F
+ ******/
+ G
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for the 'O' flag in 'comments'
+func Test_comment_O()
+ new
+ setlocal comments=Ob:* fo+=ro
+ exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
+ let expected =<< trim END
+ A
+ * B
+ * C
+ * D
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for using a multibyte character as a comment leader
+func Test_comment_multibyte_leader()
+ new
+ let t =<< trim END
+ {
+ X
+ Xa
+ XaY
+ XY
+ XYZ
+ X Y
+ X YZ
+ XX
+ XXa
+ XXY
+ }
+ END
+ call setline(1, t)
+ call cursor(2, 1)
+
+ set tw=2 fo=cqm comments=n:X
+ exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq"
+ let t =<< trim END
+ X
+ Xa
+ XaY
+ XY
+ XYZ
+ X Y
+ X YZ
+ XX
+ XXa
+ XXY
+ END
+ exe "normal o\n" . join(t, "\n")
+
+ let expected =<< trim END
+ {
+ X
+ Xa
+ Xa
+ XY
+ XY
+ XY
+ XZ
+ X Y
+ X Y
+ X Z
+ XX
+ XXa
+ XXY
+
+ X
+ Xa
+ Xa
+ XY
+ XY
+ XY
+ XZ
+ X Y
+ X Y
+ X Z
+ XX
+ XXa
+ XXY
+ }
+ END
+ call assert_equal(expected, getline(1, '$'))
+
+ set tw& fo& comments&
+ close!
+endfunc
+
+" Test for a space character in 'comments' setting
+func Test_comment_space()
+ new
+ setlocal comments=b:\ > fo+=ro
+ exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
+ exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
+ let expected =<< trim END
+ A
+ > B
+ C
+ D
+ > E
+ > F
+ > G
+ > H
+ END
+ call assert_equal(expected, getline(1, '$'))
+ close!
+endfunc
+
+" Test for formatting lines with and without comments
+func Test_comment_format_lines()
+ new
+ call setline(1, ['one', '/* two */', 'three'])
+ normal gggqG
+ call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for using 'a' in 'formatoptions' with comments
+func Test_comment_autoformat()
+ new
+ setlocal formatoptions+=a
+ call feedkeys("a- one\n- two\n", 'xt')
+ call assert_equal(['- one', '- two', ''], getline(1, '$'))
+
+ %d
+ call feedkeys("a\none\n", 'xt')
+ call assert_equal(['', 'one', ''], getline(1, '$'))
+
+ setlocal formatoptions+=aw
+ %d
+ call feedkeys("aone \ntwo\n", 'xt')
+ call assert_equal(['one two', ''], getline(1, '$'))
+
+ %d
+ call feedkeys("aone\ntwo\n", 'xt')
+ call assert_equal(['one', 'two', ''], getline(1, '$'))
+
+ close!
+endfunc
+
+" Test for joining lines with comments ('j' flag in 'formatoptions')
+func Test_comment_join_lines_fo_j()
+ new
+ setlocal fo+=j comments=://
+ call setline(1, ['i++; // comment1', ' // comment2'])
+ normal J
+ call assert_equal('i++; // comment1 comment2', getline(1))
+ setlocal fo-=j
+ call setline(1, ['i++; // comment1', ' // comment2'])
+ normal J
+ call assert_equal('i++; // comment1 // comment2', getline(1))
+ " Test with nested comments
+ setlocal fo+=j comments=n:>,n:)
+ call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
+ normal J
+ call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
+ close!
+endfunc
+
+" Test for formatting lines where only the first line has a comment.
+func Test_comment_format_firstline_comment()
+ new
+ setlocal formatoptions=tcq
+ call setline(1, ['- one two', 'three'])
+ normal gggqG
+ call assert_equal(['- one two three'], getline(1, '$'))
+
+ %d
+ call setline(1, ['- one', '- two'])
+ normal gggqG
+ call assert_equal(['- one', '- two'], getline(1, '$'))
+ close!
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim
index 3b8a5f27ad..f13842edc8 100644
--- a/src/nvim/testdir/test_cursor_func.vim
+++ b/src/nvim/testdir/test_cursor_func.vim
@@ -22,7 +22,7 @@ func Test_move_cursor()
call cursor(3, 0)
call assert_equal([3, 1, 0, 1], getcurpos()[1:])
" below last line goes to last line
- call cursor(9, 1)
+ eval [9, 1]->cursor()
call assert_equal([4, 1, 0, 1], getcurpos()[1:])
" pass string arguments
call cursor('3', '3')
diff --git a/src/nvim/testdir/test_debugger.vim b/src/nvim/testdir/test_debugger.vim
index e038c0096a..2be94409ca 100644
--- a/src/nvim/testdir/test_debugger.vim
+++ b/src/nvim/testdir/test_debugger.vim
@@ -15,14 +15,18 @@ func CheckCWD()
endfunc
command! -nargs=0 -bar CheckCWD call CheckCWD()
+" "options" argument can contain:
+" 'msec' - time to wait for a match
+" 'match' - "pattern" to use "lines" as pattern instead of text
func CheckDbgOutput(buf, lines, options = {})
" Verify the expected output
let lnum = 20 - len(a:lines)
+ let msec = get(a:options, 'msec', 1000)
for l in a:lines
if get(a:options, 'match', 'equal') ==# 'pattern'
- call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, 200)
+ call WaitForAssert({-> assert_match(l, term_getline(a:buf, lnum))}, msec)
else
- call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, 200)
+ call WaitForAssert({-> assert_equal(l, term_getline(a:buf, lnum))}, msec)
endif
let lnum += 1
endfor
@@ -198,7 +202,7 @@ func Test_Debugger()
" Start a debug session, so that reading the last line from the terminal
" works properly.
- call RunDbgCmd(buf, ':debug echo Foo()')
+ call RunDbgCmd(buf, ':debug echo Foo()', ['cmd: echo Foo()'])
" No breakpoints
call RunDbgCmd(buf, 'breakl', ['No breakpoints defined'])
@@ -814,9 +818,10 @@ func Test_Backtrace_CmdLine()
\ '-S Xtest1.vim -c "debug call GlobalFunction()"',
\ {'wait_for_ruler': 0})
- " Need to wait for the vim-in-terminal to be ready
+ " Need to wait for the vim-in-terminal to be ready.
+ " With valgrind this can take quite long.
call CheckDbgOutput(buf, ['command line',
- \ 'cmd: call GlobalFunction()'])
+ \ 'cmd: call GlobalFunction()'], #{msec: 5000})
" At this point the ontly thing in the stack is the cmdline
call RunDbgCmd(buf, 'backtrace', [
@@ -967,14 +972,14 @@ func Test_debug_backtrace_level()
" set a breakpoint and source file1.vim
let buf = RunVimInTerminal(
\ '-c "breakadd file 1 Xtest1.vim" -S Xtest1.vim',
- \ #{ wait_for_ruler: 0 } )
+ \ #{wait_for_ruler: 0})
call CheckDbgOutput(buf, [
\ 'Breakpoint in "' .. file1 .. '" line 1',
\ 'Entering Debug mode. Type "cont" to continue.',
\ 'command line..script ' .. file1,
\ 'line 1: let s:file1_var = ''file1'''
- \ ])
+ \ ], #{msec: 5000})
" step through the initial declarations
call RunDbgCmd(buf, 'step', [ 'line 2: let g:global_var = ''global''' ] )
diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim
index 1dbbe578c5..1cb71664bd 100644
--- a/src/nvim/testdir/test_diffmode.vim
+++ b/src/nvim/testdir/test_diffmode.vim
@@ -137,7 +137,7 @@ func Common_vert_split()
" Test diffoff
diffoff!
- 1wincmd 2
+ 1wincmd w
let &diff = 1
let &fdm = diff_fdm
let &fdc = diff_fdc
@@ -744,17 +744,13 @@ func Test_diff_hlID()
call diff_hlID(-1, 1)->synIDattr("name")->assert_equal("")
- call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
call diff_hlID(1, 1)->synIDattr("name")->assert_equal("DiffChange")
- call assert_equal(diff_hlID(1, 2), hlID("DiffText"))
call diff_hlID(1, 2)->synIDattr("name")->assert_equal("DiffText")
call diff_hlID(2, 1)->synIDattr("name")->assert_equal("")
- call assert_equal(diff_hlID(3, 1), hlID("DiffAdd"))
call diff_hlID(3, 1)->synIDattr("name")->assert_equal("DiffAdd")
- call diff_hlID(4, 1)->synIDattr("name")->assert_equal("")
+ eval 4->diff_hlID(1)->synIDattr("name")->assert_equal("")
wincmd w
- call assert_equal(diff_hlID(1, 1), hlID("DiffChange"))
call assert_equal(synIDattr(diff_hlID(1, 1), "name"), "DiffChange")
call assert_equal(synIDattr(diff_hlID(2, 1), "name"), "")
call assert_equal(synIDattr(diff_hlID(3, 1), "name"), "")
diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim
index acc34e5e7c..f08dff8605 100644
--- a/src/nvim/testdir/test_digraph.vim
+++ b/src/nvim/testdir/test_digraph.vim
@@ -211,6 +211,8 @@ func Test_digraphs()
call Put_Dig("00")
call Put_Dig("el")
call assert_equal(['â€', 'ü', '∞', 'l'], getline(line('.')-3,line('.')))
+ call assert_fails('exe "digraph a\<Esc> 100"', 'E104:')
+ call assert_fails('exe "digraph \<Esc>a 100"', 'E104:')
call assert_fails('digraph xy z', 'E39:')
call assert_fails('digraph x', 'E1214:')
bw!
@@ -491,6 +493,17 @@ func Test_show_digraph_cp1251()
bwipe!
endfunc
+" Test for error in a keymap file
+func Test_loadkeymap_error()
+ if !has('keymap')
+ return
+ endif
+ call assert_fails('loadkeymap', 'E105:')
+ call writefile(['loadkeymap', 'a'], 'Xkeymap')
+ call assert_fails('source Xkeymap', 'E791:')
+ call delete('Xkeymap')
+endfunc
+
" Test for the characters displayed on the screen when entering a digraph
func Test_entering_digraph()
CheckRunVimInTerminal
diff --git a/src/nvim/testdir/test_display.vim b/src/nvim/testdir/test_display.vim
index 6938abbc28..217bb5d781 100644
--- a/src/nvim/testdir/test_display.vim
+++ b/src/nvim/testdir/test_display.vim
@@ -309,6 +309,88 @@ func Test_eob_fillchars()
close
endfunc
+" Test for 'foldopen', 'foldclose' and 'foldsep' in 'fillchars'
+func Test_fold_fillchars()
+ new
+ set fdc=2 foldenable foldmethod=manual
+ call setline(1, ['one', 'two', 'three', 'four', 'five'])
+ 2,4fold
+ " First check for the default setting for a closed fold
+ let lines = ScreenLines([1, 3], 8)
+ let expected = [
+ \ ' one ',
+ \ '+ +-- 3',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+ normal 2Gzo
+ " check the characters for an open fold
+ let lines = ScreenLines([1, 5], 8)
+ let expected = [
+ \ ' one ',
+ \ '- two ',
+ \ '| three ',
+ \ '| four ',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+
+ " change the setting
+ set fillchars=vert:\|,fold:-,eob:~,foldopen:[,foldclose:],foldsep:-
+
+ " check the characters for an open fold
+ let lines = ScreenLines([1, 5], 8)
+ let expected = [
+ \ ' one ',
+ \ '[ two ',
+ \ '- three ',
+ \ '- four ',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+
+ " check the characters for a closed fold
+ normal 2Gzc
+ let lines = ScreenLines([1, 3], 8)
+ let expected = [
+ \ ' one ',
+ \ '] +-- 3',
+ \ ' five '
+ \ ]
+ call assert_equal(expected, lines)
+
+ %bw!
+ set fillchars& fdc& foldmethod& foldenable&
+endfunc
+
+func Test_local_fillchars()
+ CheckScreendump
+
+ let lines =<< trim END
+ call setline(1, ['window 1']->repeat(3))
+ setlocal fillchars=stl:1,stlnc:a,vert:=,eob:x
+ vnew
+ call setline(1, ['window 2']->repeat(3))
+ setlocal fillchars=stl:2,stlnc:b,vert:+,eob:y
+ new
+ wincmd J
+ call setline(1, ['window 3']->repeat(3))
+ setlocal fillchars=stl:3,stlnc:c,vert:<,eob:z
+ vnew
+ call setline(1, ['window 4']->repeat(3))
+ setlocal fillchars=stl:4,stlnc:d,vert:>,eob:o
+ END
+ call writefile(lines, 'Xdisplayfillchars')
+ let buf = RunVimInTerminal('-S Xdisplayfillchars', #{rows: 12})
+ call VerifyScreenDump(buf, 'Test_display_fillchars_1', {})
+
+ call term_sendkeys(buf, ":wincmd k\r")
+ call VerifyScreenDump(buf, 'Test_display_fillchars_2', {})
+
+ call StopVimInTerminal(buf)
+ call delete('Xdisplayfillchars')
+endfunc
+
func Test_display_linebreak_breakat()
new
vert resize 25
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index a09346a595..679b877ef6 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -262,22 +262,6 @@ func Test_edit_09()
bw!
endfunc
-func Test_edit_10()
- " Test for starting selectmode
- new
- set selectmode=key keymodel=startsel
- call setline(1, ['abc', 'def', 'ghi'])
- call cursor(1, 4)
- call feedkeys("A\<s-home>start\<esc>", 'txin')
- call assert_equal(['startdef', 'ghi'], getline(1, '$'))
- " start select mode again with gv
- set selectmode=cmd
- call feedkeys('gvabc', 'xt')
- call assert_equal('abctdef', getline(1))
- set selectmode= keymodel=
- bw!
-endfunc
-
func Test_edit_11()
" Test that indenting kicks in
new
@@ -729,23 +713,32 @@ endfunc
func Test_edit_CTRL_N()
" Check keyword completion
- new
- set complete=.
- call setline(1, ['INFER', 'loWER', '', '', ])
- call cursor(3, 1)
- call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix")
- call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
- call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$'))
- %d
- call setline(1, ['INFER', 'loWER', '', '', ])
- call cursor(3, 1)
- set ignorecase infercase
- call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix")
- call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
- call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$'))
-
- set noignorecase noinfercase complete&
- bw!
+ " for e in ['latin1', 'utf-8']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+ new
+ set complete=.
+ call setline(1, ['INFER', 'loWER', '', '', ])
+ call cursor(3, 1)
+ call feedkeys("Ai\<c-n>\<cr>\<esc>", "tnix")
+ call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['INFER', 'loWER', 'i', 'LO', '', ''], getline(1, '$'), e)
+ %d
+ call setline(1, ['INFER', 'loWER', '', '', ])
+ call cursor(3, 1)
+ set ignorecase infercase
+ call feedkeys("Ii\<c-n>\<cr>\<esc>", "tnix")
+ call feedkeys("ILO\<c-n>\<cr>\<esc>", 'tnix')
+ call assert_equal(['INFER', 'loWER', 'infer', 'LOWER', '', ''], getline(1, '$'), e)
+ set noignorecase noinfercase
+ %d
+ call setline(1, ['one word', 'two word'])
+ exe "normal! Goo\<C-P>\<C-X>\<C-P>"
+ call assert_equal('one word', getline(3))
+ %d
+ set complete&
+ bw!
+ endfor
endfunc
func Test_edit_CTRL_O()
@@ -909,6 +902,24 @@ func Test_edit_CTRL_T()
bw!
endfunc
+" Test thesaurus completion with different encodings
+func Test_thesaurus_complete_with_encoding()
+ call writefile(['angry furious mad enraged'], 'Xthesaurus')
+ set thesaurus=Xthesaurus
+ " for e in ['latin1', 'utf-8']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+ new
+ call setline(1, 'mad')
+ call cursor(1, 1)
+ call feedkeys("A\<c-x>\<c-t>\<cr>\<esc>", 'tnix')
+ call assert_equal(['mad', ''], getline(1, '$'))
+ bw!
+ endfor
+ set thesaurus=
+ call delete('Xthesaurus')
+endfunc
+
" Test 'thesaurusfunc'
func MyThesaurus(findstart, base)
let mythesaurus = [
@@ -1620,6 +1631,7 @@ func Test_edit_InsertLeave_undo()
bwipe!
au! InsertLeave
call delete('XtestUndo')
+ call delete(undofile('XtestUndo'))
set undofile&
endfunc
@@ -1687,11 +1699,11 @@ func Test_edit_noesckeys()
endfunc
" Test for running an invalid ex command in insert mode using CTRL-O
-" Note that vim has a hard-coded sleep of 3 seconds. So this test will take
-" more than 3 seconds to complete.
func Test_edit_ctrl_o_invalid_cmd()
new
set showmode showcmd
+ " Avoid a sleep of 3 seconds. Zero might have side effects.
+ " call test_override('ui_delay', 50)
let caught_e492 = 0
try
call feedkeys("i\<C-O>:invalid\<CR>abc\<Esc>", "xt")
@@ -1701,6 +1713,18 @@ func Test_edit_ctrl_o_invalid_cmd()
call assert_equal(1, caught_e492)
call assert_equal('abc', getline(1))
set showmode& showcmd&
+ " call test_override('ui_delay', 0)
+ close!
+endfunc
+
+" Test for editing a file with a very long name
+func Test_edit_illegal_filename()
+ CheckEnglish
+ new
+ redir => msg
+ exe 'edit ' . repeat('f', 5000)
+ redir END
+ call assert_match("Illegal file name$", split(msg, "\n")[0])
close!
endfunc
@@ -1763,6 +1787,102 @@ func Test_edit_is_a_directory()
call delete(dirname, 'rf')
endfunc
+" Test for editing a file using invalid file encoding
+func Test_edit_invalid_encoding()
+ CheckEnglish
+ call writefile([], 'Xfile')
+ redir => msg
+ new ++enc=axbyc Xfile
+ redir END
+ call assert_match('\[NOT converted\]', msg)
+ call delete('Xfile')
+ close!
+endfunc
+
+" Test for the "charconvert" option
+func Test_edit_charconvert()
+ CheckEnglish
+ call writefile(['one', 'two'], 'Xfile')
+
+ " set 'charconvert' to a non-existing function
+ set charconvert=NonExitingFunc()
+ new
+ let caught_e117 = v:false
+ try
+ redir => msg
+ edit ++enc=axbyc Xfile
+ catch /E117:/
+ let caught_e117 = v:true
+ finally
+ redir END
+ endtry
+ call assert_true(caught_e117)
+ call assert_equal(['one', 'two'], getline(1, '$'))
+ call assert_match("Conversion with 'charconvert' failed", msg)
+ close!
+ set charconvert&
+
+ " 'charconvert' function doesn't create a output file
+ func Cconv1()
+ endfunc
+ set charconvert=Cconv1()
+ new
+ redir => msg
+ edit ++enc=axbyc Xfile
+ redir END
+ call assert_equal(['one', 'two'], getline(1, '$'))
+ call assert_match("can't read output of 'charconvert'", msg)
+ close!
+ delfunc Cconv1
+ set charconvert&
+
+ " 'charconvert' function to convert to upper case
+ func Cconv2()
+ let data = readfile(v:fname_in)
+ call map(data, 'toupper(v:val)')
+ call writefile(data, v:fname_out)
+ endfunc
+ set charconvert=Cconv2()
+ new Xfile
+ write ++enc=ucase Xfile1
+ call assert_equal(['ONE', 'TWO'], readfile('Xfile1'))
+ call delete('Xfile1')
+ close!
+ delfunc Cconv2
+ set charconvert&
+
+ " 'charconvert' function removes the input file
+ func Cconv3()
+ call delete(v:fname_in)
+ endfunc
+ set charconvert=Cconv3()
+ new
+ call assert_fails('edit ++enc=lcase Xfile', 'E202:')
+ call assert_equal([''], getline(1, '$'))
+ close!
+ delfunc Cconv3
+ set charconvert&
+
+ call delete('Xfile')
+endfunc
+
+" Test for editing a file without read permission
+func Test_edit_file_no_read_perm()
+ CheckUnix
+ CheckNotBSD
+ call writefile(['one', 'two'], 'Xfile')
+ call setfperm('Xfile', '-w-------')
+ new
+ redir => msg
+ edit Xfile
+ redir END
+ call assert_equal(1, &readonly)
+ call assert_equal([''], getline(1, '$'))
+ call assert_match('\[Permission Denied\]', msg)
+ close!
+ call delete('Xfile')
+endfunc
+
" Using :edit without leaving 'insertmode' should not cause Insert mode to be
" re-entered immediately after <C-L>
func Test_edit_insertmode_ex_edit()
diff --git a/src/nvim/testdir/test_environ.vim b/src/nvim/testdir/test_environ.vim
index dd34983ee5..d8344817f5 100644
--- a/src/nvim/testdir/test_environ.vim
+++ b/src/nvim/testdir/test_environ.vim
@@ -28,6 +28,26 @@ func Test_setenv()
call assert_equal(v:null, getenv('TEST ENV'))
endfunc
+func Test_special_env()
+ " The value for $HOME is cached internally by Vim, ensure the value is up to
+ " date.
+ let orig_ENV = $HOME
+
+ let $HOME = 'foo'
+ call assert_equal('foo', expand('~'))
+ " old $HOME value is kept until a new one is set
+ unlet $HOME
+ call assert_equal('foo', expand('~'))
+
+ call setenv('HOME', 'bar')
+ call assert_equal('bar', expand('~'))
+ " old $HOME value is kept until a new one is set
+ call setenv('HOME', v:null)
+ call assert_equal('bar', expand('~'))
+
+ let $HOME = orig_ENV
+endfunc
+
func Test_external_env()
call setenv('FOO', 'HelloWorld')
if has('win32')
diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim
index 811c6c946d..eff1376d3c 100644
--- a/src/nvim/testdir/test_eval_stuff.vim
+++ b/src/nvim/testdir/test_eval_stuff.vim
@@ -75,6 +75,18 @@ func Test_for_invalid()
redraw
endfunc
+func Test_for_over_null_string()
+ let save_enc = &enc
+ " set enc=iso8859
+ let cnt = 0
+ for c in v:_null_string
+ let cnt += 1
+ endfor
+ call assert_equal(0, cnt)
+
+ let &enc = save_enc
+endfunc
+
func Test_readfile_binary()
new
call setline(1, ['one', 'two', 'three'])
diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim
index dac7a6989d..9a9e5c546b 100644
--- a/src/nvim/testdir/test_excmd.vim
+++ b/src/nvim/testdir/test_excmd.vim
@@ -568,10 +568,12 @@ endfunc
" Test for the :verbose command
func Test_verbose_cmd()
- call assert_equal([' verbose=1'], split(execute('verbose set vbs'), "\n"))
+ set verbose=3
+ call assert_match(' verbose=1\n\s*Last set from ', execute('verbose set vbs'), "\n")
call assert_equal([' verbose=0'], split(execute('0verbose set vbs'), "\n"))
- let l = execute("4verbose set verbose | set verbose")
- call assert_equal([' verbose=4', ' verbose=0'], split(l, "\n"))
+ set verbose=0
+ call assert_match(' verbose=4\n\s*Last set from .*\n verbose=0',
+ \ execute("4verbose set verbose | set verbose"))
endfunc
" Test for the :delete command and the related abbreviated commands
diff --git a/src/nvim/testdir/test_exit.vim b/src/nvim/testdir/test_exit.vim
index befcaec2b2..37be293950 100644
--- a/src/nvim/testdir/test_exit.vim
+++ b/src/nvim/testdir/test_exit.vim
@@ -95,7 +95,7 @@ func Test_exit_code()
[CODE]
if RunVim(before, ['quit'], '')
- call assert_equal(['qp = null', 'ep = null', 'lp = 0', 'l = 0'], readfile('Xtestout'))
+ call assert_equal(['qp = v:null', 'ep = v:null', 'lp = 0', 'l = 0'], readfile('Xtestout'))
endif
call delete('Xtestout')
diff --git a/src/nvim/testdir/test_expand.vim b/src/nvim/testdir/test_expand.vim
index 383921bb82..aa131a49ff 100644
--- a/src/nvim/testdir/test_expand.vim
+++ b/src/nvim/testdir/test_expand.vim
@@ -1,6 +1,7 @@
" Test for expanding file names
source shared.vim
+source check.vim
func Test_with_directories()
call mkdir('Xdir1')
@@ -77,10 +78,11 @@ func Test_expandcmd()
edit a1a2a3.rb
call assert_equal('make b1b2b3.rb a1a2a3 Xfile.o', expandcmd('make %:gs?a?b? %< #<.o'))
- call assert_fails('call expandcmd("make <afile>")', 'E495:')
- call assert_fails('call expandcmd("make <afile>")', 'E495:')
+ call assert_equal('make <afile>', expandcmd("make <afile>"))
+ call assert_equal('make <amatch>', expandcmd("make <amatch>"))
+ call assert_equal('make <abuf>', expandcmd("make <abuf>"))
enew
- call assert_fails('call expandcmd("make %")', 'E499:')
+ call assert_equal('make %', expandcmd("make %"))
let $FOO="blue\tsky"
call setline(1, "$FOO")
call assert_equal("grep pat blue\tsky", expandcmd('grep pat <cfile>'))
@@ -93,6 +95,11 @@ func Test_expandcmd()
let $FOO= "foo bar baz"
call assert_equal("e foo bar baz", expandcmd("e $FOO"))
+ if has('unix')
+ " test for using the shell to expand a command argument
+ call assert_equal('{1..4}', expandcmd('{1..4}'))
+ endif
+
unlet $FOO
close!
endfunc
@@ -100,22 +107,30 @@ endfunc
" Test for expanding <sfile>, <slnum> and <sflnum> outside of sourcing a script
func Test_source_sfile()
let lines =<< trim [SCRIPT]
- :call assert_fails('echo expandcmd("<sfile>")', 'E498:')
- :call assert_fails('echo expandcmd("<slnum>")', 'E842:')
- :call assert_fails('echo expandcmd("<sflnum>")', 'E961:')
- :call assert_fails('call expandcmd("edit <cfile>")', 'E446:')
- :call assert_fails('call expandcmd("edit #")', 'E194:')
- :call assert_fails('call expandcmd("edit #<2")', 'E684:')
- :call assert_fails('call expandcmd("edit <cword>")', 'E348:')
- :call assert_fails('call expandcmd("edit <cexpr>")', 'E348:')
+ :call assert_equal('<sfile>', expandcmd("<sfile>"))
+ :call assert_equal('<slnum>', expandcmd("<slnum>"))
+ :call assert_equal('<sflnum>', expandcmd("<sflnum>"))
+ :call assert_equal('edit <cfile>', expandcmd("edit <cfile>"))
+ :call assert_equal('edit #', expandcmd("edit #"))
+ :call assert_equal('edit #<2', expandcmd("edit #<2"))
+ :call assert_equal('edit <cword>', expandcmd("edit <cword>"))
+ :call assert_equal('edit <cexpr>', expandcmd("edit <cexpr>"))
:call assert_fails('autocmd User MyCmd echo "<sfile>"', 'E498:')
+ :
+ :call assert_equal('', expand('<script>'))
+ :verbose echo expand('<script>')
+ :call add(v:errors, v:errmsg)
+ :verbose echo expand('<sfile>')
+ :call add(v:errors, v:errmsg)
:call writefile(v:errors, 'Xresult')
:qall!
-
[SCRIPT]
call writefile(lines, 'Xscript')
if RunVim([], [], '--clean -s Xscript')
- call assert_equal([], readfile('Xresult'))
+ call assert_equal([
+ \ 'E1274: No script file name to substitute for "<script>"',
+ \ 'E498: no :source file name to substitute for "<sfile>"'],
+ \ readfile('Xresult'))
endif
call delete('Xscript')
call delete('Xresult')
@@ -131,7 +146,72 @@ func Test_expand_filename_multicmd()
call assert_equal(4, winnr('$'))
call assert_equal('foo!', bufname(winbufnr(1)))
call assert_equal('foo', bufname(winbufnr(2)))
+ call assert_fails('e %:s/.*//', 'E500:')
%bwipe!
endfunc
+func Test_expandcmd_shell_nonomatch()
+ CheckNotMSWindows
+ call assert_equal('$*', expandcmd('$*'))
+endfunc
+
+func Test_expand_script_source()
+ let lines0 =<< trim [SCRIPT]
+ call extend(g:script_level, [expand('<script>:t')])
+ so Xscript1
+ func F0()
+ call extend(g:func_level, [expand('<script>:t')])
+ endfunc
+
+ au User * call extend(g:au_level, [expand('<script>:t')])
+ [SCRIPT]
+
+ let lines1 =<< trim [SCRIPT]
+ call extend(g:script_level, [expand('<script>:t')])
+ so Xscript2
+ func F1()
+ call extend(g:func_level, [expand('<script>:t')])
+ endfunc
+
+ au User * call extend(g:au_level, [expand('<script>:t')])
+ [SCRIPT]
+
+ let lines2 =<< trim [SCRIPT]
+ call extend(g:script_level, [expand('<script>:t')])
+ func F2()
+ call extend(g:func_level, [expand('<script>:t')])
+ endfunc
+
+ au User * call extend(g:au_level, [expand('<script>:t')])
+ [SCRIPT]
+
+ call writefile(lines0, 'Xscript0')
+ call writefile(lines1, 'Xscript1')
+ call writefile(lines2, 'Xscript2')
+
+ " Check the expansion of <script> at different levels.
+ let g:script_level = []
+ let g:func_level = []
+ let g:au_level = []
+
+ so Xscript0
+ call F0()
+ call F1()
+ call F2()
+ doautocmd User
+
+ call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:script_level)
+ call assert_equal(['Xscript0', 'Xscript1', 'Xscript2'], g:func_level)
+ call assert_equal(['Xscript2', 'Xscript1', 'Xscript0'], g:au_level)
+
+ unlet g:script_level g:func_level
+ delfunc F0
+ delfunc F1
+ delfunc F2
+
+ call delete('Xscript0')
+ call delete('Xscript1')
+ call delete('Xscript2')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_expand_func.vim b/src/nvim/testdir/test_expand_func.vim
index b48c2e8a19..80bfdb8553 100644
--- a/src/nvim/testdir/test_expand_func.vim
+++ b/src/nvim/testdir/test_expand_func.vim
@@ -37,17 +37,54 @@ func Test_expand_sflnum()
delcommand Flnum
endfunc
-func Test_expand_sfile()
+func Test_expand_sfile_and_stack()
call assert_match('test_expand_func\.vim$', s:sfile)
- call assert_match('^function .*\.\.Test_expand_sfile$', expand('<sfile>'))
+ let expected = 'script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack'
+ call assert_match(expected .. '$', expand('<sfile>'))
+ call assert_match(expected .. '\[4\]$' , expand('<stack>'))
" Call in script-local function
- call assert_match('^function .*\.\.Test_expand_sfile\[5\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
+ call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack\[7\]\.\.<SNR>\d\+_expand_sfile$', s:expand_sfile())
" Call in command
command Sfile echo expand('<sfile>')
- call assert_match('^function .*\.\.Test_expand_sfile$', trim(execute('Sfile')))
+ call assert_match('script .*testdir/runtest.vim\[\d\+\]\.\.function RunTheTest\[\d\+\]\.\.Test_expand_sfile_and_stack$', trim(execute('Sfile')))
delcommand Sfile
+
+ " Use <stack> from sourced script.
+ let lines =<< trim END
+ " comment here
+ let g:stack_value = expand('<stack>')
+ END
+ call writefile(lines, 'Xstack')
+ source Xstack
+ call assert_match('\<Xstack\[2\]$', g:stack_value)
+ unlet g:stack_value
+ call delete('Xstack')
+
+ if exists('+shellslash')
+ call mkdir('Xshellslash')
+ let lines =<< trim END
+ let g:stack1 = expand('<stack>')
+ set noshellslash
+ let g:stack2 = expand('<stack>')
+ set shellslash
+ let g:stack3 = expand('<stack>')
+ END
+ call writefile(lines, 'Xshellslash/Xstack')
+ " Test that changing 'shellslash' always affects the result of expand()
+ " when sourcing a script multiple times.
+ for i in range(2)
+ source Xshellslash/Xstack
+ call assert_match('\<Xshellslash/Xstack\[1\]$', g:stack1)
+ call assert_match('\<Xshellslash\\Xstack\[3\]$', g:stack2)
+ call assert_match('\<Xshellslash/Xstack\[5\]$', g:stack3)
+ unlet g:stack1
+ unlet g:stack2
+ unlet g:stack3
+ endfor
+ call delete('Xshellslash', 'rf')
+ endif
endfunc
func Test_expand_slnum()
@@ -70,10 +107,15 @@ endfunc
func Test_expand()
new
- call assert_equal("", expand('%:S'))
+ call assert_equal("", expand('%:S'))
call assert_equal('3', '<slnum>'->expand())
call assert_equal(['4'], expand('<slnum>', v:false, v:true))
" Don't add any line above this, otherwise <slnum> will change.
+ call assert_equal("", expand('%'))
+ set verbose=1
+ call assert_equal("", expand('%'))
+ set verbose=0
+ call assert_equal("", expand('%:p'))
quit
endfunc
diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim
index 5b10e691e5..15622cd6fe 100644
--- a/src/nvim/testdir/test_expr.vim
+++ b/src/nvim/testdir/test_expr.vim
@@ -547,6 +547,7 @@ func Test_funcref()
call assert_fails('echo funcref("{")', 'E475:')
let OneByRef = funcref("One", repeat(["foo"], 20))
call assert_fails('let OneByRef = funcref("One", repeat(["foo"], 21))', 'E118:')
+ call assert_fails('echo function("min") =~ function("min")', 'E694:')
endfunc
func Test_setmatches()
diff --git a/src/nvim/testdir/test_filechanged.vim b/src/nvim/testdir/test_filechanged.vim
index c6e781a1ef..fef0eb732f 100644
--- a/src/nvim/testdir/test_filechanged.vim
+++ b/src/nvim/testdir/test_filechanged.vim
@@ -140,7 +140,8 @@ func Test_FileChangedShell_edit()
endfunc
func Test_FileChangedShell_edit_dialog()
- throw 'Skipped: requires a UI to be active'
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/filechanged_spec.lua'
CheckNotGui
CheckUnix " Using low level feedkeys() does not work on MS-Windows.
@@ -190,7 +191,8 @@ func Test_FileChangedShell_edit_dialog()
endfunc
func Test_file_changed_dialog()
- throw 'Skipped: requires a UI to be active'
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/legacy/filechanged_spec.lua'
CheckUnix
CheckNotGui
au! FileChangedShell
@@ -242,6 +244,15 @@ func Test_file_changed_dialog()
call assert_equal(1, line('$'))
call assert_equal('new line', getline(1))
+ " File created after starting to edit it
+ call delete('Xchanged_d')
+ new Xchanged_d
+ call writefile(['one'], 'Xchanged_d')
+ call feedkeys('L', 'L')
+ checktime Xchanged_d
+ call assert_equal(['one'], getline(1, '$'))
+ close!
+
bwipe!
call delete('Xchanged_d')
endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index eedad15e9e..68ce9148a4 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -71,6 +71,7 @@ let s:filename_checks = {
\ 'asciidoc': ['file.asciidoc', 'file.adoc'],
\ 'asn': ['file.asn', 'file.asn1'],
\ 'asterisk': ['asterisk/file.conf', 'asterisk/file.conf-file', 'some-asterisk/file.conf', 'some-asterisk/file.conf-file'],
+ \ 'astro': ['file.astro'],
\ 'atlas': ['file.atl', 'file.as'],
\ 'autohotkey': ['file.ahk'],
\ 'autoit': ['file.au3'],
@@ -208,6 +209,7 @@ let s:filename_checks = {
\ 'gdmo': ['file.mo', 'file.gdmo'],
\ 'gdresource': ['file.tscn', 'file.tres'],
\ 'gdscript': ['file.gd'],
+ \ 'gdshader': ['file.gdshader', 'file.shader'],
\ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'],
\ 'gemtext': ['file.gmi', 'file.gemini'],
\ 'gift': ['file.gift'],
@@ -360,7 +362,7 @@ let s:filename_checks = {
\ 'monk': ['file.isc', 'file.monk', 'file.ssc', 'file.tsc'],
\ 'moo': ['file.moo'],
\ 'moonscript': ['file.moon'],
- \ 'mp': ['file.mp'],
+ \ 'mp': ['file.mp', 'file.mpxl', 'file.mpiv', 'file.mpvi'],
\ 'mplayerconf': ['mplayer.conf', '/.mplayer/config', 'any/.mplayer/config'],
\ 'mrxvtrc': ['mrxvtrc', '.mrxvtrc'],
\ 'msidl': ['file.odl', 'file.mof'],
@@ -441,6 +443,7 @@ let s:filename_checks = {
\ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'],
\ 'ql': ['file.ql', 'file.qll'],
\ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'],
+ \ 'quarto': ['file.qmd'],
\ 'r': ['file.r'],
\ 'radiance': ['file.rad', 'file.mat'],
\ 'raku': ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'],
@@ -586,6 +589,9 @@ let s:filename_checks = {
\ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'],
\ 'vala': ['file.vala'],
\ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'],
+ \ 'vdmpp': ['file.vpp', 'file.vdmpp'],
+ \ 'vdmrt': ['file.vdmrt'],
+ \ 'vdmsl': ['file.vdm', 'file.vdmsl'],
\ 'vera': ['file.vr', 'file.vri', 'file.vrh'],
\ 'verilog': ['file.v'],
\ 'verilogams': ['file.va', 'file.vams'],
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index c11e7b4fea..147eda5b0a 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1420,12 +1420,15 @@ func Test_trim()
call assert_equal("vim", trim(" vim ", " ", 0))
call assert_equal("vim ", trim(" vim ", " ", 1))
call assert_equal(" vim", trim(" vim ", " ", 2))
- call assert_fails('call trim(" vim ", " ", [])', 'E745:')
- call assert_fails('call trim(" vim ", " ", -1)', 'E475:')
- call assert_fails('call trim(" vim ", " ", 3)', 'E475:')
+ call assert_fails('eval trim(" vim ", " ", [])', 'E745:')
+ call assert_fails('eval trim(" vim ", " ", -1)', 'E475:')
+ call assert_fails('eval trim(" vim ", " ", 3)', 'E475:')
+ call assert_fails('eval trim(" vim ", 0)', 'E475:')
let chars = join(map(range(1, 0x20) + [0xa0], {n -> n->nr2char()}), '')
call assert_equal("x", trim(chars . "x" . chars))
+
+ call assert_fails('let c=trim([])', 'E730:')
endfunc
" Test for reg_recording() and reg_executing()
@@ -1697,6 +1700,63 @@ func Test_platform_name()
endif
endfunc
+" Test confirm({msg} [, {choices} [, {default} [, {type}]]])
+func Test_confirm()
+ " requires a UI to be active
+ throw 'Skipped: use test/functional/vimscript/input_spec.lua'
+ if !has('unix') || has('gui_running')
+ return
+ endif
+
+ call feedkeys('o', 'L')
+ let a = confirm('Press O to proceed')
+ call assert_equal(1, a)
+
+ call feedkeys('y', 'L')
+ let a = 'Are you sure?'->confirm("&Yes\n&No")
+ call assert_equal(1, a)
+
+ call feedkeys('n', 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(2, a)
+
+ " confirm() should return 0 when pressing CTRL-C.
+ call feedkeys("\<C-c>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(0, a)
+
+ " <Esc> requires another character to avoid it being seen as the start of an
+ " escape sequence. Zero should be harmless.
+ eval "\<Esc>0"->feedkeys('L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(0, a)
+
+ " Default choice is returned when pressing <CR>.
+ call feedkeys("\<CR>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No")
+ call assert_equal(1, a)
+
+ call feedkeys("\<CR>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No", 2)
+ call assert_equal(2, a)
+
+ call feedkeys("\<CR>", 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No", 0)
+ call assert_equal(0, a)
+
+ " Test with the {type} 4th argument
+ for type in ['Error', 'Question', 'Info', 'Warning', 'Generic']
+ call feedkeys('y', 'L')
+ let a = confirm('Are you sure?', "&Yes\n&No\n", 1, type)
+ call assert_equal(1, a)
+ endfor
+
+ call assert_fails('call confirm([])', 'E730:')
+ call assert_fails('call confirm("Are you sure?", [])', 'E730:')
+ call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", [])', 'E745:')
+ call assert_fails('call confirm("Are you sure?", "&Yes\n&No\n", 0, [])', 'E730:')
+endfunc
+
func Test_readdir()
call mkdir('Xdir')
call writefile([], 'Xdir/foo.txt')
@@ -1724,7 +1784,7 @@ func Test_readdir()
let files = readdir('Xdir', {x -> len(add(l, x)) == 2 ? -1 : 1})
call assert_equal(1, len(files))
- call delete('Xdir', 'rf')
+ eval 'Xdir'->delete('rf')
endfunc
func Test_delete_rf()
@@ -1767,6 +1827,16 @@ endfunc
func Test_char2nr()
call assert_equal(12354, char2nr('ã‚', 1))
+ call assert_equal(120, 'x'->char2nr())
+endfunc
+
+func Test_charclass()
+ call assert_equal(0, charclass(' '))
+ call assert_equal(1, charclass('.'))
+ call assert_equal(2, charclass('x'))
+ call assert_equal(3, charclass("\u203c"))
+ " this used to crash vim
+ call assert_equal(0, "xxx"[-1]->charclass())
endfunc
func Test_eventhandler()
@@ -1810,6 +1880,22 @@ func Test_bufadd_bufload()
exe 'bwipe ' .. buf2
call assert_equal(0, bufexists(buf2))
+ " When 'buftype' is "nofile" then bufload() does not read the file.
+ " Other values too.
+ for val in [['nofile', 0],
+ \ ['nowrite', 1],
+ \ ['acwrite', 1],
+ \ ['quickfix', 0],
+ \ ['help', 1],
+ \ ['prompt', 0],
+ \ ]
+ bwipe! XotherName
+ let buf = bufadd('XotherName')
+ call setbufvar(buf, '&bt', val[0])
+ call bufload(buf)
+ call assert_equal(val[1] ? ['some', 'text'] : [''], getbufline(buf, 1, '$'), val[0])
+ endfor
+
bwipe someName
bwipe XotherName
call assert_equal(0, bufexists('someName'))
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index feae44e5ee..b2bb189688 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -191,6 +191,22 @@ func Test_gf_error()
au! InsertCharPre
bwipe!
+
+ " gf is not allowed when buffer is locked
+ new
+ augroup Test_gf
+ au!
+ au OptionSet diff norm! gf
+ augroup END
+ call setline(1, ['Xfile1', 'line2', 'line3', 'line4'])
+ " Nvim does not support test_override()
+ " call test_override('starting', 1)
+ " call assert_fails('diffthis', 'E788:')
+ " call test_override('starting', 0)
+ augroup Test_gf
+ au!
+ augroup END
+ bw!
endfunc
" If a file is not found by 'gf', then 'includeexpr' should be used to locate
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index 947f7efc7c..cb6851250c 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -9,7 +9,10 @@ func Test_yank_put_clipboard()
set clipboard=unnamed
g/^/normal yyp
call assert_equal(['a', 'a', 'b', 'b', 'c', 'c'], getline(1, 6))
-
+ set clipboard=unnamed,unnamedplus
+ call setline(1, ['a', 'b', 'c'])
+ g/^/normal yyp
+ call assert_equal(['a', 'a', 'b', 'b', 'c', 'c'], getline(1, 6))
set clipboard&
bwipe!
endfunc
diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim
index 49095400ef..6d029ffda2 100644
--- a/src/nvim/testdir/test_goto.vim
+++ b/src/nvim/testdir/test_goto.vim
@@ -122,6 +122,24 @@ func Test_gd()
call XTest_goto_decl('gd', lines, 3, 14)
endfunc
+" Using gd to jump to a declaration in a fold
+func Test_gd_with_fold()
+ new
+ let lines =<< trim END
+ #define ONE 1
+ #define TWO 2
+ #define THREE 3
+
+ TWO
+ END
+ call setline(1, lines)
+ 1,3fold
+ call feedkeys('Ggd', 'xt')
+ call assert_equal(2, line('.'))
+ call assert_equal(-1, foldclosedend(2))
+ bw!
+endfunc
+
func Test_gd_not_local()
let lines =<< trim [CODE]
int func1(void)
diff --git a/src/nvim/testdir/test_highlight.vim b/src/nvim/testdir/test_highlight.vim
index efdf44a0d6..8e808a00d0 100644
--- a/src/nvim/testdir/test_highlight.vim
+++ b/src/nvim/testdir/test_highlight.vim
@@ -731,7 +731,8 @@ func Test_1_highlight_Normalgroup_exists()
endif
endfunc
-function Test_no_space_before_xxx()
+" Do this test last, sometimes restoring the columns doesn't work
+func Test_z_no_space_before_xxx()
" Note: we need to create this highlight group in the test because it does not exist in Neovim
execute('hi StatusLineTermNC ctermfg=green')
let l:org_columns = &columns
@@ -739,7 +740,7 @@ function Test_no_space_before_xxx()
let l:hi_StatusLineTermNC = join(split(execute('hi StatusLineTermNC')))
call assert_match('StatusLineTermNC xxx', l:hi_StatusLineTermNC)
let &columns = l:org_columns
-endfunction
+endfunc
" Test for :highlight command errors
func Test_highlight_cmd_errors()
diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim
index feb521e232..f1c31dee04 100644
--- a/src/nvim/testdir/test_history.vim
+++ b/src/nvim/testdir/test_history.vim
@@ -95,6 +95,23 @@ function Test_History()
call assert_fails('call histnr([])', 'E730:')
call assert_fails('history xyz', 'E488:')
call assert_fails('history ,abc', 'E488:')
+ call assert_fails('call histdel(":", "\\%(")', 'E53:')
+endfunction
+
+function Test_history_truncates_long_entry()
+ " History entry short enough to fit on the screen should not be truncated.
+ call histadd(':', 'echo x' .. repeat('y', &columns - 17) .. 'z')
+ let a = execute('history : -1')
+
+ call assert_match("^\n # cmd history\n"
+ \ .. "> *\\d\\+ echo x" .. repeat('y', &columns - 17) .. 'z$', a)
+
+ " Long history entry should be truncated to fit on the screen, with, '...'
+ " inserted in the string to indicate the that there is truncation.
+ call histadd(':', 'echo x' .. repeat('y', &columns - 16) .. 'z')
+ let a = execute('history : -1')
+ call assert_match("^\n # cmd history\n"
+ \ .. "> *\\d\\+ echo xy\\+\.\.\.y\\+z$", a)
endfunction
function Test_Search_history_window()
diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim
index 179218e48a..3e563f29f9 100644
--- a/src/nvim/testdir/test_ins_complete.vim
+++ b/src/nvim/testdir/test_ins_complete.vim
@@ -44,11 +44,11 @@ func Test_ins_complete()
exe "normal o\<C-X>\<C-P>\<C-P>\<C-X>\<C-X>\<C-N>\<C-X>\<C-N>\<C-N>"
call assert_equal('run1 run2', getline('.'))
- set cpt=.,w,i
+ set cpt=.,\ ,w,i
" i-add-expands and switches to local
exe "normal OM\<C-N>\<C-X>\<C-N>\<C-X>\<C-N>\<C-X>\<C-X>\<C-X>\<C-P>"
call assert_equal("Makefile\tto\trun3", getline('.'))
- " add-expands lines (it would end in an empty line if it didn't ignored
+ " add-expands lines (it would end in an empty line if it didn't ignore
" itself)
exe "normal o\<C-X>\<C-L>\<C-X>\<C-L>\<C-P>\<C-P>"
call assert_equal("Makefile\tto\trun3", getline('.'))
@@ -68,6 +68,11 @@ func Test_ins_complete()
call assert_equal('Xtest11.one', getline('.'))
normal ddk
+ " Test for expanding a non-existing filename
+ exe "normal oa1b2X3Y4\<C-X>\<C-F>"
+ call assert_equal('a1b2X3Y4', getline('.'))
+ normal ddk
+
set cpt=w
" checks make_cyclic in other window
exe "normal oST\<C-N>\<C-P>\<C-P>\<C-P>\<C-P>"
@@ -682,6 +687,21 @@ func Test_complete_func_error()
call assert_equal([], complete_info(['items']).items)
endfunc
+" Test for recursively starting completion mode using complete()
+func Test_recursive_complete_func()
+ func ListColors()
+ call complete(5, ["red", "blue"])
+ return ''
+ endfunc
+ new
+ call setline(1, ['a1', 'a2'])
+ set complete=.
+ exe "normal Goa\<C-X>\<C-L>\<C-R>=ListColors()\<CR>\<C-N>"
+ call assert_equal('a2blue', getline(3))
+ delfunc ListColors
+ bw!
+endfunc
+
" Test for completing words following a completed word in a line
func Test_complete_wrapscan()
" complete words from another buffer
@@ -721,6 +741,17 @@ func Test_complete_across_line()
close!
endfunc
+" Test for completing words with a '.' at the end of a word.
+func Test_complete_joinspaces()
+ new
+ call setline(1, ['one two.', 'three. four'])
+ set joinspaces
+ exe "normal Goon\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
+ call assert_equal("one two. three. four", getline(3))
+ set joinspaces&
+ bw!
+endfunc
+
" Test for using CTRL-L to add one character when completing matching
func Test_complete_add_onechar()
new
@@ -741,6 +772,39 @@ func Test_complete_add_onechar()
close!
endfunc
+" Test for using CTRL-X CTRL-L to complete whole lines lines
+func Test_complete_wholeline()
+ new
+ " complete one-line
+ call setline(1, ['a1', 'a2'])
+ exe "normal ggoa\<C-X>\<C-L>"
+ call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
+ " go to the next match (wrapping around the buffer)
+ exe "normal 2GCa\<C-X>\<C-L>\<C-N>"
+ call assert_equal(['a1', 'a', 'a2'], getline(1, '$'))
+ " go to the next match
+ exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>"
+ call assert_equal(['a1', 'a2', 'a2'], getline(1, '$'))
+ exe "normal 2GCa\<C-X>\<C-L>\<C-N>\<C-N>\<C-N>"
+ call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
+ " repeat the test using CTRL-L
+ " go to the next match (wrapping around the buffer)
+ exe "normal 2GCa\<C-X>\<C-L>\<C-L>"
+ call assert_equal(['a1', 'a2', 'a2'], getline(1, '$'))
+ " go to the next match
+ exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>"
+ call assert_equal(['a1', 'a', 'a2'], getline(1, '$'))
+ exe "normal 2GCa\<C-X>\<C-L>\<C-L>\<C-L>\<C-L>"
+ call assert_equal(['a1', 'a1', 'a2'], getline(1, '$'))
+ %d
+ " use CTRL-X CTRL-L to add one more line
+ call setline(1, ['a1', 'b1'])
+ setlocal complete=.
+ exe "normal ggOa\<C-X>\<C-L>\<C-X>\<C-L>\<C-X>\<C-L>"
+ call assert_equal(['a1', 'b1', '', 'a1', 'b1'], getline(1, '$'))
+ bw!
+endfunc
+
" Test insert completion with 'cindent' (adjust the indent)
func Test_complete_with_cindent()
new
@@ -829,6 +893,25 @@ func Test_complete_stop()
close!
endfunc
+" Test for typing CTRL-R in insert completion mode to insert a register
+" content.
+func Test_complete_reginsert()
+ new
+ call setline(1, ['a1', 'a12', 'a123', 'a1234'])
+
+ " if a valid CTRL-X mode key is returned from <C-R>=, then it should be
+ " processed. Otherwise, CTRL-X mode should be stopped and the key should be
+ " inserted.
+ exe "normal Goa\<C-P>\<C-R>=\"\\<C-P>\"\<CR>"
+ call assert_equal('a123', getline(5))
+ let @r = "\<C-P>\<C-P>"
+ exe "normal GCa\<C-P>\<C-R>r"
+ call assert_equal('a12', getline(5))
+ exe "normal GCa\<C-P>\<C-R>=\"x\"\<CR>"
+ call assert_equal('a1234x', getline(5))
+ bw!
+endfunc
+
func Test_issue_7021()
CheckMSWindows
@@ -842,6 +925,322 @@ func Test_issue_7021()
set completeslash=
endfunc
+" Test for 'longest' setting in 'completeopt' with latin1 and utf-8 encodings
+func Test_complete_longest_match()
+ " for e in ['latin1', 'utf-8']
+ for e in ['utf-8']
+ exe 'set encoding=' .. e
+ new
+ set complete=.
+ set completeopt=menu,longest
+ call setline(1, ['pfx_a1', 'pfx_a12', 'pfx_a123', 'pfx_b1'])
+ exe "normal Gopfx\<C-P>"
+ call assert_equal('pfx_', getline(5))
+ bw!
+ endfor
+
+ " Test for completing additional words with longest match set
+ new
+ call setline(1, ['abc1', 'abd2'])
+ exe "normal Goab\<C-P>\<C-X>\<C-P>"
+ call assert_equal('ab', getline(3))
+ bw!
+ set complete& completeopt&
+endfunc
+
+" Test for removing the first displayed completion match and selecting the
+" match just before that.
+func Test_complete_erase_firstmatch()
+ new
+ call setline(1, ['a12', 'a34', 'a56'])
+ set complete=.
+ exe "normal Goa\<C-P>\<BS>\<BS>3\<CR>"
+ call assert_equal('a34', getline('$'))
+ set complete&
+ bw!
+endfunc
+
+" Test for completing words from unloaded buffers
+func Test_complete_from_unloadedbuf()
+ call writefile(['abc'], "Xfile1")
+ call writefile(['def'], "Xfile2")
+ edit Xfile1
+ edit Xfile2
+ new | close
+ enew
+ bunload Xfile1 Xfile2
+ set complete=u
+ " complete from an unloaded buffer
+ exe "normal! ia\<C-P>"
+ call assert_equal('abc', getline(1))
+ exe "normal! od\<C-P>"
+ call assert_equal('def', getline(2))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+ call delete("Xfile2")
+endfunc
+
+" Test for completing whole lines from unloaded buffers
+func Test_complete_wholeline_unloadedbuf()
+ call writefile(['a line1', 'a line2', 'a line3'], "Xfile1")
+ edit Xfile1
+ enew
+ set complete=u
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a line2', getline(1))
+ %d
+ " completing from an unlisted buffer should fail
+ bdel Xfile1
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a', getline(1))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+endfunc
+
+" Test for completing words from unlisted buffers
+func Test_complete_from_unlistedbuf()
+ call writefile(['abc'], "Xfile1")
+ call writefile(['def'], "Xfile2")
+ edit Xfile1
+ edit Xfile2
+ new | close
+ bdel Xfile1 Xfile2
+ set complete=U
+ " complete from an unlisted buffer
+ exe "normal! ia\<C-P>"
+ call assert_equal('abc', getline(1))
+ exe "normal! od\<C-P>"
+ call assert_equal('def', getline(2))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+ call delete("Xfile2")
+endfunc
+
+" Test for completing whole lines from unlisted buffers
+func Test_complete_wholeline_unlistedbuf()
+ call writefile(['a line1', 'a line2', 'a line3'], "Xfile1")
+ edit Xfile1
+ enew
+ set complete=U
+ " completing from a unloaded buffer should fail
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a', getline(1))
+ %d
+ bdel Xfile1
+ exe "normal! ia\<C-X>\<C-L>\<C-P>"
+ call assert_equal('a line2', getline(1))
+ set complete&
+ %bw!
+ call delete("Xfile1")
+endfunc
+
+" Test for adding a multibyte character using CTRL-L in completion mode
+func Test_complete_mbyte_char_add()
+ new
+ set complete=.
+ call setline(1, 'abÄ—')
+ exe "normal! oa\<C-P>\<BS>\<BS>\<C-L>\<C-L>"
+ call assert_equal('abÄ—', getline(2))
+ " Test for a leader with multibyte character
+ %d
+ call setline(1, 'abÄ—Ä•')
+ exe "normal! oabÄ—\<C-P>"
+ call assert_equal('abÄ—Ä•', getline(2))
+ bw!
+endfunc
+
+" Test for using <C-X><C-P> for local expansion even if 'complete' is set to
+" not to complete matches from the local buffer. Also test using multiple
+" <C-X> to cancel the current completion mode.
+func Test_complete_local_expansion()
+ new
+ set complete=t
+ call setline(1, ['abc', 'def'])
+ exe "normal! Go\<C-X>\<C-P>"
+ call assert_equal("def", getline(3))
+ exe "normal! Go\<C-P>"
+ call assert_equal("", getline(4))
+ exe "normal! Go\<C-X>\<C-N>"
+ call assert_equal("abc", getline(5))
+ exe "normal! Go\<C-N>"
+ call assert_equal("", getline(6))
+
+ " use multiple <C-X> to cancel the previous completion mode
+ exe "normal! Go\<C-P>\<C-X>\<C-P>"
+ call assert_equal("", getline(7))
+ exe "normal! Go\<C-P>\<C-X>\<C-X>\<C-P>"
+ call assert_equal("", getline(8))
+ exe "normal! Go\<C-P>\<C-X>\<C-X>\<C-X>\<C-P>"
+ call assert_equal("abc", getline(9))
+
+ " interrupt the current completion mode
+ set completeopt=menu,noinsert
+ exe "normal! Go\<C-X>\<C-F>\<C-X>\<C-X>\<C-P>\<C-Y>"
+ call assert_equal("abc", getline(10))
+
+ " when only one <C-X> is used to interrupt, do normal expansion
+ exe "normal! Go\<C-X>\<C-F>\<C-X>\<C-P>"
+ call assert_equal("", getline(11))
+ set completeopt&
+
+ " using two <C-X> in non-completion mode and restarting the same mode
+ exe "normal! God\<C-X>\<C-X>\<C-P>\<C-X>\<C-X>\<C-P>\<C-Y>"
+ call assert_equal("def", getline(12))
+
+ " test for adding a match from the original empty text
+ %d
+ call setline(1, 'abc def g')
+ exe "normal! o\<C-X>\<C-P>\<C-N>\<C-X>\<C-P>"
+ call assert_equal('def', getline(2))
+ exe "normal! 0C\<C-X>\<C-N>\<C-P>\<C-X>\<C-N>"
+ call assert_equal('abc', getline(2))
+
+ bw!
+endfunc
+
+" Test for undoing changes after a insert-mode completion
+func Test_complete_undo()
+ new
+ set complete=.
+ " undo with 'ignorecase'
+ call setline(1, ['ABOVE', 'BELOW'])
+ set ignorecase
+ exe "normal! Goab\<C-G>u\<C-P>"
+ call assert_equal("ABOVE", getline(3))
+ undo
+ call assert_equal("ab", getline(3))
+ set ignorecase&
+ %d
+ " undo with longest match
+ set completeopt=menu,longest
+ call setline(1, ['above', 'about'])
+ exe "normal! Goa\<C-G>u\<C-P>"
+ call assert_equal("abo", getline(3))
+ undo
+ call assert_equal("a", getline(3))
+ set completeopt&
+ %d
+ " undo for line completion
+ call setline(1, ['above that change', 'below that change'])
+ exe "normal! Goabove\<C-G>u\<C-X>\<C-L>"
+ call assert_equal("above that change", getline(3))
+ undo
+ call assert_equal("above", getline(3))
+
+ bw!
+endfunc
+
+" Test for completing a very long word
+func Test_complete_long_word()
+ set complete&
+ new
+ call setline(1, repeat('x', 950) .. ' one two three')
+ exe "normal! Gox\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
+ call assert_equal(repeat('x', 950) .. ' one two three', getline(2))
+ %d
+ " should fail when more than 950 characters are in a word
+ call setline(1, repeat('x', 951) .. ' one two three')
+ exe "normal! Gox\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>\<C-X>\<C-P>"
+ call assert_equal(repeat('x', 951), getline(2))
+
+ " Test for adding a very long word to an existing completion
+ %d
+ call setline(1, ['abc', repeat('x', 1016) .. '012345'])
+ exe "normal! Goab\<C-P>\<C-X>\<C-P>"
+ call assert_equal('abc ' .. repeat('x', 1016) .. '0123', getline(3))
+ bw!
+endfunc
+
+" Test for some fields in the complete items used by complete()
+func Test_complete_items()
+ func CompleteItems(idx)
+ let items = [[#{word: "one", dup: 1, user_data: 'u1'}, #{word: "one", dup: 1, user_data: 'u2'}],
+ \ [#{word: "one", dup: 0, user_data: 'u3'}, #{word: "one", dup: 0, user_data: 'u4'}],
+ \ [#{word: "one", icase: 1, user_data: 'u7'}, #{word: "oNE", icase: 1, user_data: 'u8'}],
+ \ [#{user_data: 'u9'}],
+ \ [#{word: "", user_data: 'u10'}],
+ \ [#{word: "", empty: 1, user_data: 'u11'}]]
+ call complete(col('.'), items[a:idx])
+ return ''
+ endfunc
+ new
+ exe "normal! i\<C-R>=CompleteItems(0)\<CR>\<C-N>\<C-Y>"
+ call assert_equal('u2', v:completed_item.user_data)
+ call assert_equal('one', getline(1))
+ exe "normal! o\<C-R>=CompleteItems(1)\<CR>\<C-Y>"
+ call assert_equal('u3', v:completed_item.user_data)
+ call assert_equal('one', getline(2))
+ exe "normal! o\<C-R>=CompleteItems(1)\<CR>\<C-N>"
+ call assert_equal('', getline(3))
+ set completeopt=menu,noinsert
+ exe "normal! o\<C-R>=CompleteItems(2)\<CR>one\<C-N>\<C-Y>"
+ call assert_equal('oNE', getline(4))
+ call assert_equal('u8', v:completed_item.user_data)
+ set completeopt&
+ exe "normal! o\<C-R>=CompleteItems(3)\<CR>"
+ call assert_equal('', getline(5))
+ exe "normal! o\<C-R>=CompleteItems(4)\<CR>"
+ call assert_equal('', getline(6))
+ exe "normal! o\<C-R>=CompleteItems(5)\<CR>"
+ call assert_equal('', getline(7))
+ call assert_equal('u11', v:completed_item.user_data)
+ " pass invalid argument to complete()
+ let cmd = "normal! o\<C-R>=complete(1, [[]])\<CR>"
+ call assert_fails('exe cmd', 'E730:')
+ bw!
+ delfunc CompleteItems
+endfunc
+
+" Test for the "refresh" item in the dict returned by an insert completion
+" function
+func Test_complete_item_refresh_always()
+ let g:CallCount = 0
+ func! Tcomplete(findstart, base)
+ if a:findstart
+ " locate the start of the word
+ let line = getline('.')
+ let start = col('.') - 1
+ while start > 0 && line[start - 1] =~ '\a'
+ let start -= 1
+ endwhile
+ return start
+ else
+ let g:CallCount += 1
+ let res = ["update1", "update12", "update123"]
+ return #{words: res, refresh: 'always'}
+ endif
+ endfunc
+ new
+ set completeopt=menu,longest
+ set completefunc=Tcomplete
+ exe "normal! iup\<C-X>\<C-U>\<BS>\<BS>\<BS>\<BS>\<BS>"
+ call assert_equal('up', getline(1))
+ call assert_equal(2, g:CallCount)
+ set completeopt&
+ set completefunc&
+ bw!
+ delfunc Tcomplete
+endfunc
+
+" Test for completing from a thesaurus file without read permission
+func Test_complete_unreadable_thesaurus_file()
+ CheckUnix
+ CheckNotRoot
+
+ call writefile(['about', 'above'], 'Xfile')
+ call setfperm('Xfile', '---r--r--')
+ new
+ set complete=sXfile
+ exe "normal! ia\<C-P>"
+ call assert_equal('a', getline(1))
+ bw!
+ call delete('Xfile')
+ set complete&
+endfunc
+
" Test to ensure 'Scanning...' messages are not recorded in messages history
func Test_z1_complete_no_history()
new
@@ -884,4 +1283,52 @@ func Test_complete_smartindent()
delfunction! FooBarComplete
endfunc
+func Test_complete_overrun()
+ " this was going past the end of the copied text
+ new
+ sil norm si”0s0 
+ bwipe!
+endfunc
+
+func Test_infercase_very_long_line()
+ " this was truncating the line when inferring case
+ new
+ let longLine = "blah "->repeat(300)
+ let verylongLine = "blah "->repeat(400)
+ call setline(1, verylongLine)
+ call setline(2, longLine)
+ set ic infercase
+ exe "normal 2Go\<C-X>\<C-L>\<Esc>"
+ call assert_equal(longLine, getline(3))
+
+ " check that the too long text is NUL terminated
+ %del
+ norm o
+ norm 1987ax
+ exec "norm ox\<C-X>\<C-L>"
+ call assert_equal(repeat('x', 1987), getline(3))
+
+ bwipe!
+ set noic noinfercase
+endfunc
+
+func Test_ins_complete_add()
+ " this was reading past the end of allocated memory
+ new
+ norm o
+ norm 7o€€
+ sil! norm o
+
+ bwipe!
+endfunc
+
+func Test_ins_complete_end_of_line()
+ " this was reading past the end of the line
+ new
+ norm 8o€ý 
+ sil! norm o
+
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim
index c1fe47d1c9..c178c87d3e 100644
--- a/src/nvim/testdir/test_lambda.vim
+++ b/src/nvim/testdir/test_lambda.vim
@@ -308,3 +308,21 @@ func Test_lambda_error()
" This was causing a crash
call assert_fails('ec{@{->{d->()()', 'E15')
endfunc
+
+func Test_closure_error()
+ let l =<< trim END
+ func F1() closure
+ return 1
+ endfunc
+ END
+ call writefile(l, 'Xscript')
+ let caught_932 = 0
+ try
+ source Xscript
+ catch /E932:/
+ let caught_932 = 1
+ endtry
+ call assert_equal(1, caught_932)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_listdict.vim b/src/nvim/testdir/test_listdict.vim
index aa66d86af1..08c415a069 100644
--- a/src/nvim/testdir/test_listdict.vim
+++ b/src/nvim/testdir/test_listdict.vim
@@ -604,20 +604,23 @@ func Test_reverse_sort_uniq()
call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l)))
call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l))
call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l)))
- call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l))
- call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l)))
- call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l))))
- call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l)))
-
- let l=[7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four']
- call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n'))
-
- let l=[7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []]
- call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
- call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
- call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
+ if has('float')
+ call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l))
+ call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l)))
+ call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l))))
+ call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l)))
+
+ let l = [7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four']
+ call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n'))
+
+ let l = [7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []]
+ call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1))
+ call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i'))
+ call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l)))
+ endif
call assert_fails('call reverse("")', 'E899:')
+ call assert_fails('call uniq([1, 2], {x, y -> []})', 'E882:')
endfunc
" reduce a list or a blob
@@ -649,6 +652,8 @@ func Test_reduce()
call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce(0, { acc, val -> acc + val }, 1)", 'E897:')
call assert_fails("call reduce('', { acc, val -> acc + val }, 1)", 'E897:')
+ call assert_fails("call reduce([1, 2], 'Xdoes_not_exist')", 'E117:')
+ call assert_fails("echo reduce(0z01, { acc, val -> 2 * acc + val }, '')", 'E39:')
let g:lut = [1, 2, 3, 4]
func EvilRemove()
diff --git a/src/nvim/testdir/test_maparg.vim b/src/nvim/testdir/test_maparg.vim
index dad4c81a7b..17c5f433ac 100644
--- a/src/nvim/testdir/test_maparg.vim
+++ b/src/nvim/testdir/test_maparg.vim
@@ -248,6 +248,8 @@ func Test_mapset()
bwipe!
call assert_fails('call mapset([], v:false, {})', 'E730:')
+ call assert_fails('call mapset("i", 0, "")', 'E715:')
+ call assert_fails('call mapset("i", 0, {})', 'E460:')
endfunc
func Check_ctrlb_map(d, check_alt)
diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim
index a02d23b409..3a607ff533 100644
--- a/src/nvim/testdir/test_messages.vim
+++ b/src/nvim/testdir/test_messages.vim
@@ -95,6 +95,65 @@ func Test_echoerr()
call test_ignore_error('RESET')
endfunc
+func Test_mode_message_at_leaving_insert_by_ctrl_c()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+
+ " Set custom statusline built by user-defined function.
+ let testfile = 'Xtest.vim'
+ call writefile([
+ \ 'func StatusLine() abort',
+ \ ' return ""',
+ \ 'endfunc',
+ \ 'set statusline=%!StatusLine()',
+ \ 'set laststatus=2',
+ \ ], testfile)
+
+ let rows = 10
+ let buf = term_start([GetVimProg(), '--clean', '-S', testfile], {'term_rows': rows})
+ call term_wait(buf, 200)
+ call assert_equal('run', job_status(term_getjob(buf)))
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, rows))})
+ call term_sendkeys(buf, "\<C-C>")
+ call WaitForAssert({-> assert_match('^\s*$', term_getline(buf, rows))})
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe!'
+ call delete(testfile)
+endfunc
+
+func Test_mode_message_at_leaving_insert_with_esc_mapped()
+ if !has('terminal') || has('gui_running')
+ return
+ endif
+
+ " Set custom statusline built by user-defined function.
+ let testfile = 'Xtest.vim'
+ call writefile([
+ \ 'set laststatus=2',
+ \ 'inoremap <Esc> <Esc>00',
+ \ ], testfile)
+
+ let rows = 10
+ let buf = term_start([GetVimProg(), '--clean', '-S', testfile], {'term_rows': rows})
+ call term_wait(buf, 200)
+ call assert_equal('run', job_status(term_getjob(buf)))
+
+ call term_sendkeys(buf, "i")
+ call WaitForAssert({-> assert_match('^-- INSERT --\s*$', term_getline(buf, rows))})
+ call term_sendkeys(buf, "\<Esc>")
+ call WaitForAssert({-> assert_match('^\s*$', term_getline(buf, rows))})
+
+ call term_sendkeys(buf, ":qall!\<CR>")
+ call WaitForAssert({-> assert_equal('dead', job_status(term_getjob(buf)))})
+ exe buf . 'bwipe!'
+ call delete(testfile)
+endfunc
+
func Test_echospace()
set noruler noshowcmd laststatus=1
call assert_equal(&columns - 1, v:echospace)
@@ -317,6 +376,7 @@ func Test_fileinfo_after_echo()
endfunc
func Test_cmdheight_zero()
+ enew
set cmdheight=0
set showcmd
redraw!
@@ -366,10 +426,13 @@ func Test_cmdheight_zero()
7
call feedkeys(":\"\<C-R>=line('w0')\<CR>\<CR>", "xt")
call assert_equal('"1', @:)
- bwipe!
+ bwipe!
+ bwipe!
set cmdheight&
set showcmd&
+ tabnew
+ tabonly
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim
index 7cb70aa2af..4f842189b6 100644
--- a/src/nvim/testdir/test_normal.vim
+++ b/src/nvim/testdir/test_normal.vim
@@ -3,6 +3,7 @@
source shared.vim
source check.vim
source view_util.vim
+source screendump.vim
func Setup_NewWindow()
10new
@@ -123,31 +124,6 @@ func Test_normal01_keymodel()
bw!
endfunc
-" Test for select mode
-func Test_normal02_selectmode()
- call Setup_NewWindow()
- 50
- norm! gHy
- call assert_equal('y51', getline('.'))
- call setline(1, range(1,100))
- 50
- exe ":norm! V9jo\<c-g>y"
- call assert_equal('y60', getline('.'))
- " clean up
- bw!
-endfunc
-
-func Test_normal02_selectmode2()
- " some basic select mode tests
- call Setup_NewWindow()
- 50
- " call feedkeys(":set im\n\<c-o>gHc\<c-o>:set noim\n", 'tx')
- call feedkeys("i\<c-o>gHc\<esc>", 'tx')
- call assert_equal('c51', getline('.'))
- " clean up
- bw!
-endfunc
-
func Test_normal03_join()
" basic join test
call Setup_NewWindow()
@@ -376,6 +352,70 @@ func Test_normal09a_operatorfunc()
norm V10j,,
call assert_equal(22, g:a)
+ " Use a lambda function for 'opfunc'
+ unmap <buffer> ,,
+ call cursor(1, 1)
+ let g:a=0
+ nmap <buffer><silent> ,, :set opfunc={type\ ->\ CountSpaces(type)}<CR>g@
+ vmap <buffer><silent> ,, :<C-U>call CountSpaces(visualmode(), 1)<CR>
+ 50
+ norm V2j,,
+ call assert_equal(6, g:a)
+ norm V,,
+ call assert_equal(2, g:a)
+ norm ,,l
+ call assert_equal(0, g:a)
+ 50
+ exe "norm 0\<c-v>10j2l,,"
+ call assert_equal(11, g:a)
+ 50
+ norm V10j,,
+ call assert_equal(22, g:a)
+
+ " use a partial function for 'opfunc'
+ let g:OpVal = 0
+ func! Test_opfunc1(x, y, type)
+ let g:OpVal = a:x + a:y
+ endfunc
+ set opfunc=function('Test_opfunc1',\ [5,\ 7])
+ normal! g@l
+ call assert_equal(12, g:OpVal)
+ " delete the function and try to use g@
+ delfunc Test_opfunc1
+ call test_garbagecollect_now()
+ call assert_fails('normal! g@l', 'E117:')
+ set opfunc=
+
+ " use a funcref for 'opfunc'
+ let g:OpVal = 0
+ func! Test_opfunc2(x, y, type)
+ let g:OpVal = a:x + a:y
+ endfunc
+ set opfunc=funcref('Test_opfunc2',\ [4,\ 3])
+ normal! g@l
+ call assert_equal(7, g:OpVal)
+ " delete the function and try to use g@
+ delfunc Test_opfunc2
+ call test_garbagecollect_now()
+ call assert_fails('normal! g@l', 'E933:')
+ set opfunc=
+
+ " Try to use a function with two arguments for 'operatorfunc'
+ let g:OpVal = 0
+ func! Test_opfunc3(x, y)
+ let g:OpVal = 4
+ endfunc
+ set opfunc=Test_opfunc3
+ call assert_fails('normal! g@l', 'E119:')
+ call assert_equal(0, g:OpVal)
+ set opfunc=
+ delfunc Test_opfunc3
+ unlet g:OpVal
+
+ " Try to use a lambda function with two arguments for 'operatorfunc'
+ set opfunc={x,\ y\ ->\ 'done'}
+ call assert_fails('normal! g@l', 'E119:')
+
" clean up
unmap <buffer> ,,
set opfunc=
@@ -491,6 +531,18 @@ func Test_normal11_showcmd()
call assert_equal(3, line('$'))
exe "norm! 0d3\<del>2l"
call assert_equal('obar2foobar3', getline('.'))
+ " test for the visual block size displayed in the status line
+ call setline(1, ['aaaaa', 'bbbbb', 'ccccc'])
+ call feedkeys("ggl\<C-V>lljj", 'xt')
+ redraw!
+ call assert_match('3x3$', Screenline(&lines))
+ call feedkeys("\<C-V>", 'xt')
+ " test for visually selecting a multi-byte character
+ call setline(1, ["\U2206"])
+ call feedkeys("ggv", 'xt')
+ redraw!
+ call assert_match('1-3$', Screenline(&lines))
+ call feedkeys("v", 'xt')
bw!
endfunc
@@ -654,6 +706,19 @@ func Test_normal15_z_scroll_vert()
call assert_equal(21, winsaveview()['topline'])
call assert_equal([0, 21, 2, 0, 9], getcurpos())
+ " Test for z+ with [count] greater than buffer size
+ 1
+ norm! 1000z+
+ call assert_equal(' 100', getline('.'))
+ call assert_equal(100, winsaveview()['topline'])
+ call assert_equal([0, 100, 2, 0, 9], getcurpos())
+
+ " Test for z+ from the last buffer line
+ norm! Gz.z+
+ call assert_equal(' 100', getline('.'))
+ call assert_equal(100, winsaveview()['topline'])
+ call assert_equal([0, 100, 2, 0, 9], getcurpos())
+
" Test for z^
norm! 22z+0
norm! z^
@@ -661,6 +726,12 @@ func Test_normal15_z_scroll_vert()
call assert_equal(12, winsaveview()['topline'])
call assert_equal([0, 21, 2, 0, 9], getcurpos())
+ " Test for z^ from first buffer line
+ norm! ggz^
+ call assert_equal('1', getline('.'))
+ call assert_equal(1, winsaveview()['topline'])
+ call assert_equal([0, 1, 1, 0, 1], getcurpos())
+
" Test for [count]z^
1
norm! 30z^
@@ -740,6 +811,19 @@ func Test_normal16_z_scroll_hor()
norm! yl
call assert_equal('z', @0)
+ " Test for zs and ze with folds
+ %fold
+ norm! $zs
+ call assert_equal(26, col('.'))
+ call assert_equal(0, winsaveview()['leftcol'])
+ norm! yl
+ call assert_equal('z', @0)
+ norm! ze
+ call assert_equal(26, col('.'))
+ call assert_equal(0, winsaveview()['leftcol'])
+ norm! yl
+ call assert_equal('z', @0)
+
" cleanup
set wrap listchars=eol:$
bw!
@@ -833,6 +917,19 @@ func Test_vert_scroll_cmds()
normal! 4H
call assert_equal(33, line('.'))
+ " Test for using a large count value
+ %d
+ call setline(1, range(1, 4))
+ norm! 6H
+ call assert_equal(4, line('.'))
+
+ " Test for 'M' with folded lines
+ %d
+ call setline(1, range(1, 20))
+ 1,5fold
+ norm! LM
+ call assert_equal(12, line('.'))
+
" Test for the CTRL-E and CTRL-Y commands with folds
%d
call setline(1, range(1, 10))
@@ -851,6 +948,18 @@ func Test_vert_scroll_cmds()
exe "normal \<C-Y>\<C-Y>"
call assert_equal(h + 1, line('w$'))
+ " Test for CTRL-Y from the first line and CTRL-E from the last line
+ %d
+ set scrolloff=2
+ call setline(1, range(1, 4))
+ exe "normal gg\<C-Y>"
+ call assert_equal(1, line('w0'))
+ call assert_equal(1, line('.'))
+ exe "normal G4\<C-E>\<C-E>"
+ call assert_equal(4, line('w$'))
+ call assert_equal(4, line('.'))
+ set scrolloff&
+
" Using <PageUp> and <PageDown> in an empty buffer should beep
%d
call assert_beeps('exe "normal \<PageUp>"')
@@ -899,6 +1008,18 @@ func Test_vert_scroll_cmds()
exe "normal \<C-D>"
call assert_equal(50, line('w0'))
+ " Test for <S-CR>. Page down.
+ %d
+ call setline(1, range(1, 100))
+ call feedkeys("\<S-CR>", 'xt')
+ call assert_equal(14, line('w0'))
+ call assert_equal(28, line('w$'))
+
+ " Test for <S-->. Page up.
+ call feedkeys("\<S-->", 'xt')
+ call assert_equal(1, line('w0'))
+ call assert_equal(15, line('w$'))
+
set foldenable&
close!
endfunc
@@ -1213,6 +1334,13 @@ func Test_normal18_z_fold()
norm! j
call assert_equal('55', getline('.'))
+ " Test for zm with a count
+ 50
+ set foldlevel=2
+ norm! 3zm
+ call assert_equal(0, &foldlevel)
+ call assert_equal(49, foldclosed(line('.')))
+
" Test for zM
48
set nofoldenable foldlevel=99
@@ -1327,6 +1455,7 @@ func Test_normal21_nv_hat()
edit Xfoo | %bw
call assert_fails(':buffer #', 'E86')
call assert_fails(':execute "normal! \<C-^>"', 'E23')
+ call assert_fails("normal i\<C-R>#", 'E23:')
" Test for the expected behavior when switching between two named buffers.
edit Xfoo | edit Xbar
@@ -1420,6 +1549,15 @@ func Test_normal23_K()
set iskeyword-=%
set iskeyword-=\|
+ " Currently doesn't work in Nvim, see #19436
+ " Test for specifying a count to K
+ " 1
+ " com! -nargs=* Kprog let g:Kprog_Args = <q-args>
+ " set keywordprg=:Kprog
+ " norm! 3K
+ " call assert_equal('3 version8', g:Kprog_Args)
+ " delcom Kprog
+
" Only expect "man" to work on Unix
if !has("unix") || has('nvim') " Nvim K uses :terminal. #15398
let &keywordprg = k
@@ -1867,7 +2005,31 @@ func Test_normal29_brace()
bw!
endfunc
-" Test for ~ command
+" Test for section movements
+func Test_normal_section()
+ new
+ let lines =<< trim [END]
+ int foo()
+ {
+ if (1)
+ {
+ a = 1;
+ }
+ }
+ [END]
+ call setline(1, lines)
+
+ " jumping to a folded line using [[ should open the fold
+ 2,3fold
+ call cursor(5, 1)
+ call feedkeys("[[", 'xt')
+ call assert_equal(2, line('.'))
+ call assert_equal(-1, foldclosedend(line('.')))
+
+ close!
+endfunc
+
+" Test for changing case using u, U, gu, gU and ~ (tilde) commands
func Test_normal30_changecase()
new
call append(0, 'This is a simple test: äüöß')
@@ -1887,6 +2049,9 @@ func Test_normal30_changecase()
call assert_equal('this is a SIMPLE TEST: ÄÜÖSS', getline('.'))
norm! V~
call assert_equal('THIS IS A simple test: äüöss', getline('.'))
+ call assert_beeps('norm! c~')
+ %d
+ call assert_beeps('norm! ~')
" Test for changing case across lines using 'whichwrap'
call setline(1, ['aaaaaa', 'aaaaaa'])
@@ -2038,9 +2203,9 @@ func Test_normal33_g_cmd2()
call assert_equal(2, line('.'))
call assert_fails(':norm! g;', 'E662')
call assert_fails(':norm! g,', 'E663')
- let &ul=&ul
+ let &ul = &ul
call append('$', ['a', 'b', 'c', 'd'])
- let &ul=&ul
+ let &ul = &ul
call append('$', ['Z', 'Y', 'X', 'W'])
let a = execute(':changes')
call assert_match('2\s\+0\s\+2', a)
@@ -2889,6 +3054,20 @@ func Test_message_when_using_ctrl_c()
bwipe!
endfunc
+func Test_mode_updated_after_ctrl_c()
+ CheckScreendump
+
+ let buf = RunVimInTerminal('', {'rows': 5})
+ call term_sendkeys(buf, "i")
+ call term_sendkeys(buf, "\<C-O>")
+ " wait a moment so that the "-- (insert) --" message is displayed
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "\<C-C>")
+ call VerifyScreenDump(buf, 'Test_mode_updated_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test for '[m', ']m', '[M' and ']M'
" Jumping to beginning and end of methods in Java-like languages
func Test_java_motion()
@@ -2897,25 +3076,26 @@ func Test_java_motion()
call assert_beeps('normal! ]m')
call assert_beeps('normal! [M')
call assert_beeps('normal! ]M')
- a
-Piece of Java
-{
- tt m1 {
- t1;
- } e1
-
- tt m2 {
- t2;
- } e2
-
- tt m3 {
- if (x)
- {
- t3;
- }
- } e3
-}
-.
+ let lines =<< trim [CODE]
+ Piece of Java
+ {
+ tt m1 {
+ t1;
+ } e1
+
+ tt m2 {
+ t2;
+ } e2
+
+ tt m3 {
+ if (x)
+ {
+ t3;
+ }
+ } e3
+ }
+ [CODE]
+ call setline(1, lines)
normal gg
@@ -2968,14 +3148,21 @@ Piece of Java
call assert_equal("{LF", getline('.'))
call assert_equal([2, 2, 2], [line('.'), col('.'), virtcol('.')])
+ call cursor(2, 1)
+ call assert_beeps('norm! 5]m')
+
+ " jumping to a method in a fold should open the fold
+ 6,10fold
+ call feedkeys("gg3]m", 'xt')
+ call assert_equal([7, 8, 15], [line('.'), col('.'), virtcol('.')])
+ call assert_equal(-1, foldclosedend(7))
+
close!
endfunc
+" Tests for g cmds
func Test_normal_gdollar_cmd()
- if !has("jumplist")
- return
- endif
- " Tests for g cmds
+ CheckFeature jumplist
call Setup_NewWindow()
" Make long lines that will wrap
%s/$/\=repeat(' foobar', 10)/
@@ -3183,6 +3370,27 @@ func Test_normal_colon_op()
close!
endfunc
+" Test for deleting or changing characters across lines with 'whichwrap'
+" containing 's'. Should count <EOL> as one character.
+func Test_normal_op_across_lines()
+ new
+ set whichwrap&
+ call setline(1, ['one two', 'three four'])
+ exe "norm! $3d\<Space>"
+ call assert_equal(['one twhree four'], getline(1, '$'))
+
+ call setline(1, ['one two', 'three four'])
+ exe "norm! $3c\<Space>x"
+ call assert_equal(['one twxhree four'], getline(1, '$'))
+
+ set whichwrap+=l
+ call setline(1, ['one two', 'three four'])
+ exe "norm! $3x"
+ call assert_equal(['one twhree four'], getline(1, '$'))
+ close!
+ set whichwrap&
+endfunc
+
" Test for 'w' and 'b' commands
func Test_normal_word_move()
new
@@ -3256,6 +3464,19 @@ func Test_normal_vert_scroll_longline()
close!
endfunc
+" Test for jumping in a file using %
+func Test_normal_percent_jump()
+ new
+ call setline(1, range(1, 100))
+
+ " jumping to a folded line should open the fold
+ 25,75fold
+ call feedkeys('50%', 'xt')
+ call assert_equal(50, line('.'))
+ call assert_equal(-1, foldclosedend(50))
+ close!
+endfunc
+
" Some commands like yy, cc, dd, >>, << and !! accept a count after
" typing the first letter of the command.
func Test_normal_count_after_operator()
diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim
index b10f0f5030..655d537336 100644
--- a/src/nvim/testdir/test_options.vim
+++ b/src/nvim/testdir/test_options.vim
@@ -234,6 +234,7 @@ func Test_complete()
new
call feedkeys("i\<C-N>\<Esc>", 'xt')
bwipe!
+ call assert_fails('set complete=ix', 'E535:')
set complete&
endfun
@@ -368,9 +369,17 @@ func Test_set_errors()
call assert_fails('set sessionoptions=curdir,sesdir', 'E474:')
call assert_fails('set foldmarker={{{,', 'E474:')
call assert_fails('set sessionoptions=sesdir,curdir', 'E474:')
- call assert_fails('set listchars=trail:· ambiwidth=double', 'E834:')
+ setlocal listchars=trail:·
+ call assert_fails('set ambiwidth=double', 'E834:')
+ setlocal listchars=trail:-
+ setglobal listchars=trail:·
+ call assert_fails('set ambiwidth=double', 'E834:')
set listchars&
- call assert_fails('set fillchars=stl:· ambiwidth=double', 'E835:')
+ setlocal fillchars=stl:·
+ call assert_fails('set ambiwidth=double', 'E835:')
+ setlocal fillchars=stl:-
+ setglobal fillchars=stl:·
+ call assert_fails('set ambiwidth=double', 'E835:')
set fillchars&
call assert_fails('set fileencoding=latin1,utf-8', 'E474:')
set nomodifiable
@@ -423,32 +432,37 @@ func Test_copy_context()
endfunc
func Test_set_ttytype()
- " Nvim does not support 'ttytype'.
- if !has('nvim') && !has('gui_running') && has('unix')
- " Setting 'ttytype' used to cause a double-free when exiting vim and
- " when vim is compiled with -DEXITFREE.
- set ttytype=ansi
- call assert_equal('ansi', &ttytype)
- call assert_equal(&ttytype, &term)
- set ttytype=xterm
- call assert_equal('xterm', &ttytype)
- call assert_equal(&ttytype, &term)
- try
- set ttytype=
- call assert_report('set ttytype= did not fail')
- catch /E529/
- endtry
-
- " Some systems accept any terminal name and return dumb settings,
- " check for failure of finding the entry and for missing 'cm' entry.
- try
- set ttytype=xxx
- call assert_report('set ttytype=xxx did not fail')
- catch /E522\|E437/
- endtry
-
- set ttytype&
- call assert_equal(&ttytype, &term)
+ throw "Skipped: Nvim does not support 'ttytype'"
+ CheckUnix
+ CheckNotGui
+
+ " Setting 'ttytype' used to cause a double-free when exiting vim and
+ " when vim is compiled with -DEXITFREE.
+ set ttytype=ansi
+ call assert_equal('ansi', &ttytype)
+ call assert_equal(&ttytype, &term)
+ set ttytype=xterm
+ call assert_equal('xterm', &ttytype)
+ call assert_equal(&ttytype, &term)
+ try
+ set ttytype=
+ call assert_report('set ttytype= did not fail')
+ catch /E529/
+ endtry
+
+ " Some systems accept any terminal name and return dumb settings,
+ " check for failure of finding the entry and for missing 'cm' entry.
+ try
+ set ttytype=xxx
+ call assert_report('set ttytype=xxx did not fail')
+ catch /E522\|E437/
+ endtry
+
+ set ttytype&
+ call assert_equal(&ttytype, &term)
+
+ if has('gui') && !has('gui_running')
+ call assert_fails('set term=gui', 'E531:')
endif
endfunc
@@ -766,7 +780,13 @@ func Test_shell()
CheckUnix
let save_shell = &shell
set shell=
- call assert_fails('shell', 'E91:')
+ let caught_e91 = 0
+ try
+ shell
+ catch /E91:/
+ let caught_e91 = 1
+ endtry
+ call assert_equal(1, caught_e91)
let &shell = save_shell
endfunc
@@ -812,11 +832,16 @@ func Test_rightleftcmd()
set rightleft&
endfunc
-" Test for the "debug" option
+" Test for the 'debug' option
func Test_debug_option()
+ " redraw to avoid matching previous messages
+ redraw
set debug=beep
exe "normal \<C-c>"
call assert_equal('Beep!', Screenline(&lines))
+ call assert_equal('line 4:', Screenline(&lines - 1))
+ " only match the final colon in the line that shows the source
+ call assert_match(':$', Screenline(&lines - 2))
set debug&
endfunc
diff --git a/src/nvim/testdir/test_preview.vim b/src/nvim/testdir/test_preview.vim
index 6c4ae414d3..b7b908e761 100644
--- a/src/nvim/testdir/test_preview.vim
+++ b/src/nvim/testdir/test_preview.vim
@@ -1,5 +1,8 @@
" Tests for the preview window
+source check.vim
+CheckFeature quickfix
+
func Test_Psearch()
" this used to cause ml_get errors
help
@@ -13,6 +16,8 @@ func Test_Psearch()
endfunc
func Test_window_preview()
+ CheckFeature quickfix
+
" Open a preview window
pedit Xa
call assert_equal(2, winnr('$'))
@@ -32,6 +37,8 @@ func Test_window_preview()
endfunc
func Test_window_preview_from_help()
+ CheckFeature quickfix
+
filetype on
call writefile(['/* some C code */'], 'Xpreview.c')
help
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index f6d573d76b..8c9e39570f 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -85,6 +85,12 @@ func s:setup_commands(cchar)
endif
endfunc
+" This must be run before any error lists are created.
+func Test_AA_cc_no_errors()
+ call assert_fails('cc', 'E42:')
+ call assert_fails('ll', 'E42:')
+endfunc
+
" Tests for the :clist and :llist commands
func XlistTests(cchar)
call s:setup_commands(a:cchar)
diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim
index 93865869fa..e3ca141328 100644
--- a/src/nvim/testdir/test_quotestar.vim
+++ b/src/nvim/testdir/test_quotestar.vim
@@ -98,8 +98,6 @@ func Do_test_quotestar_for_x11()
" Running in a terminal and the GUI is available: Tell the server to open
" the GUI and check that the remote command still works.
- " Need to wait for the GUI to start up, otherwise the send hangs in trying
- " to send to the terminal window.
if has('gui_athena') || has('gui_motif')
" For those GUIs, ignore the 'failed to create input context' error.
call remote_send(name, ":call test_ignore_error('E285') | gui -f\<CR>")
@@ -107,7 +105,10 @@ func Do_test_quotestar_for_x11()
call remote_send(name, ":gui -f\<CR>")
endif
" Wait for the server in the GUI to be up and answering requests.
+ " First need to wait for the GUI to start up, otherwise the send hangs in
+ " trying to send to the terminal window.
" On some systems and with valgrind this can be very slow.
+ sleep 1
call WaitForAssert({-> assert_match("1", remote_expr(name, "has('gui_running')", "", 1))}, 10000)
call remote_send(name, ":let @* = 'maybe'\<CR>")
diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim
index 191cd948ac..14b9724d67 100644
--- a/src/nvim/testdir/test_regexp_utf8.vim
+++ b/src/nvim/testdir/test_regexp_utf8.vim
@@ -522,8 +522,8 @@ endfunc
func Test_search_with_end_offset()
new
call setline(1, ['', 'dog(a', 'cat('])
- exe "normal /(/e+" .. "\<CR>"
- normal "ayn
+ exe "normal /(/e+\<CR>"
+ normal n"ayn
call assert_equal("a\ncat(", @a)
close!
endfunc
diff --git a/src/nvim/testdir/test_registers.vim b/src/nvim/testdir/test_registers.vim
index 52e745438d..11dd3badb6 100644
--- a/src/nvim/testdir/test_registers.vim
+++ b/src/nvim/testdir/test_registers.vim
@@ -279,7 +279,12 @@ func Test_get_register()
call feedkeys(":\<C-R>r\<Esc>", 'xt')
call assert_equal("a\rb", histget(':', -1)) " Modified because of #6137
+ call assert_fails('let r = getreg("=", [])', 'E745:')
+ call assert_fails('let r = getreg("=", 1, [])', 'E745:')
enew!
+
+ " Using a register in operator-pending mode should fail
+ call assert_beeps('norm! c"')
endfunc
func Test_set_register()
@@ -426,6 +431,12 @@ func Test_execute_register()
@
call assert_equal(3, i)
+ " try to execute expression register and use a backspace to cancel it
+ new
+ call feedkeys("@=\<BS>ax\<CR>y", 'xt')
+ call assert_equal(['x', 'y'], getline(1, '$'))
+ close!
+
" cannot execute a register in operator pending mode
call assert_beeps('normal! c@r')
endfunc
diff --git a/src/nvim/testdir/test_selectmode.vim b/src/nvim/testdir/test_selectmode.vim
index b483841060..f2cab45450 100644
--- a/src/nvim/testdir/test_selectmode.vim
+++ b/src/nvim/testdir/test_selectmode.vim
@@ -2,6 +2,156 @@
source shared.vim
+" Test for select mode
+func Test_selectmode_basic()
+ new
+ call setline(1, range(1,100))
+ 50
+ norm! gHy
+ call assert_equal('y51', getline('.'))
+ call setline(1, range(1,100))
+ 50
+ exe ":norm! V9jo\<c-g>y"
+ call assert_equal('y60', getline('.'))
+ call setline(1, range(1,100))
+ 50
+ " call feedkeys(":set im\n\<c-o>gHc\<c-o>:set noim\n", 'tx')
+ call feedkeys("i\<c-o>gHc\<esc>", 'tx')
+ call assert_equal('c51', getline('.'))
+ " clean up
+ bw!
+endfunc
+
+" Test for starting selectmode
+func Test_selectmode_start()
+ new
+ set selectmode=key keymodel=startsel
+ call setline(1, ['abc', 'def', 'ghi'])
+ call cursor(1, 4)
+ call feedkeys("A\<s-home>start\<esc>", 'txin')
+ call assert_equal(['startdef', 'ghi'], getline(1, '$'))
+ " start select mode again with gv
+ set selectmode=cmd
+ call feedkeys('gvabc', 'xt')
+ call assert_equal('abctdef', getline(1))
+ set selectmode= keymodel=
+ bw!
+endfunc
+
+" Test for characterwise select mode
+func Test_characterwise_select_mode()
+ new
+
+ " Select mode maps
+ snoremap <lt>End> <End>
+ snoremap <lt>Down> <Down>
+ snoremap <lt>Del> <Del>
+
+ " characterwise select mode: delete middle line
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Gkkgh\<End>\<Del>"
+ call assert_equal(['', 'b', 'c'], getline(1, '$'))
+
+ " characterwise select mode: delete middle two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Gkkgh\<Down>\<End>\<Del>"
+ call assert_equal(['', 'c'], getline(1, '$'))
+
+ " characterwise select mode: delete last line
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Ggh\<End>\<Del>"
+ call assert_equal(['', 'a', 'b', ''], getline(1, '$'))
+
+ " characterwise select mode: delete last two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal Gkgh\<Down>\<End>\<Del>"
+ call assert_equal(['', 'a', ''], getline(1, '$'))
+
+ " CTRL-H in select mode behaves like 'x'
+ call setline(1, 'abcdef')
+ exe "normal! gggh\<Right>\<Right>\<Right>\<C-H>"
+ call assert_equal('ef', getline(1))
+
+ " CTRL-O in select mode switches to visual mode for one command
+ call setline(1, 'abcdef')
+ exe "normal! gggh\<C-O>3lm"
+ call assert_equal('mef', getline(1))
+
+ sunmap <lt>End>
+ sunmap <lt>Down>
+ sunmap <lt>Del>
+ bwipe!
+endfunc
+
+" Test for linewise select mode
+func Test_linewise_select_mode()
+ new
+
+ " linewise select mode: delete middle line
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GkkgH\<Del>"
+ call assert_equal(['', 'b', 'c'], getline(1, '$'))
+
+ " linewise select mode: delete middle two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GkkgH\<Down>\<Del>"
+ call assert_equal(['', 'c'], getline(1, '$'))
+
+ " linewise select mode: delete last line
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GgH\<Del>"
+ call assert_equal(['', 'a', 'b'], getline(1, '$'))
+
+ " linewise select mode: delete last two lines
+ call deletebufline('', 1, '$')
+ call append('$', ['a', 'b', 'c'])
+ exe "normal GkgH\<Down>\<Del>"
+ call assert_equal(['', 'a'], getline(1, '$'))
+
+ bwipe!
+endfunc
+
+" Test for blockwise select mode (g CTRL-H)
+func Test_blockwise_select_mode()
+ new
+ call setline(1, ['foo', 'bar'])
+ call feedkeys("g\<BS>\<Right>\<Down>mm", 'xt')
+ call assert_equal(['mmo', 'mmr'], getline(1, '$'))
+ close!
+endfunc
+
+" Test for using visual mode maps in select mode
+func Test_select_mode_map()
+ new
+ vmap <buffer> <F2> 3l
+ call setline(1, 'Test line')
+ call feedkeys("gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+
+ vmap <buffer> <F2> ygV
+ call feedkeys("0gh\<Right>\<Right>\<F2>cwabc", 'xt')
+ call assert_equal('abc line', getline(1))
+
+ vmap <buffer> <F2> :<C-U>let v=100<CR>
+ call feedkeys("gggh\<Right>\<Right>\<F2>foo", 'xt')
+ call assert_equal('foo line', getline(1))
+
+ " reselect the select mode using gv from a visual mode map
+ vmap <buffer> <F2> gv
+ set selectmode=cmd
+ call feedkeys("0gh\<F2>map", 'xt')
+ call assert_equal('map line', getline(1))
+ set selectmode&
+
+ close!
+endfunc
+
" Test for selecting a register with CTRL-R
func Test_selectmode_register()
new
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 7744c5bcca..8ab8204b10 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -474,6 +474,16 @@ func Test_spellsuggest_option_expr()
bwipe!
endfunc
+func Test_spellsuggest_timeout()
+ set spellsuggest=timeout:30
+ set spellsuggest=timeout:-123
+ set spellsuggest=timeout:999999
+ call assert_fails('set spellsuggest=timeout', 'E474:')
+ call assert_fails('set spellsuggest=timeout:x', 'E474:')
+ call assert_fails('set spellsuggest=timeout:-x', 'E474:')
+ call assert_fails('set spellsuggest=timeout:--9', 'E474:')
+endfunc
+
func Test_spellinfo()
throw 'skipped: Nvim does not support enc=latin1'
new
diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim
index b028e9d969..dbffbafed9 100644
--- a/src/nvim/testdir/test_spellfile.vim
+++ b/src/nvim/testdir/test_spellfile.vim
@@ -25,6 +25,18 @@ func Test_spell_normal()
let cnt=readfile('./Xspellfile.add')
call assert_equal('goood', cnt[0])
+ " zg should fail in operator-pending mode
+ call assert_beeps('norm! czg')
+
+ " zg fails in visual mode when not able to get the visual text
+ call assert_beeps('norm! ggVjzg')
+ norm! V
+
+ " zg fails for a non-identifier word
+ call append(line('$'), '###')
+ call assert_fails('norm! Gzg', 'E349:')
+ $d
+
" Test for zw
2
norm! $zw
@@ -907,6 +919,33 @@ func Test_spellfile_COMMON()
call delete('XtestCOMMON-utf8.spl')
endfunc
+" Test NOSUGGEST (see :help spell-COMMON)
+func Test_spellfile_NOSUGGEST()
+ call writefile(['2', 'foo/X', 'fog'], 'XtestNOSUGGEST.dic')
+ call writefile(['NOSUGGEST X'], 'XtestNOSUGGEST.aff')
+
+ mkspell! XtestNOSUGGEST-utf8.spl XtestNOSUGGEST
+ set spell spelllang=XtestNOSUGGEST-utf8.spl
+
+ for goodword in ['foo', 'Foo', 'FOO', 'fog', 'Fog', 'FOG']
+ call assert_equal(['', ''], spellbadword(goodword), goodword)
+ endfor
+ for badword in ['foO', 'fOO', 'fooo', 'foog', 'foofog', 'fogfoo']
+ call assert_equal([badword, 'bad'], spellbadword(badword))
+ endfor
+
+ call assert_equal(['fog'], spellsuggest('fooo', 1))
+ call assert_equal(['fog'], spellsuggest('fOo', 1))
+ call assert_equal(['fog'], spellsuggest('foG', 1))
+ call assert_equal(['fog'], spellsuggest('fogg', 1))
+
+ set spell& spelllang&
+ call delete('XtestNOSUGGEST.dic')
+ call delete('XtestNOSUGGEST.aff')
+ call delete('XtestNOSUGGEST-utf8.spl')
+endfunc
+
+
" Test CIRCUMFIX (see: :help spell-CIRCUMFIX)
func Test_spellfile_CIRCUMFIX()
" Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests
diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim
index 39fafbf7b4..880ca62685 100644
--- a/src/nvim/testdir/test_startup.vim
+++ b/src/nvim/testdir/test_startup.vim
@@ -603,7 +603,7 @@ func Test_invalid_args()
call assert_equal(0, v:shell_error)
if has('quickfix')
- " Detect invalid repeated arguments '-t foo -t foo", '-q foo -q foo'.
+ " Detect invalid repeated arguments '-t foo -t foo', '-q foo -q foo'.
for opt in ['-t', '-q']
let out = split(system(GetVimCommand() .. repeat(' ' .. opt .. ' foo', 2)), "\n")
call assert_equal(1, v:shell_error)
@@ -855,7 +855,7 @@ func Test_t_arg()
call writefile([' first', ' second', ' third'], 'Xfile1')
for t_arg in ['-t second', '-tsecond']
- if RunVim(before, after, '-t second')
+ if RunVim(before, after, t_arg)
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'), t_arg)
call delete('Xtestout')
endif
diff --git a/src/nvim/testdir/test_substitute.vim b/src/nvim/testdir/test_substitute.vim
index f795d1c0cf..b3a80072d9 100644
--- a/src/nvim/testdir/test_substitute.vim
+++ b/src/nvim/testdir/test_substitute.vim
@@ -858,6 +858,44 @@ func Test_substitute_skipped_range()
bwipe!
endfunc
+" Test using the 'gdefault' option (when on, flag 'g' is default on).
+func Test_substitute_gdefault()
+ new
+
+ " First check without 'gdefault'
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/
+ call assert_equal('FOO bar foo', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/g
+ call assert_equal('FOO bar FOO', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/gg
+ call assert_equal('FOO bar foo', getline(1))
+
+ " Then check with 'gdefault'
+ set gdefault
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/
+ call assert_equal('FOO bar FOO', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/g
+ call assert_equal('FOO bar foo', getline(1))
+ call setline(1, 'foo bar foo')
+ s/foo/FOO/gg
+ call assert_equal('FOO bar FOO', getline(1))
+
+ " Setting 'compatible' should reset 'gdefault'
+ call assert_equal(1, &gdefault)
+ " set compatible
+ set nogdefault
+ call assert_equal(0, &gdefault)
+ set nocompatible
+ call assert_equal(0, &gdefault)
+
+ bw!
+endfunc
+
" This was using "old_sub" after it was freed.
func Test_using_old_sub()
" set compatible maxfuncdepth=10
diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim
index 923e1cbf50..34d1d585ce 100644
--- a/src/nvim/testdir/test_swap.vim
+++ b/src/nvim/testdir/test_swap.vim
@@ -2,6 +2,7 @@
source check.vim
source shared.vim
+source term_util.vim
func s:swapname()
return trim(execute('swapname'))
@@ -374,24 +375,26 @@ func Test_swap_prompt_splitwin()
call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))})
call StopVimInTerminal(buf)
- " This caused Vim to crash when typing "q".
- " TODO: it does not actually reproduce the crash.
- call writefile(['au BufAdd * set virtualedit=all'], 'Xvimrc')
-
- let buf = RunVimInTerminal('-u Xvimrc Xfile1', {'rows': 20, 'wait_for_ruler': 0})
- call TermWait(buf)
- call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 20))})
+ " This caused Vim to crash when typing "q" at the swap file prompt.
+ let buf = RunVimInTerminal('-c "au bufadd * let foo_w = wincol()"', {'rows': 18})
+ call term_sendkeys(buf, ":e Xfile1\<CR>")
+ call WaitForAssert({-> assert_match('More', term_getline(buf, 18))})
+ call term_sendkeys(buf, " ")
+ call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 18))})
call term_sendkeys(buf, "q")
+ call TermWait(buf)
+ " check that Vim is still running
+ call term_sendkeys(buf, ":echo 'hello'\<CR>")
+ call WaitForAssert({-> assert_match('^hello', term_getline(buf, 18))})
+ call term_sendkeys(buf, ":%bwipe!\<CR>")
+ call StopVimInTerminal(buf)
%bwipe!
call delete('Xfile1')
- call delete('Xvimrc')
endfunc
func Test_swap_symlink()
- if !has("unix")
- return
- endif
+ CheckUnix
call writefile(['text'], 'Xtestfile')
silent !ln -s -f Xtestfile Xtestlink
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 7ba0149971..ccff01486e 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -188,22 +188,26 @@ func Test_syntax_completion()
call assert_equal('"syn sync ccomment clear fromstart linebreaks= linecont lines= match maxlines= minlines= region', @:)
" Check that clearing "Aap" avoids it showing up before Boolean.
- hi Aap ctermfg=blue
+ hi @Aap ctermfg=blue
call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^"syn list Aap Boolean Character ', @:)
- hi clear Aap
+ call assert_match('^"syn list @Aap @boolean @character ', @:)
+ hi clear @Aap
call feedkeys(":syn list \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^"syn list Boolean Character ', @:)
+ call assert_match('^"syn list @boolean @character ', @:)
call feedkeys(":syn match \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_match('^"syn match Boolean Character ', @:)
+ call assert_match('^"syn match @boolean @character ', @:)
+
+ syn cluster Aax contains=Aap
+ call feedkeys(":syn list @A\<C-A>\<C-B>\"\<CR>", 'tx')
+ call assert_match('^"syn list @Aax', @:)
endfunc
func Test_echohl_completion()
call feedkeys(":echohl no\<C-A>\<C-B>\"\<CR>", 'tx')
" call assert_equal('"echohl NonText Normal none', @:)
- call assert_equal('"echohl NonText Normal NormalFloat NormalNC none', @:)
+ call assert_equal('"echohl NonText Normal NormalFloat none', @:)
endfunc
func Test_syntax_arg_skipped()
@@ -389,7 +393,7 @@ func Test_invalid_name()
syn keyword Nop yes
call assert_fails("syntax keyword Wr\x17ong bar", 'E669:')
syntax keyword @Wrong bar
- call assert_match('W18:', execute('1messages'))
+ call assert_fails("syntax keyword @#Wrong bar", 'E5248:')
syn clear
hi clear Nop
hi clear @Wrong
diff --git a/src/nvim/testdir/test_tabpage.vim b/src/nvim/testdir/test_tabpage.vim
index 07f35ddb4f..6d468ec9de 100644
--- a/src/nvim/testdir/test_tabpage.vim
+++ b/src/nvim/testdir/test_tabpage.vim
@@ -670,15 +670,19 @@ func Test_tabline_tabmenu()
call assert_equal(3, tabpagenr('$'))
" go to tab page 2 in operator-pending mode (should beep)
- call assert_beeps('call feedkeys("f" .. TabLineSelectPageCode(2), "Lx!")')
+ call assert_beeps('call feedkeys("c" .. TabLineSelectPageCode(2), "Lx!")')
+ call assert_equal(2, tabpagenr())
+ call assert_equal(3, tabpagenr('$'))
" open new tab page before tab page 1 in operator-pending mode (should beep)
- call assert_beeps('call feedkeys("f" .. TabMenuNewItemCode(1), "Lx!")')
+ call assert_beeps('call feedkeys("c" .. TabMenuNewItemCode(1), "Lx!")')
+ call assert_equal(1, tabpagenr())
+ call assert_equal(4, tabpagenr('$'))
" open new tab page after tab page 3 in normal mode
call feedkeys(TabMenuNewItemCode(4), "Lx!")
call assert_equal(4, tabpagenr())
- call assert_equal(4, tabpagenr('$'))
+ call assert_equal(5, tabpagenr('$'))
" go to tab page 2 in insert mode
call feedkeys("i" .. TabLineSelectPageCode(2) .. "\<C-C>", "Lx!")
@@ -686,17 +690,17 @@ func Test_tabline_tabmenu()
" close tab page 2 in insert mode
call feedkeys("i" .. TabMenuCloseItemCode(2) .. "\<C-C>", "Lx!")
- call assert_equal(3, tabpagenr('$'))
+ call assert_equal(4, tabpagenr('$'))
" open new tab page before tab page 3 in insert mode
call feedkeys("i" .. TabMenuNewItemCode(3) .. "\<C-C>", "Lx!")
call assert_equal(3, tabpagenr())
- call assert_equal(4, tabpagenr('$'))
+ call assert_equal(5, tabpagenr('$'))
" open new tab page after tab page 4 in insert mode
call feedkeys("i" .. TabMenuNewItemCode(5) .. "\<C-C>", "Lx!")
call assert_equal(5, tabpagenr())
- call assert_equal(5, tabpagenr('$'))
+ call assert_equal(6, tabpagenr('$'))
%bw!
endfunc
diff --git a/src/nvim/testdir/test_tagjump.vim b/src/nvim/testdir/test_tagjump.vim
index 1dd656ece5..04c0218f74 100644
--- a/src/nvim/testdir/test_tagjump.vim
+++ b/src/nvim/testdir/test_tagjump.vim
@@ -1133,7 +1133,7 @@ endfunc
" Test for [i, ]i, [I, ]I, [ CTRL-I, ] CTRL-I and CTRL-W i commands
func Test_inc_search()
new
- call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo'])
+ call setline(1, ['1:foo', '2:foo', 'foo', '3:foo', '4:foo', '==='])
call cursor(3, 1)
" Test for [i and ]i
@@ -1143,6 +1143,9 @@ func Test_inc_search()
call assert_equal('3:foo', execute('normal ]i'))
call assert_equal('4:foo', execute('normal 2]i'))
call assert_fails('normal 3]i', 'E389:')
+ call assert_fails('normal G]i', 'E349:')
+ call assert_fails('normal [i', 'E349:')
+ call cursor(3, 1)
" Test for :isearch
call assert_equal('1:foo', execute('isearch foo'))
@@ -1163,6 +1166,9 @@ func Test_inc_search()
call assert_equal([
\ ' 1: 4 3:foo',
\ ' 2: 5 4:foo'], split(execute('normal ]I'), "\n"))
+ call assert_fails('normal G]I', 'E349:')
+ call assert_fails('normal [I', 'E349:')
+ call cursor(3, 1)
" Test for :ilist
call assert_equal([
@@ -1188,6 +1194,9 @@ func Test_inc_search()
exe "normal k2]\t"
call assert_equal([5, 3], [line('.'), col('.')])
call assert_fails("normal 2k3]\t", 'E389:')
+ call assert_fails("normal G[\t", 'E349:')
+ call assert_fails("normal ]\t", 'E349:')
+ call cursor(3, 1)
" Test for :ijump
call cursor(3, 1)
@@ -1212,6 +1221,8 @@ func Test_inc_search()
close
call assert_fails('3wincmd i', 'E387:')
call assert_fails('6wincmd i', 'E389:')
+ call assert_fails("normal G\<C-W>i", 'E349:')
+ call cursor(3, 1)
" Test for :isplit
isplit foo
diff --git a/src/nvim/testdir/test_textformat.vim b/src/nvim/testdir/test_textformat.vim
index 0fc56083aa..4eb6e69adf 100644
--- a/src/nvim/testdir/test_textformat.vim
+++ b/src/nvim/testdir/test_textformat.vim
@@ -962,78 +962,6 @@ func Test_tw_2_fo_tm_noai()
bwipe!
endfunc
-func Test_tw_2_fo_cqm_com()
- new
- let t =<< trim END
- {
- X
- Xa
- XaY
- XY
- XYZ
- X Y
- X YZ
- XX
- XXa
- XXY
- }
- END
- call setline(1, t)
- call cursor(2, 1)
-
- set tw=2 fo=cqm comments=n:X
- exe "normal gqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgqjgqgq"
- let t =<< trim END
- X
- Xa
- XaY
- XY
- XYZ
- X Y
- X YZ
- XX
- XXa
- XXY
- END
- exe "normal o\n" . join(t, "\n")
-
- let expected =<< trim END
- {
- X
- Xa
- Xa
- XY
- XY
- XY
- XZ
- X Y
- X Y
- X Z
- XX
- XXa
- XXY
-
- X
- Xa
- Xa
- XY
- XY
- XY
- XZ
- X Y
- X Y
- X Z
- XX
- XXa
- XXY
- }
- END
- call assert_equal(expected, getline(1, '$'))
-
- set tw& fo& comments&
- bwipe!
-endfunc
-
func Test_tw_2_fo_tm_replace()
new
let t =<< trim END
@@ -1161,140 +1089,6 @@ func Test_whichwrap_multi_byte()
bwipe!
endfunc
-" Test for automatically adding comment leaders in insert mode
-func Test_threepiece_comment()
- new
- setlocal expandtab
- call setline(1, ["\t/*"])
- setlocal formatoptions=croql
- call cursor(1, 3)
- call feedkeys("A\<cr>\<cr>/", 'tnix')
- call assert_equal(["\t/*", " *", " */"], getline(1, '$'))
-
- " If a comment ends in a single line, then don't add it in the next line
- %d
- call setline(1, '/* line1 */')
- call feedkeys("A\<CR>next line", 'xt')
- call assert_equal(['/* line1 */', 'next line'], getline(1, '$'))
-
- %d
- " Copy the trailing indentation from the leader comment to a new line
- setlocal autoindent noexpandtab
- call feedkeys("a\t/*\tone\ntwo\n/", 'xt')
- call assert_equal(["\t/*\tone", "\t *\ttwo", "\t */"], getline(1, '$'))
- close!
-endfunc
-
-" Test for the 'f' flag in 'comments' (only the first line has the comment
-" string)
-func Test_firstline_comment()
- new
- setlocal comments=f:- fo+=ro
- exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
- call assert_equal(['A', '- B', ' C', ' D'], getline(1, '$'))
- %d
- setlocal comments=:-
- exe "normal i- B\nD\<C-C>ggoC\<C-C>ggOA\<C-C>"
- call assert_equal(['- A', '- B', '- C', '- D'], getline(1, '$'))
- %bw!
-endfunc
-
-" Test for the 'r' flag in 'comments' (right align comment)
-func Test_comment_rightalign()
- new
- setlocal comments=sr:/***,m:**,ex-2:******/ fo+=ro
- exe "normal i=\<C-C>o\t /***\nD\n/"
- exe "normal 2GOA\<C-C>joB\<C-C>jOC\<C-C>joE\<C-C>GOF\<C-C>joG"
- let expected =<< trim END
- =
- A
- /***
- ** B
- ** C
- ** D
- ** E
- ** F
- ******/
- G
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
-" Test for the 'b' flag in 'comments'
-func Test_comment_blank()
- new
- setlocal comments=b:* fo+=ro
- exe "normal i* E\nF\n\<BS>G\nH\<C-C>ggOC\<C-C>O\<BS>B\<C-C>OA\<C-C>2joD"
- let expected =<< trim END
- A
- *B
- * C
- * D
- * E
- * F
- *G
- H
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
-" Test for the 'n' flag in comments
-func Test_comment_nested()
- new
- setlocal comments=n:> fo+=ro
- exe "normal i> B\nD\<C-C>ggOA\<C-C>joC\<C-C>Go\<BS>>>> F\nH"
- exe "normal 5GOE\<C-C>6GoG"
- let expected =<< trim END
- > A
- > B
- > C
- > D
- >>>> E
- >>>> F
- >>>> G
- >>>> H
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
-" Test for a space character in 'comments' setting
-func Test_comment_space()
- new
- setlocal comments=b:\ > fo+=ro
- exe "normal i> B\nD\<C-C>ggOA\<C-C>joC"
- exe "normal Go > F\nH\<C-C>kOE\<C-C>joG"
- let expected =<< trim END
- A
- > B
- C
- D
- > E
- > F
- > G
- > H
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
-" Test for the 'O' flag in 'comments'
-func Test_comment_O()
- new
- setlocal comments=Ob:* fo+=ro
- exe "normal i* B\nD\<C-C>kOA\<C-C>joC"
- let expected =<< trim END
- A
- * B
- * C
- * D
- END
- call assert_equal(expected, getline(1, '$'))
- %bw!
-endfunc
-
" Test for 'a' and 'w' flags in 'formatoptions'
func Test_fo_a_w()
new
@@ -1334,25 +1128,6 @@ func Test_fo_a_w()
%bw!
endfunc
-" Test for 'j' flag in 'formatoptions'
-func Test_fo_j()
- new
- setlocal fo+=j comments=://
- call setline(1, ['i++; // comment1', ' // comment2'])
- normal J
- call assert_equal('i++; // comment1 comment2', getline(1))
- setlocal fo-=j
- call setline(1, ['i++; // comment1', ' // comment2'])
- normal J
- call assert_equal('i++; // comment1 // comment2', getline(1))
- " Test with nested comments
- setlocal fo+=j comments=n:>,n:)
- call setline(1, ['i++; > ) > ) comment1', ' > ) comment2'])
- normal J
- call assert_equal('i++; > ) > ) comment1 comment2', getline(1))
- %bw!
-endfunc
-
" Test for formatting lines using gq in visual mode
func Test_visual_gq_format()
new
@@ -1487,53 +1262,6 @@ func Test_fo_2()
close!
endfunc
-" Test for formatting lines where only the first line has a comment.
-func Test_fo_gq_with_firstline_comment()
- new
- setlocal formatoptions=tcq
- call setline(1, ['- one two', 'three'])
- normal gggqG
- call assert_equal(['- one two three'], getline(1, '$'))
-
- %d
- call setline(1, ['- one', '- two'])
- normal gggqG
- call assert_equal(['- one', '- two'], getline(1, '$'))
- close!
-endfunc
-
-" Test for trying to join a comment line with a non-comment line
-func Test_join_comments()
- new
- call setline(1, ['one', '/* two */', 'three'])
- normal gggqG
- call assert_equal(['one', '/* two */', 'three'], getline(1, '$'))
- close!
-endfunc
-
-" Test for using 'a' in 'formatoptions' with comments
-func Test_autoformat_comments()
- new
- setlocal formatoptions+=a
- call feedkeys("a- one\n- two\n", 'xt')
- call assert_equal(['- one', '- two', ''], getline(1, '$'))
-
- %d
- call feedkeys("a\none\n", 'xt')
- call assert_equal(['', 'one', ''], getline(1, '$'))
-
- setlocal formatoptions+=aw
- %d
- call feedkeys("aone \ntwo\n", 'xt')
- call assert_equal(['one two', ''], getline(1, '$'))
-
- %d
- call feedkeys("aone\ntwo\n", 'xt')
- call assert_equal(['one', 'two', ''], getline(1, '$'))
-
- close!
-endfunc
-
" This was leaving the cursor after the end of a line. Complicated way to
" have the problem show up with valgrind.
func Test_correct_cursor_position()
diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim
index eeb2946a8b..f21d6fcb99 100644
--- a/src/nvim/testdir/test_textobjects.vim
+++ b/src/nvim/testdir/test_textobjects.vim
@@ -1,7 +1,6 @@
" Test for textobjects
source check.vim
-CheckFeature textobjects
func CpoM(line, useM, expected)
new
diff --git a/src/nvim/testdir/test_trycatch.vim b/src/nvim/testdir/test_trycatch.vim
index 646594e482..d71bb5bbb8 100644
--- a/src/nvim/testdir/test_trycatch.vim
+++ b/src/nvim/testdir/test_trycatch.vim
@@ -2027,16 +2027,179 @@ func Test_try_catch_verbose()
endtry
redir END
let expected = [
- \ 'Exception thrown: Vim(echo):E121: Undefined variable: i',
- \ '',
- \ 'Exception caught: Vim(echo):E121: Undefined variable: i',
- \ '',
- \ 'Exception finished: Vim(echo):E121: Undefined variable: i'
- \ ]
+ \ 'Exception thrown: Vim(echo):E121: Undefined variable: i', '',
+ \ 'Exception caught: Vim(echo):E121: Undefined variable: i', '',
+ \ 'Exception finished: Vim(echo):E121: Undefined variable: i']
call assert_equal(expected, split(msg, "\n"))
+
+ " Test for verbose messages displayed when an exception is discarded
+ redir => msg
+ try
+ try
+ throw 'abc'
+ finally
+ throw 'xyz'
+ endtry
+ catch
+ endtry
+ redir END
+ let expected = [
+ \ 'Exception thrown: abc', '',
+ \ 'Exception made pending: abc', '',
+ \ 'Exception thrown: xyz', '',
+ \ 'Exception discarded: abc', '',
+ \ 'Exception caught: xyz', '',
+ \ 'Exception finished: xyz']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :throw is resumed after :finally
+ redir => msg
+ try
+ try
+ throw 'abc'
+ finally
+ endtry
+ catch
+ endtry
+ redir END
+ let expected = [
+ \ 'Exception thrown: abc', '',
+ \ 'Exception made pending: abc', '',
+ \ 'Exception resumed: abc', '',
+ \ 'Exception caught: abc', '',
+ \ 'Exception finished: abc']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :break is resumed after :finally
+ redir => msg
+ for i in range(1)
+ try
+ break
+ finally
+ endtry
+ endfor
+ redir END
+ let expected = [':break made pending', '', ':break resumed']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :continue is resumed after :finally
+ redir => msg
+ for i in range(1)
+ try
+ continue
+ finally
+ endtry
+ endfor
+ redir END
+ let expected = [':continue made pending', '', ':continue resumed']
+ call assert_equal(expected, split(msg, "\n"))
+
+ " Test for messages displayed when :return is resumed after :finally
+ func Xtest()
+ try
+ return 'vim'
+ finally
+ endtry
+ endfunc
+ redir => msg
+ call Xtest()
+ redir END
+ let expected = [
+ \ 'calling Xtest()', '',
+ \ ':return vim made pending', '',
+ \ ':return vim resumed', '',
+ \ 'Xtest returning ''vim''', '',
+ \ 'continuing in Test_try_catch_verbose']
+ call assert_equal(expected, split(msg, "\n"))
+ delfunc Xtest
+
+ " Test for messages displayed when :finish is resumed after :finally
+ call writefile(['try', 'finish', 'finally', 'endtry'], 'Xscript')
+ redir => msg
+ source Xscript
+ redir END
+ let expected = [
+ \ ':finish made pending', '',
+ \ ':finish resumed', '',
+ \ 'finished sourcing Xscript',
+ \ 'continuing in Test_try_catch_verbose']
+ call assert_equal(expected, split(msg, "\n")[1:])
+ call delete('Xscript')
+
+ " Test for messages displayed when a pending :continue is discarded by an
+ " exception in a finally handler
+ redir => msg
+ try
+ for i in range(1)
+ try
+ continue
+ finally
+ throw 'abc'
+ endtry
+ endfor
+ catch
+ endtry
+ redir END
+ let expected = [
+ \ ':continue made pending', '',
+ \ 'Exception thrown: abc', '',
+ \ ':continue discarded', '',
+ \ 'Exception caught: abc', '',
+ \ 'Exception finished: abc']
+ call assert_equal(expected, split(msg, "\n"))
+
set verbose&
endfunc
+" Test for throwing an exception from a BufEnter autocmd {{{1
+func Test_BufEnter_exception()
+ augroup bufenter_exception
+ au!
+ autocmd BufEnter Xfile1 throw 'abc'
+ augroup END
+
+ let caught_abc = 0
+ try
+ sp Xfile1
+ catch /^abc/
+ let caught_abc = 1
+ endtry
+ call assert_equal(1, caught_abc)
+ call assert_equal(1, winnr('$'))
+
+ augroup bufenter_exception
+ au!
+ augroup END
+ augroup! bufenter_exception
+ %bwipe!
+
+ " Test for recursively throwing exceptions in autocmds
+ augroup bufenter_exception
+ au!
+ autocmd BufEnter Xfile1 throw 'bufenter'
+ autocmd BufLeave Xfile1 throw 'bufleave'
+ augroup END
+
+ let ex_count = 0
+ try
+ try
+ sp Xfile1
+ catch /^bufenter/
+ let ex_count += 1
+ endtry
+ catch /^bufleave/
+ let ex_count += 10
+ endtry
+ call assert_equal(10, ex_count)
+ call assert_equal(2, winnr('$'))
+
+ augroup bufenter_exception
+ au!
+ augroup END
+ augroup! bufenter_exception
+ %bwipe!
+endfunc
+
" Test for using throw in a called function with following error {{{1
func Test_user_command_throw_in_function_call()
let lines =<< trim END
diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim
index a9ec405aa4..eb47af08d7 100644
--- a/src/nvim/testdir/test_undo.vim
+++ b/src/nvim/testdir/test_undo.vim
@@ -336,7 +336,7 @@ func Test_undofile_earlier()
" create undofile with timestamps older than Vim startup time.
let t0 = localtime() - 43200
call test_settime(t0)
- new Xfile
+ new XfileEarlier
call feedkeys("ione\<Esc>", 'xt')
set ul=100
call test_settime(t0 + 1)
@@ -350,12 +350,12 @@ func Test_undofile_earlier()
bwipe!
" restore normal timestamps.
call test_settime(0)
- new Xfile
+ new XfileEarlier
rundo Xundofile
earlier 1d
call assert_equal('', getline(1))
bwipe!
- call delete('Xfile')
+ call delete('XfileEarlier')
call delete('Xundofile')
endfunc
diff --git a/src/nvim/testdir/test_user_func.vim b/src/nvim/testdir/test_user_func.vim
index 5231ef7b4f..c14624f5b4 100644
--- a/src/nvim/testdir/test_user_func.vim
+++ b/src/nvim/testdir/test_user_func.vim
@@ -169,3 +169,10 @@ endfunc
func Test_failed_call_in_try()
try | call UnknownFunc() | catch | endtry
endfunc
+
+" Test for listing user-defined functions
+func Test_function_list()
+ call assert_fails("function Xabc", 'E123:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_utf8.vim b/src/nvim/testdir/test_utf8.vim
index 9b010a5dbc..ab3503c282 100644
--- a/src/nvim/testdir/test_utf8.vim
+++ b/src/nvim/testdir/test_utf8.vim
@@ -140,6 +140,51 @@ func Test_list2str_str2list_latin1()
call assert_equal(s, sres)
endfunc
+func Test_setcellwidths()
+ call setcellwidths([
+ \ [0x1330, 0x1330, 2],
+ \ [9999, 10000, 1],
+ \ [0x1337, 0x1339, 2],
+ \])
+
+ call assert_equal(2, strwidth("\u1330"))
+ call assert_equal(1, strwidth("\u1336"))
+ call assert_equal(2, strwidth("\u1337"))
+ call assert_equal(2, strwidth("\u1339"))
+ call assert_equal(1, strwidth("\u133a"))
+
+ call setcellwidths([])
+
+ call assert_fails('call setcellwidths(1)', 'E714:')
+
+ call assert_fails('call setcellwidths([1, 2, 0])', 'E1109:')
+
+ call assert_fails('call setcellwidths([[0x101]])', 'E1110:')
+ call assert_fails('call setcellwidths([[0x101, 0x102]])', 'E1110:')
+ call assert_fails('call setcellwidths([[0x101, 0x102, 1, 4]])', 'E1110:')
+ call assert_fails('call setcellwidths([["a"]])', 'E1110:')
+
+ call assert_fails('call setcellwidths([[0x102, 0x101, 1]])', 'E1111:')
+
+ call assert_fails('call setcellwidths([[0x101, 0x102, 0]])', 'E1112:')
+ call assert_fails('call setcellwidths([[0x101, 0x102, 3]])', 'E1112:')
+
+ call assert_fails('call setcellwidths([[0x111, 0x122, 1], [0x115, 0x116, 2]])', 'E1113:')
+ call assert_fails('call setcellwidths([[0x111, 0x122, 1], [0x122, 0x123, 2]])', 'E1113:')
+
+ call assert_fails('call setcellwidths([[0x33, 0x44, 2]])', 'E1114:')
+
+ set listchars=tab:--\\u2192
+ call assert_fails('call setcellwidths([[0x2192, 0x2192, 2]])', 'E834:')
+
+ set fillchars=stl:\\u2501
+ call assert_fails('call setcellwidths([[0x2501, 0x2501, 2]])', 'E835:')
+
+ set listchars&
+ set fillchars&
+ call setcellwidths([])
+endfunc
+
func Test_print_overlong()
" Text with more composing characters than MB_MAXBYTES.
new
diff --git a/src/nvim/testdir/test_viminfo.vim b/src/nvim/testdir/test_viminfo.vim
new file mode 100644
index 0000000000..2d6d598011
--- /dev/null
+++ b/src/nvim/testdir/test_viminfo.vim
@@ -0,0 +1,21 @@
+
+" Test for errors in setting 'viminfo'
+func Test_viminfo_option_error()
+ " Missing number
+ call assert_fails('set viminfo=\"', 'E526:')
+ for c in split("'/:<@s", '\zs')
+ call assert_fails('set viminfo=' .. c, 'E526:')
+ endfor
+
+ " Missing comma
+ call assert_fails('set viminfo=%10!', 'E527:')
+ call assert_fails('set viminfo=!%10', 'E527:')
+ call assert_fails('set viminfo=h%10', 'E527:')
+ call assert_fails('set viminfo=c%10', 'E527:')
+ call assert_fails('set viminfo=:10%10', 'E527:')
+
+ " Missing ' setting
+ call assert_fails('set viminfo=%10', 'E528:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim
index de4629451b..97e879c9ef 100644
--- a/src/nvim/testdir/test_vimscript.vim
+++ b/src/nvim/testdir/test_vimscript.vim
@@ -1165,10 +1165,10 @@ func Test_type()
" call assert_equal(0, 0 + v:none)
call assert_equal(0, 0 + v:null)
- call assert_equal('false', '' . v:false)
- call assert_equal('true', '' . v:true)
- " call assert_equal('none', '' . v:none)
- call assert_equal('null', '' . v:null)
+ call assert_equal('v:false', '' . v:false)
+ call assert_equal('v:true', '' . v:true)
+ " call assert_equal('v:none', '' . v:none)
+ call assert_equal('v:null', '' . v:null)
call assert_true(v:false == 0)
call assert_false(v:false != 0)
@@ -1573,6 +1573,23 @@ func Test_script_local_func()
enew! | close
endfunc
+func Test_script_expand_sfile()
+ let lines =<< trim END
+ func s:snr()
+ return expand('<sfile>')
+ endfunc
+ let g:result = s:snr()
+ END
+ call writefile(lines, 'Xexpand')
+ source Xexpand
+ call assert_match('<SNR>\d\+_snr', g:result)
+ source Xexpand
+ call assert_match('<SNR>\d\+_snr', g:result)
+
+ call delete('Xexpand')
+ unlet g:result
+endfunc
+
func Test_compound_assignment_operators()
" Test for number
let x = 1
@@ -1812,6 +1829,9 @@ func Test_missing_end()
endtry
call assert_equal(1, caught_e733)
+ " Using endfunc with :if
+ call assert_fails('exe "if 1 | endfunc | endif"', 'E193:')
+
" Missing 'in' in a :for statement
call assert_fails('for i range(1) | endfor', 'E690:')
endfunc
@@ -1858,6 +1878,15 @@ func Test_deep_nest()
@a
let @a = ''
endfunc
+
+ " Deep nesting of function ... endfunction
+ func Test5()
+ let @a = join(repeat(['function X()'], 51), "\n")
+ let @a ..= "\necho v:true\n"
+ let @a ..= join(repeat(['endfunction'], 51), "\n")
+ @a
+ let @a = ''
+ endfunc
[SCRIPT]
call writefile(lines, 'Xscript')
@@ -1865,20 +1894,31 @@ func Test_deep_nest()
" Deep nesting of if ... endif
call term_sendkeys(buf, ":call Test1()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E579:', term_getline(buf, 5))})
" Deep nesting of for ... endfor
call term_sendkeys(buf, ":call Test2()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
" Deep nesting of while ... endwhile
call term_sendkeys(buf, ":call Test3()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E585:', term_getline(buf, 5))})
" Deep nesting of try ... endtry
call term_sendkeys(buf, ":call Test4()\n")
+ call term_wait(buf)
call WaitForAssert({-> assert_match('^E601:', term_getline(buf, 5))})
+ " Deep nesting of function ... endfunction
+ call term_sendkeys(buf, ":call Test5()\n")
+ call term_wait(buf)
+ call WaitForAssert({-> assert_match('^E1058:', term_getline(buf, 4))})
+ call term_sendkeys(buf, "\<C-C>\n")
+ call term_wait(buf)
+
"let l = ''
"for i in range(1, 6)
" let l ..= term_getline(buf, i) . "\n"
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index f9ac0e0884..9c1ad0c099 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -378,14 +378,17 @@ endfunc
func Test_Visual_paragraph_textobject()
new
- call setline(1, ['First line.',
- \ '',
- \ 'Second line.',
- \ 'Third line.',
- \ 'Fourth line.',
- \ 'Fifth line.',
- \ '',
- \ 'Sixth line.'])
+ let lines =<< trim [END]
+ First line.
+
+ Second line.
+ Third line.
+ Fourth line.
+ Fifth line.
+
+ Sixth line.
+ [END]
+ call setline(1, lines)
" When start and end of visual area are identical, 'ap' or 'ip' select
" the whole paragraph.
@@ -639,6 +642,14 @@ func Test_characterwise_visual_mode()
normal Gkvj$d
call assert_equal(['', 'a', ''], getline(1, '$'))
+ " characterwise visual mode: use a count with the visual mode from the last
+ " line in the buffer
+ %d _
+ call setline(1, ['one', 'two', 'three', 'four'])
+ norm! vj$y
+ norm! G1vy
+ call assert_equal('four', @")
+
" characterwise visual mode: replace a single character line and the eol
%d _
call setline(1, "a")
@@ -653,92 +664,6 @@ func Test_characterwise_visual_mode()
bwipe!
endfunc
-func Test_characterwise_select_mode()
- new
-
- " Select mode maps
- snoremap <lt>End> <End>
- snoremap <lt>Down> <Down>
- snoremap <lt>Del> <Del>
-
- " characterwise select mode: delete middle line
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Gkkgh\<End>\<Del>"
- call assert_equal(['', 'b', 'c'], getline(1, '$'))
-
- " characterwise select mode: delete middle two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Gkkgh\<Down>\<End>\<Del>"
- call assert_equal(['', 'c'], getline(1, '$'))
-
- " characterwise select mode: delete last line
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Ggh\<End>\<Del>"
- call assert_equal(['', 'a', 'b', ''], getline(1, '$'))
-
- " characterwise select mode: delete last two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal Gkgh\<Down>\<End>\<Del>"
- call assert_equal(['', 'a', ''], getline(1, '$'))
-
- " CTRL-H in select mode behaves like 'x'
- call setline(1, 'abcdef')
- exe "normal! gggh\<Right>\<Right>\<Right>\<C-H>"
- call assert_equal('ef', getline(1))
-
- " CTRL-O in select mode switches to visual mode for one command
- call setline(1, 'abcdef')
- exe "normal! gggh\<C-O>3lm"
- call assert_equal('mef', getline(1))
-
- sunmap <lt>End>
- sunmap <lt>Down>
- sunmap <lt>Del>
- bwipe!
-endfunc
-
-func Test_linewise_select_mode()
- new
-
- " linewise select mode: delete middle line
- call append('$', ['a', 'b', 'c'])
- exe "normal GkkgH\<Del>"
- call assert_equal(['', 'b', 'c'], getline(1, '$'))
-
- " linewise select mode: delete middle two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal GkkgH\<Down>\<Del>"
- call assert_equal(['', 'c'], getline(1, '$'))
-
- " linewise select mode: delete last line
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal GgH\<Del>"
- call assert_equal(['', 'a', 'b'], getline(1, '$'))
-
- " linewise select mode: delete last two lines
- call deletebufline('', 1, '$')
- call append('$', ['a', 'b', 'c'])
- exe "normal GkgH\<Down>\<Del>"
- call assert_equal(['', 'a'], getline(1, '$'))
-
- bwipe!
-endfunc
-
-" Test for blockwise select mode (g CTRL-H)
-func Test_blockwise_select_mode()
- new
- call setline(1, ['foo', 'bar'])
- call feedkeys("g\<BS>\<Right>\<Down>mm", 'xt')
- call assert_equal(['mmo', 'mmr'], getline(1, '$'))
- close!
-endfunc
-
func Test_visual_mode_put()
new
@@ -778,16 +703,16 @@ func Test_visual_mode_put()
bwipe!
endfunc
-func Test_select_mode_gv()
+func Test_gv_with_exclusive_selection()
new
- " gv in exclusive select mode after operation
+ " gv with exclusive selection after an operation
call append('$', ['zzz ', 'äà '])
set selection=exclusive
normal Gkv3lyjv3lpgvcxxx
call assert_equal(['', 'zzz ', 'xxx '], getline(1, '$'))
- " gv in exclusive select mode without operation
+ " gv with exclusive selection without an operation
call deletebufline('', 1, '$')
call append('$', 'zzz ')
set selection=exclusive
@@ -1136,32 +1061,6 @@ func Test_star_register()
close!
endfunc
-" Test for using visual mode maps in select mode
-func Test_select_mode_map()
- new
- vmap <buffer> <F2> 3l
- call setline(1, 'Test line')
- call feedkeys("gh\<F2>map", 'xt')
- call assert_equal('map line', getline(1))
-
- vmap <buffer> <F2> ygV
- call feedkeys("0gh\<Right>\<Right>\<F2>cwabc", 'xt')
- call assert_equal('abc line', getline(1))
-
- vmap <buffer> <F2> :<C-U>let v=100<CR>
- call feedkeys("gggh\<Right>\<Right>\<F2>foo", 'xt')
- call assert_equal('foo line', getline(1))
-
- " reselect the select mode using gv from a visual mode map
- vmap <buffer> <F2> gv
- set selectmode=cmd
- call feedkeys("0gh\<F2>map", 'xt')
- call assert_equal('map line', getline(1))
- set selectmode&
-
- close!
-endfunc
-
" Test for changing text in visual mode with 'exclusive' selection
func Test_exclusive_selection()
new
@@ -1178,15 +1077,38 @@ func Test_exclusive_selection()
close!
endfunc
-" Test for starting visual mode with a count.
-" This test should be run without any previous visual modes. So this should be
-" run as a first test.
-func Test_AAA_start_visual_mode_with_count()
- new
- call setline(1, ['aaaaaaa', 'aaaaaaa', 'aaaaaaa', 'aaaaaaa'])
- normal! gg2Vy
- call assert_equal("aaaaaaa\naaaaaaa\n", @")
- close!
+" Test for starting linewise visual with a count.
+" This test needs to be run without any previous visual mode. Otherwise the
+" count will use the count from the previous visual mode.
+func Test_linewise_visual_with_count()
+ let after =<< trim [CODE]
+ call setline(1, ['one', 'two', 'three', 'four'])
+ norm! 3Vy
+ call assert_equal("one\ntwo\nthree\n", @")
+ call writefile(v:errors, 'Xtestout')
+ qall!
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal([], readfile('Xtestout'))
+ call delete('Xtestout')
+ endif
+endfunc
+
+" Test for starting characterwise visual with a count.
+" This test needs to be run without any previous visual mode. Otherwise the
+" count will use the count from the previous visual mode.
+func Test_characterwise_visual_with_count()
+ let after =<< trim [CODE]
+ call setline(1, ['one two', 'three'])
+ norm! l5vy
+ call assert_equal("ne tw", @")
+ call writefile(v:errors, 'Xtestout')
+ qall!
+ [CODE]
+ if RunVim([], after, '')
+ call assert_equal([], readfile('Xtestout'))
+ call delete('Xtestout')
+ endif
endfunc
" Test for visually selecting an inner block (iB)
@@ -1550,5 +1472,25 @@ func Test_visual_area_adjusted_when_hiding()
bwipe!
endfunc
+func Test_switch_buffer_ends_visual_mode()
+ enew
+ call setline(1, 'foo')
+ set hidden
+ set virtualedit=all
+ let buf1 = bufnr()
+ enew
+ let buf2 = bufnr()
+ call setline(1, ['', '', '', ''])
+ call cursor(4, 5)
+ call feedkeys("\<C-V>3k4h", 'xt')
+ exe 'buffer' buf1
+ call assert_equal('n', mode())
+
+ set nohidden
+ set virtualedit=
+ bwipe!
+ exe 'bwipe!' buf2
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index bfbba1f793..a8735bcaf1 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -128,6 +128,25 @@ func Test_nowrite_quit_split()
bwipe Xfile
endfunc
+func Test_writefile_sync_arg()
+ " This doesn't check if fsync() works, only that the argument is accepted.
+ call writefile(['one'], 'Xtest', 's')
+ call writefile(['two'], 'Xtest', 'S')
+ call delete('Xtest')
+endfunc
+
+func Test_writefile_sync_dev_stdout()
+ if !has('unix')
+ return
+ endif
+ if filewritable('/dev/stdout')
+ " Just check that this doesn't cause an error.
+ call writefile(['one'], '/dev/stdout', 's')
+ else
+ throw 'Skipped: /dev/stdout is not writable'
+ endif
+endfunc
+
func Test_writefile_autowrite()
set autowrite
new
@@ -237,29 +256,18 @@ func Test_write_errors()
call delete('Xfile')
endfunc
-func Test_writefile_sync_dev_stdout()
- if !has('unix')
- return
- endif
- if filewritable('/dev/stdout')
- " Just check that this doesn't cause an error.
- call writefile(['one'], '/dev/stdout', 's')
- else
- throw 'Skipped: /dev/stdout is not writable'
- endif
-endfunc
-
-func Test_writefile_sync_arg()
- " This doesn't check if fsync() works, only that the argument is accepted.
- call writefile(['one'], 'Xtest', 's')
- call writefile(['two'], 'Xtest', 'S')
- call delete('Xtest')
+" Test for writing a file using invalid file encoding
+func Test_write_invalid_encoding()
+ new
+ call setline(1, 'abc')
+ call assert_fails('write ++enc=axbyc Xfile', 'E213:')
+ close!
endfunc
" Tests for reading and writing files with conversion for Win32.
func Test_write_file_encoding()
- CheckMSWindows
throw 'skipped: Nvim does not support :w ++enc=cp1251'
+ CheckMSWindows
let save_encoding = &encoding
let save_fileencodings = &fileencodings
set encoding& fileencodings&
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 69b687e44f..4a252dca3e 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -7,6 +7,7 @@
#include "nvim/eval/encode.h"
#include "nvim/ex_docmd.h"
#include "nvim/os/os.h"
+#include "nvim/runtime.h"
#include "nvim/testing.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -17,21 +18,23 @@
static void prepare_assert_error(garray_T *gap)
{
char buf[NUMBUFLEN];
+ char *sname = estack_sfile(ESTACK_NONE);
ga_init(gap, 1, 100);
- if (sourcing_name != NULL) {
- ga_concat(gap, (char *)sourcing_name);
- if (sourcing_lnum > 0) {
+ if (sname != NULL) {
+ ga_concat(gap, sname);
+ if (SOURCING_LNUM > 0) {
ga_concat(gap, " ");
}
}
- if (sourcing_lnum > 0) {
- vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum);
+ if (SOURCING_LNUM > 0) {
+ vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)SOURCING_LNUM);
ga_concat(gap, buf);
}
- if (sourcing_name != NULL || sourcing_lnum > 0) {
+ if (sname != NULL || SOURCING_LNUM > 0) {
ga_concat(gap, ": ");
}
+ xfree(sname);
}
/// Append "p[clen]" to "gap", escaping unprintable characters.
@@ -323,19 +326,19 @@ static int assert_beeps(typval_T *argvars, bool no_beep)
}
/// "assert_beeps(cmd [, error])" function
-void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_beeps(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_beeps(argvars, false);
}
/// "assert_nobeep(cmd [, error])" function
-void f_assert_nobeep(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_nobeep(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_beeps(argvars, true);
}
/// "assert_equal(expected, actual[, msg])" function
-void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_equal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
}
@@ -430,19 +433,19 @@ static int assert_equalfile(typval_T *argvars)
}
/// "assert_equalfile(fname-one, fname-two[, msg])" function
-void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_equalfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_equalfile(argvars);
}
/// "assert_notequal(expected, actual[, msg])" function
-void f_assert_notequal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_notequal(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_equal_common(argvars, ASSERT_NOTEQUAL);
}
/// "assert_exception(string[, msg])" function
-void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_exception(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga;
@@ -454,7 +457,7 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
ga_clear(&ga);
rettv->vval.v_number = 1;
} else if (error != NULL
- && strstr((char *)get_vim_var_str(VV_EXCEPTION), error) == NULL) {
+ && strstr(get_vim_var_str(VV_EXCEPTION), error) == NULL) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[1], NULL, &argvars[0],
get_vim_var_tv(VV_EXCEPTION), ASSERT_OTHER);
@@ -465,7 +468,7 @@ void f_assert_exception(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "assert_fails(cmd [, error [, msg]])" function
-void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_fails(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
const char *const cmd = tv_get_string_chk(&argvars[0]);
garray_T ga;
@@ -490,7 +493,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const char *const error = tv_get_string_buf_chk(&argvars[1], buf);
if (error == NULL
- || strstr((char *)get_vim_var_str(VV_ERRMSG), error) == NULL) {
+ || strstr(get_vim_var_str(VV_ERRMSG), error) == NULL) {
prepare_assert_error(&ga);
fill_assert_error(&ga, &argvars[2], NULL, &argvars[1],
get_vim_var_tv(VV_ERRMSG), ASSERT_OTHER);
@@ -510,7 +513,7 @@ void f_assert_fails(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
// "assert_false(actual[, msg])" function
-void f_assert_false(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_false(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_bool(argvars, false);
}
@@ -571,25 +574,25 @@ static int assert_inrange(typval_T *argvars)
}
/// "assert_inrange(lower, upper[, msg])" function
-void f_assert_inrange(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_inrange(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_inrange(argvars);
}
/// "assert_match(pattern, actual[, msg])" function
-void f_assert_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_match_common(argvars, ASSERT_MATCH);
}
/// "assert_notmatch(pattern, actual[, msg])" function
-void f_assert_notmatch(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_notmatch(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_match_common(argvars, ASSERT_NOTMATCH);
}
/// "assert_report(msg)" function
-void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_report(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
garray_T ga;
@@ -601,13 +604,13 @@ void f_assert_report(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "assert_true(actual[, msg])" function
-void f_assert_true(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_assert_true(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = assert_bool(argvars, true);
}
/// "test_garbagecollect_now()" function
-void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
+void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
// This is dangerous, any Lists and Dicts used internally may be freed
// while still in use.
@@ -615,7 +618,7 @@ void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// "test_write_list_log()" function
-void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, FunPtr fptr)
+void f_test_write_list_log(typval_T *const argvars, typval_T *const rettv, EvalFuncData fptr)
{
const char *const fname = tv_get_string_chk(&argvars[0]);
if (fname == NULL) {
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
new file mode 100644
index 0000000000..125146f712
--- /dev/null
+++ b/src/nvim/textformat.c
@@ -0,0 +1,1127 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// textformat.c: text formatting functions
+
+#include <stdbool.h>
+
+#include "nvim/ascii.h"
+#include "nvim/change.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
+#include "nvim/edit.h"
+#include "nvim/eval.h"
+#include "nvim/getchar.h"
+#include "nvim/globals.h"
+#include "nvim/indent.h"
+#include "nvim/indent_c.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/move.h"
+#include "nvim/normal.h"
+#include "nvim/ops.h"
+#include "nvim/option.h"
+#include "nvim/os/input.h"
+#include "nvim/pos.h"
+#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
+#include "nvim/undo.h"
+#include "nvim/vim.h"
+#include "nvim/window.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textformat.c.generated.h"
+#endif
+
+static bool did_add_space = false; ///< auto_format() added an extra space
+ ///< under the cursor
+
+#define WHITECHAR(cc) (ascii_iswhite(cc) \
+ && !utf_iscomposing(utf_ptr2char((char *)get_cursor_pos_ptr() + 1)))
+
+/// Return true if format option 'x' is in effect.
+/// Take care of no formatting when 'paste' is set.
+bool has_format_option(int x)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (p_paste) {
+ return false;
+ }
+ return vim_strchr(curbuf->b_p_fo, x) != NULL;
+}
+
+/// Format text at the current insert position.
+///
+/// If the INSCHAR_COM_LIST flag is present, then the value of second_indent
+/// will be the comment leader length sent to open_line().
+///
+/// @param c character to be inserted (can be NUL)
+void internal_format(int textwidth, int second_indent, int flags, bool format_only, int c)
+{
+ int cc;
+ int save_char = NUL;
+ bool haveto_redraw = false;
+ const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
+ const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK);
+ const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
+ const bool fo_white_par = has_format_option(FO_WHITE_PAR);
+ bool first_line = true;
+ colnr_T leader_len;
+ bool no_leader = false;
+ int do_comments = (flags & INSCHAR_DO_COM);
+ int has_lbr = curwin->w_p_lbr;
+
+ // make sure win_lbr_chartabsize() counts correctly
+ curwin->w_p_lbr = false;
+
+ // When 'ai' is off we don't want a space under the cursor to be
+ // deleted. Replace it with an 'x' temporarily.
+ if (!curbuf->b_p_ai
+ && !(State & VREPLACE_FLAG)) {
+ cc = gchar_cursor();
+ if (ascii_iswhite(cc)) {
+ save_char = cc;
+ pchar_cursor('x');
+ }
+ }
+
+ // Repeat breaking lines, until the current line is not too long.
+ while (!got_int) {
+ int startcol; // Cursor column at entry
+ int wantcol; // column at textwidth border
+ int foundcol; // column for start of spaces
+ int end_foundcol = 0; // column for start of word
+ colnr_T len;
+ colnr_T virtcol;
+ int orig_col = 0;
+ char_u *saved_text = NULL;
+ colnr_T col;
+ colnr_T end_col;
+ bool did_do_comment = false;
+
+ virtcol = get_nolist_virtcol()
+ + char2cells(c != NUL ? c : gchar_cursor());
+ if (virtcol <= (colnr_T)textwidth) {
+ break;
+ }
+
+ if (no_leader) {
+ do_comments = false;
+ } else if (!(flags & INSCHAR_FORMAT)
+ && has_format_option(FO_WRAP_COMS)) {
+ do_comments = true;
+ }
+
+ // Don't break until after the comment leader
+ if (do_comments) {
+ char_u *line = get_cursor_line_ptr();
+ leader_len = get_leader_len((char *)line, NULL, false, true);
+ if (leader_len == 0 && curbuf->b_p_cin) {
+ // Check for a line comment after code.
+ int comment_start = check_linecomment((char *)line);
+ if (comment_start != MAXCOL) {
+ leader_len = get_leader_len((char *)line + comment_start, NULL, false, true);
+ if (leader_len != 0) {
+ leader_len += comment_start;
+ }
+ }
+ }
+ } else {
+ leader_len = 0;
+ }
+
+ // If the line doesn't start with a comment leader, then don't
+ // start one in a following broken line. Avoids that a %word
+ // moved to the start of the next line causes all following lines
+ // to start with %.
+ if (leader_len == 0) {
+ no_leader = true;
+ }
+ if (!(flags & INSCHAR_FORMAT)
+ && leader_len == 0
+ && !has_format_option(FO_WRAP)) {
+ break;
+ }
+ if ((startcol = curwin->w_cursor.col) == 0) {
+ break;
+ }
+
+ // find column of textwidth border
+ coladvance((colnr_T)textwidth);
+ wantcol = curwin->w_cursor.col;
+
+ curwin->w_cursor.col = startcol;
+ foundcol = 0;
+ int skip_pos = 0;
+
+ // Find position to break at.
+ // Stop at first entered white when 'formatoptions' has 'v'
+ while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
+ || (flags & INSCHAR_FORMAT)
+ || curwin->w_cursor.lnum != Insstart.lnum
+ || curwin->w_cursor.col >= Insstart.col) {
+ if (curwin->w_cursor.col == startcol && c != NUL) {
+ cc = c;
+ } else {
+ cc = gchar_cursor();
+ }
+ if (WHITECHAR(cc)) {
+ // remember position of blank just before text
+ end_col = curwin->w_cursor.col;
+
+ // find start of sequence of blanks
+ int wcc = 0; // counter for whitespace chars
+ while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
+ dec_cursor();
+ cc = gchar_cursor();
+
+ // Increment count of how many whitespace chars in this
+ // group; we only need to know if it's more than one.
+ if (wcc < 2) {
+ wcc++;
+ }
+ }
+ if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
+ break; // only spaces in front of text
+ }
+
+ // Don't break after a period when 'formatoptions' has 'p' and
+ // there are less than two spaces.
+ if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
+ continue;
+ }
+
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
+ break;
+ }
+
+ if (has_format_option(FO_ONE_LETTER)) {
+ // do not break after one-letter words
+ if (curwin->w_cursor.col == 0) {
+ break; // one-letter word at begin
+ }
+ // do not break "#a b" when 'tw' is 2
+ if (curwin->w_cursor.col <= leader_len) {
+ break;
+ }
+ col = curwin->w_cursor.col;
+ dec_cursor();
+ cc = gchar_cursor();
+
+ if (WHITECHAR(cc)) {
+ continue; // one-letter, continue
+ }
+ curwin->w_cursor.col = col;
+ }
+
+ inc_cursor();
+
+ end_foundcol = end_col + 1;
+ foundcol = curwin->w_cursor.col;
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ break;
+ }
+ } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) {
+ int ncc;
+ bool allow_break;
+
+ // Break after or before a multi-byte character.
+ if (curwin->w_cursor.col != startcol) {
+ // Don't break until after the comment leader
+ if (curwin->w_cursor.col < leader_len) {
+ break;
+ }
+ col = curwin->w_cursor.col;
+ inc_cursor();
+ ncc = gchar_cursor();
+ allow_break = utf_allow_break(cc, ncc);
+
+ // If we have already checked this position, skip!
+ if (curwin->w_cursor.col != skip_pos && allow_break) {
+ foundcol = curwin->w_cursor.col;
+ end_foundcol = foundcol;
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ break;
+ }
+ }
+ curwin->w_cursor.col = col;
+ }
+
+ if (curwin->w_cursor.col == 0) {
+ break;
+ }
+
+ ncc = cc;
+ col = curwin->w_cursor.col;
+
+ dec_cursor();
+ cc = gchar_cursor();
+
+ if (WHITECHAR(cc)) {
+ continue; // break with space
+ }
+ // Don't break until after the comment leader.
+ if (curwin->w_cursor.col < leader_len) {
+ break;
+ }
+
+ curwin->w_cursor.col = col;
+ skip_pos = curwin->w_cursor.col;
+
+ allow_break = utf_allow_break(cc, ncc);
+
+ // Must handle this to respect line break prohibition.
+ if (allow_break) {
+ foundcol = curwin->w_cursor.col;
+ end_foundcol = foundcol;
+ }
+ if (curwin->w_cursor.col <= (colnr_T)wantcol) {
+ const bool ncc_allow_break = utf_allow_break_before(ncc);
+
+ if (allow_break) {
+ break;
+ }
+ if (!ncc_allow_break && !fo_rigor_tw) {
+ // Enable at most 1 punct hang outside of textwidth.
+ if (curwin->w_cursor.col == startcol) {
+ // We are inserting a non-breakable char, postpone
+ // line break check to next insert.
+ end_foundcol = foundcol = 0;
+ break;
+ }
+
+ // Neither cc nor ncc is NUL if we are here, so
+ // it's safe to inc_cursor.
+ col = curwin->w_cursor.col;
+
+ inc_cursor();
+ cc = ncc;
+ ncc = gchar_cursor();
+ // handle insert
+ ncc = (ncc != NUL) ? ncc : c;
+
+ allow_break = utf_allow_break(cc, ncc);
+
+ if (allow_break) {
+ // Break only when we are not at end of line.
+ end_foundcol = foundcol = ncc == NUL? 0 : curwin->w_cursor.col;
+ break;
+ }
+ curwin->w_cursor.col = col;
+ }
+ }
+ }
+ if (curwin->w_cursor.col == 0) {
+ break;
+ }
+ dec_cursor();
+ }
+
+ if (foundcol == 0) { // no spaces, cannot break line
+ curwin->w_cursor.col = startcol;
+ break;
+ }
+
+ // Going to break the line, remove any "$" now.
+ undisplay_dollar();
+
+ // Offset between cursor position and line break is used by replace
+ // stack functions. MODE_VREPLACE does not use this, and backspaces
+ // over the text instead.
+ if (State & VREPLACE_FLAG) {
+ orig_col = startcol; // Will start backspacing from here
+ } else {
+ replace_offset = startcol - end_foundcol;
+ }
+
+ // adjust startcol for spaces that will be deleted and
+ // characters that will remain on top line
+ curwin->w_cursor.col = foundcol;
+ while ((cc = gchar_cursor(), WHITECHAR(cc))
+ && (!fo_white_par || curwin->w_cursor.col < startcol)) {
+ inc_cursor();
+ }
+ startcol -= curwin->w_cursor.col;
+ if (startcol < 0) {
+ startcol = 0;
+ }
+
+ if (State & VREPLACE_FLAG) {
+ // In MODE_VREPLACE state, we will backspace over the text to be
+ // wrapped, so save a copy now to put on the next line.
+ saved_text = vim_strsave(get_cursor_pos_ptr());
+ curwin->w_cursor.col = orig_col;
+ saved_text[startcol] = NUL;
+
+ // Backspace over characters that will move to the next line
+ if (!fo_white_par) {
+ backspace_until_column(foundcol);
+ }
+ } else {
+ // put cursor after pos. to break line
+ if (!fo_white_par) {
+ curwin->w_cursor.col = foundcol;
+ }
+ }
+
+ // Split the line just before the margin.
+ // Only insert/delete lines, but don't really redraw the window.
+ open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
+ + (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
+ + (do_comments ? OPENLINE_DO_COM : 0)
+ + OPENLINE_FORMAT
+ + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0),
+ ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent),
+ &did_do_comment);
+ if (!(flags & INSCHAR_COM_LIST)) {
+ old_indent = 0;
+ }
+
+ // If a comment leader was inserted, may also do this on a following
+ // line.
+ if (did_do_comment) {
+ no_leader = false;
+ }
+
+ replace_offset = 0;
+ if (first_line) {
+ if (!(flags & INSCHAR_COM_LIST)) {
+ // This section is for auto-wrap of numeric lists. When not
+ // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
+ // flag will be set and open_line() will handle it (as seen
+ // above). The code here (and in get_number_indent()) will
+ // recognize comments if needed...
+ if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) {
+ second_indent = get_number_indent(curwin->w_cursor.lnum - 1);
+ }
+ if (second_indent >= 0) {
+ if (State & VREPLACE_FLAG) {
+ change_indent(INDENT_SET, second_indent, false, NUL, true);
+ } else if (leader_len > 0 && second_indent - leader_len > 0) {
+ int padding = second_indent - leader_len;
+
+ // We started at the first_line of a numbered list
+ // that has a comment. the open_line() function has
+ // inserted the proper comment leader and positioned
+ // the cursor at the end of the split line. Now we
+ // add the additional whitespace needed after the
+ // comment leader for the numbered list.
+ for (int i = 0; i < padding; i++) {
+ ins_str(" ");
+ }
+ changed_bytes(curwin->w_cursor.lnum, leader_len);
+ } else {
+ (void)set_indent(second_indent, SIN_CHANGED);
+ }
+ }
+ }
+ first_line = false;
+ }
+
+ if (State & VREPLACE_FLAG) {
+ // In MODE_VREPLACE state we have backspaced over the text to be
+ // moved, now we re-insert it into the new line.
+ ins_bytes((char *)saved_text);
+ xfree(saved_text);
+ } else {
+ // Check if cursor is not past the NUL off the line, cindent
+ // may have added or removed indent.
+ curwin->w_cursor.col += startcol;
+ len = (colnr_T)STRLEN(get_cursor_line_ptr());
+ if (curwin->w_cursor.col > len) {
+ curwin->w_cursor.col = len;
+ }
+ }
+
+ haveto_redraw = true;
+ set_can_cindent(true);
+ // moved the cursor, don't autoindent or cindent now
+ did_ai = false;
+ did_si = false;
+ can_si = false;
+ can_si_back = false;
+ line_breakcheck();
+ }
+
+ if (save_char != NUL) { // put back space after cursor
+ pchar_cursor((char_u)save_char);
+ }
+
+ curwin->w_p_lbr = has_lbr;
+
+ if (!format_only && haveto_redraw) {
+ update_topline(curwin);
+ redraw_curbuf_later(UPD_VALID);
+ }
+}
+
+/// Blank lines, and lines containing only the comment leader, are left
+/// untouched by the formatting. The function returns true in this
+/// case. It also returns true when a line starts with the end of a comment
+/// ('e' in comment flags), so that this line is skipped, and not joined to the
+/// previous line. A new paragraph starts after a blank line, or when the
+/// comment leader changes.
+static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, bool do_comments)
+{
+ char_u *flags = NULL; // init for GCC
+ char_u *ptr;
+
+ ptr = ml_get(lnum);
+ if (do_comments) {
+ *leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
+ } else {
+ *leader_len = 0;
+ }
+
+ if (*leader_len > 0) {
+ // Search for 'e' flag in comment leader flags.
+ flags = *leader_flags;
+ while (*flags && *flags != ':' && *flags != COM_END) {
+ flags++;
+ }
+ }
+
+ return *skipwhite((char *)ptr + *leader_len) == NUL
+ || (*leader_len > 0 && *flags == COM_END)
+ || startPS(lnum, NUL, false);
+}
+
+/// @return true if line "lnum" ends in a white character.
+static bool ends_in_white(linenr_T lnum)
+{
+ char_u *s = ml_get(lnum);
+ size_t l;
+
+ if (*s == NUL) {
+ return false;
+ }
+ l = STRLEN(s) - 1;
+ return ascii_iswhite(s[l]);
+}
+
+/// @return true if the two comment leaders given are the same.
+///
+/// @param lnum The first line. White-space is ignored.
+///
+/// @note the whole of 'leader1' must match 'leader2_len' characters from 'leader2'.
+static bool same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, int leader2_len,
+ char_u *leader2_flags)
+{
+ int idx1 = 0, idx2 = 0;
+ char_u *p;
+ char_u *line1;
+ char_u *line2;
+
+ if (leader1_len == 0) {
+ return leader2_len == 0;
+ }
+
+ // If first leader has 'f' flag, the lines can be joined only if the
+ // second line does not have a leader.
+ // If first leader has 'e' flag, the lines can never be joined.
+ // If first leader has 's' flag, the lines can only be joined if there is
+ // some text after it and the second line has the 'm' flag.
+ if (leader1_flags != NULL) {
+ for (p = leader1_flags; *p && *p != ':'; p++) {
+ if (*p == COM_FIRST) {
+ return leader2_len == 0;
+ }
+ if (*p == COM_END) {
+ return false;
+ }
+ if (*p == COM_START) {
+ if (*(ml_get(lnum) + leader1_len) == NUL) {
+ return false;
+ }
+ if (leader2_flags == NULL || leader2_len == 0) {
+ return false;
+ }
+ for (p = leader2_flags; *p && *p != ':'; p++) {
+ if (*p == COM_MIDDLE) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+ // Get current line and next line, compare the leaders.
+ // The first line has to be saved, only one line can be locked at a time.
+ line1 = vim_strsave(ml_get(lnum));
+ for (idx1 = 0; ascii_iswhite(line1[idx1]); idx1++) {}
+ line2 = ml_get(lnum + 1);
+ for (idx2 = 0; idx2 < leader2_len; idx2++) {
+ if (!ascii_iswhite(line2[idx2])) {
+ if (line1[idx1++] != line2[idx2]) {
+ break;
+ }
+ } else {
+ while (ascii_iswhite(line1[idx1])) {
+ idx1++;
+ }
+ }
+ }
+ xfree(line1);
+
+ return idx2 == leader2_len && idx1 == leader1_len;
+}
+
+/// Used for auto-formatting.
+///
+/// @return true when a paragraph starts in line "lnum".
+/// false when the previous line is in the same paragraph.
+static bool paragraph_start(linenr_T lnum)
+{
+ char_u *p;
+ int leader_len = 0; // leader len of current line
+ char_u *leader_flags = NULL; // flags for leader of current line
+ int next_leader_len = 0; // leader len of next line
+ char_u *next_leader_flags = NULL; // flags for leader of next line
+
+ if (lnum <= 1) {
+ return true; // start of the file
+ }
+ p = ml_get(lnum - 1);
+ if (*p == NUL) {
+ return true; // after empty line
+ }
+ const bool do_comments = has_format_option(FO_Q_COMS); // format comments
+ if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
+ return true; // after non-paragraph line
+ }
+
+ if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
+ return true; // "lnum" is not a paragraph line
+ }
+
+ if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) {
+ return true; // missing trailing space in previous line.
+ }
+ if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) {
+ return true; // numbered item starts in "lnum".
+ }
+ if (!same_leader(lnum - 1, leader_len, leader_flags,
+ next_leader_len, next_leader_flags)) {
+ return true; // change of comment leader.
+ }
+ return false;
+}
+
+/// Called after inserting or deleting text: When 'formatoptions' includes the
+/// 'a' flag format from the current line until the end of the paragraph.
+/// Keep the cursor at the same position relative to the text.
+/// The caller must have saved the cursor line for undo, following ones will be
+/// saved here.
+///
+/// @param trailblank when true also format with trailing blank
+/// @param prev_line may start in previous line
+void auto_format(bool trailblank, bool prev_line)
+{
+ pos_T pos;
+ colnr_T len;
+ char_u *old;
+ char_u *new, *pnew;
+ int wasatend;
+ int cc;
+
+ if (!has_format_option(FO_AUTO)) {
+ return;
+ }
+
+ pos = curwin->w_cursor;
+ old = get_cursor_line_ptr();
+
+ // may remove added space
+ check_auto_format(false);
+
+ // Don't format in Insert mode when the cursor is on a trailing blank, the
+ // user might insert normal text next. Also skip formatting when "1" is
+ // in 'formatoptions' and there is a single character before the cursor.
+ // Otherwise the line would be broken and when typing another non-white
+ // next they are not joined back together.
+ wasatend = (pos.col == (colnr_T)STRLEN(old));
+ if (*old != NUL && !trailblank && wasatend) {
+ dec_cursor();
+ cc = gchar_cursor();
+ if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
+ && has_format_option(FO_ONE_LETTER)) {
+ dec_cursor();
+ }
+ cc = gchar_cursor();
+ if (WHITECHAR(cc)) {
+ curwin->w_cursor = pos;
+ return;
+ }
+ curwin->w_cursor = pos;
+ }
+
+ // With the 'c' flag in 'formatoptions' and 't' missing: only format
+ // comments.
+ if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
+ && get_leader_len((char *)old, NULL, false, true) == 0) {
+ return;
+ }
+
+ // May start formatting in a previous line, so that after "x" a word is
+ // moved to the previous line if it fits there now. Only when this is not
+ // the start of a paragraph.
+ if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) {
+ curwin->w_cursor.lnum--;
+ if (u_save_cursor() == FAIL) {
+ return;
+ }
+ }
+
+ // Do the formatting and restore the cursor position. "saved_cursor" will
+ // be adjusted for the text formatting.
+ saved_cursor = pos;
+ format_lines((linenr_T) - 1, false);
+ curwin->w_cursor = saved_cursor;
+ saved_cursor.lnum = 0;
+
+ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
+ // "cannot happen"
+ curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
+ coladvance(MAXCOL);
+ } else {
+ check_cursor_col();
+ }
+
+ // Insert mode: If the cursor is now after the end of the line while it
+ // previously wasn't, the line was broken. Because of the rule above we
+ // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
+ // formatted.
+ if (!wasatend && has_format_option(FO_WHITE_PAR)) {
+ new = get_cursor_line_ptr();
+ len = (colnr_T)STRLEN(new);
+ if (curwin->w_cursor.col == len) {
+ pnew = vim_strnsave(new, (size_t)len + 2);
+ pnew[len] = ' ';
+ pnew[len + 1] = NUL;
+ ml_replace(curwin->w_cursor.lnum, (char *)pnew, false);
+ // remove the space later
+ did_add_space = true;
+ } else {
+ // may remove added space
+ check_auto_format(false);
+ }
+ }
+
+ check_cursor();
+}
+
+/// When an extra space was added to continue a paragraph for auto-formatting,
+/// delete it now. The space must be under the cursor, just after the insert
+/// position.
+///
+/// @param end_insert true when ending Insert mode
+void check_auto_format(bool end_insert)
+{
+ int c = ' ';
+ int cc;
+
+ if (did_add_space) {
+ cc = gchar_cursor();
+ if (!WHITECHAR(cc)) {
+ // Somehow the space was removed already.
+ did_add_space = false;
+ } else {
+ if (!end_insert) {
+ inc_cursor();
+ c = gchar_cursor();
+ dec_cursor();
+ }
+ if (c != NUL) {
+ // The space is no longer at the end of the line, delete it.
+ del_char(false);
+ did_add_space = false;
+ }
+ }
+ }
+}
+
+/// Find out textwidth to be used for formatting:
+/// if 'textwidth' option is set, use it
+/// else if 'wrapmargin' option is set, use curwin->w_width_inner-'wrapmargin'
+/// if invalid value, use 0.
+/// Set default to window width (maximum 79) for "gq" operator.
+///
+/// @param ff force formatting (for "gq" command)
+int comp_textwidth(bool ff)
+{
+ int textwidth = (int)curbuf->b_p_tw;
+ if (textwidth == 0 && curbuf->b_p_wm) {
+ // The width is the window width minus 'wrapmargin' minus all the
+ // things that add to the margin.
+ textwidth = curwin->w_width_inner - (int)curbuf->b_p_wm;
+ if (cmdwin_type != 0) {
+ textwidth -= 1;
+ }
+ textwidth -= win_fdccol_count(curwin);
+ textwidth -= win_signcol_count(curwin);
+
+ if (curwin->w_p_nu || curwin->w_p_rnu) {
+ textwidth -= 8;
+ }
+ }
+ if (textwidth < 0) {
+ textwidth = 0;
+ }
+ if (ff && textwidth == 0) {
+ textwidth = curwin->w_width_inner - 1;
+ if (textwidth > 79) {
+ textwidth = 79;
+ }
+ }
+ return textwidth;
+}
+
+/// Implementation of the format operator 'gq'.
+///
+/// @param keep_cursor keep cursor on same text char
+void op_format(oparg_T *oap, bool keep_cursor)
+{
+ linenr_T old_line_count = curbuf->b_ml.ml_line_count;
+
+ // Place the cursor where the "gq" or "gw" command was given, so that "u"
+ // can put it back there.
+ curwin->w_cursor = oap->cursor_start;
+
+ if (u_save((linenr_T)(oap->start.lnum - 1),
+ (linenr_T)(oap->end.lnum + 1)) == FAIL) {
+ return;
+ }
+ curwin->w_cursor = oap->start;
+
+ if (oap->is_VIsual) {
+ // When there is no change: need to remove the Visual selection
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // Set '[ mark at the start of the formatted area
+ curbuf->b_op_start = oap->start;
+ }
+
+ // For "gw" remember the cursor position and put it back below (adjusted
+ // for joined and split lines).
+ if (keep_cursor) {
+ saved_cursor = oap->cursor_start;
+ }
+
+ format_lines((linenr_T)oap->line_count, keep_cursor);
+
+ // Leave the cursor at the first non-blank of the last formatted line.
+ // If the cursor was moved one line back (e.g. with "Q}") go to the next
+ // line, so "." will do the next lines.
+ if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum++;
+ }
+ beginline(BL_WHITE | BL_FIX);
+ old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
+ msgmore(old_line_count);
+
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // put '] mark on the end of the formatted area
+ curbuf->b_op_end = curwin->w_cursor;
+ }
+
+ if (keep_cursor) {
+ curwin->w_cursor = saved_cursor;
+ saved_cursor.lnum = 0;
+
+ // formatting may have made the cursor position invalid
+ check_cursor();
+ }
+
+ if (oap->is_VIsual) {
+ FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
+ if (wp->w_old_cursor_lnum != 0) {
+ // When lines have been inserted or deleted, adjust the end of
+ // the Visual area to be redrawn.
+ if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) {
+ wp->w_old_cursor_lnum += old_line_count;
+ } else {
+ wp->w_old_visual_lnum += old_line_count;
+ }
+ }
+ }
+ }
+}
+
+/// Implementation of the format operator 'gq' for when using 'formatexpr'.
+void op_formatexpr(oparg_T *oap)
+{
+ if (oap->is_VIsual) {
+ // When there is no change: need to remove the Visual selection
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+
+ if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) {
+ // As documented: when 'formatexpr' returns non-zero fall back to
+ // internal formatting.
+ op_format(oap, false);
+ }
+}
+
+/// @param c character to be inserted
+int fex_format(linenr_T lnum, long count, int c)
+{
+ int use_sandbox = was_set_insecurely(curwin, "formatexpr", OPT_LOCAL);
+ int r;
+
+ // Set v:lnum to the first line number and v:count to the number of lines.
+ // Set v:char to the character to be inserted (can be NUL).
+ set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
+ set_vim_var_nr(VV_COUNT, (varnumber_T)count);
+ set_vim_var_char(c);
+
+ // Make a copy, the option could be changed while calling it.
+ char *fex = xstrdup(curbuf->b_p_fex);
+ // Evaluate the function.
+ if (use_sandbox) {
+ sandbox++;
+ }
+ r = (int)eval_to_number(fex);
+ if (use_sandbox) {
+ sandbox--;
+ }
+
+ set_vim_var_string(VV_CHAR, NULL, -1);
+ xfree(fex);
+
+ return r;
+}
+
+/// @param line_count number of lines to format, starting at the cursor position.
+/// when negative, format until the end of the paragraph.
+///
+/// Lines after the cursor line are saved for undo, caller must have saved the
+/// first line.
+///
+/// @param avoid_fex don't use 'formatexpr'
+void format_lines(linenr_T line_count, bool avoid_fex)
+{
+ bool is_not_par; // current line not part of parag.
+ bool next_is_not_par; // next line not part of paragraph
+ bool is_end_par; // at end of paragraph
+ bool prev_is_end_par = false; // prev. line not part of parag.
+ bool next_is_start_par = false;
+ int leader_len = 0; // leader len of current line
+ int next_leader_len; // leader len of next line
+ char_u *leader_flags = NULL; // flags for leader of current line
+ char_u *next_leader_flags = NULL; // flags for leader of next line
+ bool advance = true;
+ int second_indent = -1; // indent for second line (comment aware)
+ bool first_par_line = true;
+ int smd_save;
+ long count;
+ bool need_set_indent = true; // set indent of next paragraph
+ linenr_T first_line = curwin->w_cursor.lnum;
+ bool force_format = false;
+ const int old_State = State;
+
+ // length of a line to force formatting: 3 * 'tw'
+ const int max_len = comp_textwidth(true) * 3;
+
+ // check for 'q', '2' and '1' in 'formatoptions'
+ const bool do_comments = has_format_option(FO_Q_COMS); // format comments
+ int do_comments_list = 0; // format comments with 'n' or '2'
+ const bool do_second_indent = has_format_option(FO_Q_SECOND);
+ const bool do_number_indent = has_format_option(FO_Q_NUMBER);
+ const bool do_trail_white = has_format_option(FO_WHITE_PAR);
+
+ // Get info about the previous and current line.
+ if (curwin->w_cursor.lnum > 1) {
+ is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
+ &leader_len, &leader_flags, do_comments);
+ } else {
+ is_not_par = true;
+ }
+ next_is_not_par = fmt_check_par(curwin->w_cursor.lnum,
+ &next_leader_len, &next_leader_flags, do_comments);
+ is_end_par = (is_not_par || next_is_not_par);
+ if (!is_end_par && do_trail_white) {
+ is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
+ }
+
+ curwin->w_cursor.lnum--;
+ for (count = line_count; count != 0 && !got_int; count--) {
+ // Advance to next paragraph.
+ if (advance) {
+ curwin->w_cursor.lnum++;
+ prev_is_end_par = is_end_par;
+ is_not_par = next_is_not_par;
+ leader_len = next_leader_len;
+ leader_flags = next_leader_flags;
+ }
+
+ // The last line to be formatted.
+ if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
+ next_is_not_par = true;
+ next_leader_len = 0;
+ next_leader_flags = NULL;
+ } else {
+ next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1,
+ &next_leader_len, &next_leader_flags, do_comments);
+ if (do_number_indent) {
+ next_is_start_par =
+ (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
+ }
+ }
+ advance = true;
+ is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
+ if (!is_end_par && do_trail_white) {
+ is_end_par = !ends_in_white(curwin->w_cursor.lnum);
+ }
+
+ // Skip lines that are not in a paragraph.
+ if (is_not_par) {
+ if (line_count < 0) {
+ break;
+ }
+ } else {
+ // For the first line of a paragraph, check indent of second line.
+ // Don't do this for comments and empty lines.
+ if (first_par_line
+ && (do_second_indent || do_number_indent)
+ && prev_is_end_par
+ && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
+ if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
+ if (leader_len == 0 && next_leader_len == 0) {
+ // no comment found
+ second_indent =
+ get_indent_lnum(curwin->w_cursor.lnum + 1);
+ } else {
+ second_indent = next_leader_len;
+ do_comments_list = 1;
+ }
+ } else if (do_number_indent) {
+ if (leader_len == 0 && next_leader_len == 0) {
+ // no comment found
+ second_indent =
+ get_number_indent(curwin->w_cursor.lnum);
+ } else {
+ // get_number_indent() is now "comment aware"...
+ second_indent =
+ get_number_indent(curwin->w_cursor.lnum);
+ do_comments_list = 1;
+ }
+ }
+ }
+
+ // When the comment leader changes, it's the end of the paragraph.
+ if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
+ || !same_leader(curwin->w_cursor.lnum,
+ leader_len, leader_flags,
+ next_leader_len,
+ next_leader_flags)) {
+ // Special case: If the next line starts with a line comment
+ // and this line has a line comment after some text, the
+ // paragraph doesn't really end.
+ if (next_leader_flags == NULL
+ || STRNCMP(next_leader_flags, "://", 3) != 0
+ || check_linecomment((char *)get_cursor_line_ptr()) == MAXCOL) {
+ is_end_par = true;
+ }
+ }
+
+ // If we have got to the end of a paragraph, or the line is
+ // getting long, format it.
+ if (is_end_par || force_format) {
+ if (need_set_indent) {
+ int indent = 0; // amount of indent needed
+
+ // Replace indent in first line of a paragraph with minimal
+ // number of tabs and spaces, according to current options.
+ // For the very first formatted line keep the current
+ // indent.
+ if (curwin->w_cursor.lnum == first_line) {
+ indent = get_indent();
+ } else if (curbuf->b_p_lisp) {
+ indent = get_lisp_indent();
+ } else {
+ if (cindent_on()) {
+ indent = *curbuf->b_p_inde != NUL ? get_expr_indent() : get_c_indent();
+ } else {
+ indent = get_indent();
+ }
+ }
+ (void)set_indent(indent, SIN_CHANGED);
+ }
+
+ // put cursor on last non-space
+ State = MODE_NORMAL; // don't go past end-of-line
+ coladvance(MAXCOL);
+ while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) {
+ dec_cursor();
+ }
+
+ // do the formatting, without 'showmode'
+ State = MODE_INSERT; // for open_line()
+ smd_save = p_smd;
+ p_smd = false;
+ insertchar(NUL, INSCHAR_FORMAT
+ + (do_comments ? INSCHAR_DO_COM : 0)
+ + (do_comments && do_comments_list ? INSCHAR_COM_LIST : 0)
+ + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
+ State = old_State;
+ p_smd = smd_save;
+ second_indent = -1;
+ // at end of par.: need to set indent of next par.
+ need_set_indent = is_end_par;
+ if (is_end_par) {
+ // When called with a negative line count, break at the
+ // end of the paragraph.
+ if (line_count < 0) {
+ break;
+ }
+ first_par_line = true;
+ }
+ force_format = false;
+ }
+
+ // When still in same paragraph, join the lines together. But
+ // first delete the leader from the second line.
+ if (!is_end_par) {
+ advance = false;
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0;
+ if (line_count < 0 && u_save_cursor() == FAIL) {
+ break;
+ }
+ if (next_leader_len > 0) {
+ (void)del_bytes(next_leader_len, false, false);
+ mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
+ (long)-next_leader_len, 0);
+ } else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
+ int indent = (int)getwhitecols_curline();
+
+ if (indent > 0) {
+ (void)del_bytes(indent, false, false);
+ mark_col_adjust(curwin->w_cursor.lnum,
+ (colnr_T)0, 0L, (long)-indent, 0);
+ }
+ }
+ curwin->w_cursor.lnum--;
+ if (do_join(2, true, false, false, false) == FAIL) {
+ beep_flush();
+ break;
+ }
+ first_par_line = false;
+ // If the line is getting long, format it next time
+ if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) {
+ force_format = true;
+ } else {
+ force_format = false;
+ }
+ }
+ }
+ line_breakcheck();
+ }
+}
diff --git a/src/nvim/textformat.h b/src/nvim/textformat.h
new file mode 100644
index 0000000000..3c918a028b
--- /dev/null
+++ b/src/nvim/textformat.h
@@ -0,0 +1,10 @@
+#ifndef NVIM_TEXTFORMAT_H
+#define NVIM_TEXTFORMAT_H
+
+#include "nvim/normal.h" // for oparg_T
+#include "nvim/pos.h" // for linenr_T
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textformat.h.generated.h"
+#endif
+#endif // NVIM_TEXTFORMAT_H
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
new file mode 100644
index 0000000000..02174edeb1
--- /dev/null
+++ b/src/nvim/textobject.c
@@ -0,0 +1,1742 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// textobject.c: functions for text objects
+
+#include <stdbool.h>
+
+#include "nvim/ascii.h"
+#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
+#include "nvim/edit.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/fold.h"
+#include "nvim/globals.h"
+#include "nvim/indent.h"
+#include "nvim/mark.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/normal.h"
+#include "nvim/pos.h"
+#include "nvim/search.h"
+#include "nvim/textformat.h"
+#include "nvim/textobject.h"
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textobject.c.generated.h"
+#endif
+
+/// Find the start of the next sentence, searching in the direction specified
+/// by the "dir" argument. The cursor is positioned on the start of the next
+/// sentence when found. If the next sentence is found, return OK. Return FAIL
+/// otherwise. See ":h sentence" for the precise definition of a "sentence"
+/// text object.
+int findsent(Direction dir, long count)
+{
+ pos_T pos, tpos;
+ int c;
+ int (*func)(pos_T *);
+ bool noskip = false; // do not skip blanks
+
+ pos = curwin->w_cursor;
+ if (dir == FORWARD) {
+ func = incl;
+ } else {
+ func = decl;
+ }
+
+ while (count--) {
+ const pos_T prev_pos = pos;
+
+ // if on an empty line, skip up to a non-empty line
+ if (gchar_pos(&pos) == NUL) {
+ do {
+ if ((*func)(&pos) == -1) {
+ break;
+ }
+ } while (gchar_pos(&pos) == NUL);
+ if (dir == FORWARD) {
+ goto found;
+ }
+ // if on the start of a paragraph or a section and searching forward,
+ // go to the next line
+ } else if (dir == FORWARD && pos.col == 0
+ && startPS(pos.lnum, NUL, false)) {
+ if (pos.lnum == curbuf->b_ml.ml_line_count) {
+ return FAIL;
+ }
+ pos.lnum++;
+ goto found;
+ } else if (dir == BACKWARD) {
+ decl(&pos);
+ }
+
+ // go back to the previous non-white non-punctuation character
+ bool found_dot = false;
+ while (c = gchar_pos(&pos), ascii_iswhite(c)
+ || vim_strchr(".!?)]\"'", c) != NULL) {
+ tpos = pos;
+ if (decl(&tpos) == -1 || (LINEEMPTY(tpos.lnum) && dir == FORWARD)) {
+ break;
+ }
+ if (found_dot) {
+ break;
+ }
+ if (vim_strchr(".!?", c) != NULL) {
+ found_dot = true;
+ }
+ if (vim_strchr(")]\"'", c) != NULL
+ && vim_strchr(".!?)]\"'", gchar_pos(&tpos)) == NULL) {
+ break;
+ }
+ decl(&pos);
+ }
+
+ // remember the line where the search started
+ const int startlnum = pos.lnum;
+ const bool cpo_J = vim_strchr(p_cpo, CPO_ENDOFSENT) != NULL;
+
+ for (;;) { // find end of sentence
+ c = gchar_pos(&pos);
+ if (c == NUL || (pos.col == 0 && startPS(pos.lnum, NUL, false))) {
+ if (dir == BACKWARD && pos.lnum != startlnum) {
+ pos.lnum++;
+ }
+ break;
+ }
+ if (c == '.' || c == '!' || c == '?') {
+ tpos = pos;
+ do {
+ if ((c = inc(&tpos)) == -1) {
+ break;
+ }
+ } while (vim_strchr(")]\"'", c = gchar_pos(&tpos))
+ != NULL);
+ if (c == -1 || (!cpo_J && (c == ' ' || c == '\t')) || c == NUL
+ || (cpo_J && (c == ' ' && inc(&tpos) >= 0
+ && gchar_pos(&tpos) == ' '))) {
+ pos = tpos;
+ if (gchar_pos(&pos) == NUL) { // skip NUL at EOL
+ inc(&pos);
+ }
+ break;
+ }
+ }
+ if ((*func)(&pos) == -1) {
+ if (count) {
+ return FAIL;
+ }
+ noskip = true;
+ break;
+ }
+ }
+found:
+ // skip white space
+ while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t')) {
+ if (incl(&pos) == -1) {
+ break;
+ }
+ }
+
+ if (equalpos(prev_pos, pos)) {
+ // didn't actually move, advance one character and try again
+ if ((*func)(&pos) == -1) {
+ if (count) {
+ return FAIL;
+ }
+ break;
+ }
+ count++;
+ }
+ }
+
+ setpcmark();
+ curwin->w_cursor = pos;
+ return OK;
+}
+
+/// Find the next paragraph or section in direction 'dir'.
+/// Paragraphs are currently supposed to be separated by empty lines.
+/// If 'what' is NUL we go to the next paragraph.
+/// If 'what' is '{' or '}' we go to the next section.
+/// If 'both' is true also stop at '}'.
+///
+/// @param pincl Return: true if last char is to be included
+///
+/// @return true if the next paragraph or section was found.
+bool findpar(bool *pincl, int dir, long count, int what, bool both)
+{
+ linenr_T curr;
+ bool did_skip; // true after separating lines have been skipped
+ bool first; // true on first line
+ linenr_T fold_first; // first line of a closed fold
+ linenr_T fold_last; // last line of a closed fold
+ bool fold_skipped; // true if a closed fold was skipped this
+ // iteration
+
+ curr = curwin->w_cursor.lnum;
+
+ while (count--) {
+ did_skip = false;
+ for (first = true;; first = false) {
+ if (*ml_get(curr) != NUL) {
+ did_skip = true;
+ }
+
+ // skip folded lines
+ fold_skipped = false;
+ if (first && hasFolding(curr, &fold_first, &fold_last)) {
+ curr = ((dir > 0) ? fold_last : fold_first) + dir;
+ fold_skipped = true;
+ }
+
+ if (!first && did_skip && startPS(curr, what, both)) {
+ break;
+ }
+
+ if (fold_skipped) {
+ curr -= dir;
+ }
+ if ((curr += dir) < 1 || curr > curbuf->b_ml.ml_line_count) {
+ if (count) {
+ return false;
+ }
+ curr -= dir;
+ break;
+ }
+ }
+ }
+ setpcmark();
+ if (both && *ml_get(curr) == '}') { // include line with '}'
+ curr++;
+ }
+ curwin->w_cursor.lnum = curr;
+ if (curr == curbuf->b_ml.ml_line_count && what != '}') {
+ char_u *line = ml_get(curr);
+
+ // Put the cursor on the last character in the last line and make the
+ // motion inclusive.
+ if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) {
+ curwin->w_cursor.col--;
+ curwin->w_cursor.col -= utf_head_off(line, line + curwin->w_cursor.col);
+ *pincl = true;
+ }
+ } else {
+ curwin->w_cursor.col = 0;
+ }
+ return true;
+}
+
+/// check if the string 's' is a nroff macro that is in option 'opt'
+static bool inmacro(char_u *opt, char_u *s)
+{
+ char_u *macro;
+
+ for (macro = opt; macro[0]; macro++) {
+ // Accept two characters in the option being equal to two characters
+ // in the line. A space in the option matches with a space in the
+ // line or the line having ended.
+ if ((macro[0] == s[0]
+ || (macro[0] == ' '
+ && (s[0] == NUL || s[0] == ' ')))
+ && (macro[1] == s[1]
+ || ((macro[1] == NUL || macro[1] == ' ')
+ && (s[0] == NUL || s[1] == NUL || s[1] == ' ')))) {
+ break;
+ }
+ macro++;
+ if (macro[0] == NUL) {
+ break;
+ }
+ }
+ return macro[0] != NUL;
+}
+
+/// startPS: return true if line 'lnum' is the start of a section or paragraph.
+/// If 'para' is '{' or '}' only check for sections.
+/// If 'both' is true also stop at '}'
+bool startPS(linenr_T lnum, int para, bool both)
+{
+ char_u *s;
+
+ s = ml_get(lnum);
+ if (*s == para || *s == '\f' || (both && *s == '}')) {
+ return true;
+ }
+ if (*s == '.' && (inmacro((char_u *)p_sections, s + 1)
+ || (!para && inmacro(p_para, s + 1)))) {
+ return true;
+ }
+ return false;
+}
+
+// The following routines do the word searches performed by the 'w', 'W',
+// 'b', 'B', 'e', and 'E' commands.
+
+// To perform these searches, characters are placed into one of three
+// classes, and transitions between classes determine word boundaries.
+//
+// The classes are:
+//
+// 0 - white space
+// 1 - punctuation
+// 2 or higher - keyword characters (letters, digits and underscore)
+
+static bool cls_bigword; ///< true for "W", "B" or "E"
+
+/// cls() - returns the class of character at curwin->w_cursor
+///
+/// If a 'W', 'B', or 'E' motion is being done (cls_bigword == true), chars
+/// from class 2 and higher are reported as class 1 since only white space
+/// boundaries are of interest.
+static int cls(void)
+{
+ int c;
+
+ c = gchar_cursor();
+ if (c == ' ' || c == '\t' || c == NUL) {
+ return 0;
+ }
+
+ c = utf_class(c);
+
+ // If cls_bigword is true, report all non-blanks as class 1.
+ if (c != 0 && cls_bigword) {
+ return 1;
+ }
+ return c;
+}
+
+/// fwd_word(count, type, eol) - move forward one word
+///
+/// @return FAIL if the cursor was already at the end of the file.
+/// If eol is true, last word stops at end of line (for operators).
+///
+/// @param bigword "W", "E" or "B"
+int fwd_word(long count, bool bigword, bool eol)
+{
+ int sclass; // starting class
+ int i;
+ int last_line;
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ // When inside a range of folded lines, move to the last char of the
+ // last line.
+ if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
+ coladvance(MAXCOL);
+ }
+ sclass = cls();
+
+ // We always move at least one character, unless on the last
+ // character in the buffer.
+ last_line = (curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count);
+ i = inc_cursor();
+ if (i == -1 || (i >= 1 && last_line)) { // started at last char in file
+ return FAIL;
+ }
+ if (i >= 1 && eol && count == 0) { // started at last char in line
+ return OK;
+ }
+
+ // Go one char past end of current word (if any)
+ if (sclass != 0) {
+ while (cls() == sclass) {
+ i = inc_cursor();
+ if (i == -1 || (i >= 1 && eol && count == 0)) {
+ return OK;
+ }
+ }
+ }
+
+ // go to next non-white
+ while (cls() == 0) {
+ // We'll stop if we land on a blank line
+ if (curwin->w_cursor.col == 0 && *get_cursor_line_ptr() == NUL) {
+ break;
+ }
+
+ i = inc_cursor();
+ if (i == -1 || (i >= 1 && eol && count == 0)) {
+ return OK;
+ }
+ }
+ }
+ return OK;
+}
+
+/// bck_word() - move backward 'count' words
+///
+/// If stop is true and we are already on the start of a word, move one less.
+///
+/// Returns FAIL if top of the file was reached.
+int bck_word(long count, bool bigword, bool stop)
+{
+ int sclass; // starting class
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ // When inside a range of folded lines, move to the first char of the
+ // first line.
+ if (hasFolding(curwin->w_cursor.lnum, &curwin->w_cursor.lnum, NULL)) {
+ curwin->w_cursor.col = 0;
+ }
+ sclass = cls();
+ if (dec_cursor() == -1) { // started at start of file
+ return FAIL;
+ }
+
+ if (!stop || sclass == cls() || sclass == 0) {
+ // Skip white space before the word.
+ // Stop on an empty line.
+ while (cls() == 0) {
+ if (curwin->w_cursor.col == 0
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
+ goto finished;
+ }
+ if (dec_cursor() == -1) { // hit start of file, stop here
+ return OK;
+ }
+ }
+
+ // Move backward to start of this word.
+ if (skip_chars(cls(), BACKWARD)) {
+ return OK;
+ }
+ }
+
+ inc_cursor(); // overshot - forward one
+finished:
+ stop = false;
+ }
+ return OK;
+}
+
+/// end_word() - move to the end of the word
+///
+/// There is an apparent bug in the 'e' motion of the real vi. At least on the
+/// System V Release 3 version for the 80386. Unlike 'b' and 'w', the 'e'
+/// motion crosses blank lines. When the real vi crosses a blank line in an
+/// 'e' motion, the cursor is placed on the FIRST character of the next
+/// non-blank line. The 'E' command, however, works correctly. Since this
+/// appears to be a bug, I have not duplicated it here.
+///
+/// Returns FAIL if end of the file was reached.
+///
+/// If stop is true and we are already on the end of a word, move one less.
+/// If empty is true stop on an empty line.
+int end_word(long count, bool bigword, bool stop, bool empty)
+{
+ int sclass; // starting class
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ // When inside a range of folded lines, move to the last char of the
+ // last line.
+ if (hasFolding(curwin->w_cursor.lnum, NULL, &curwin->w_cursor.lnum)) {
+ coladvance(MAXCOL);
+ }
+ sclass = cls();
+ if (inc_cursor() == -1) {
+ return FAIL;
+ }
+
+ // If we're in the middle of a word, we just have to move to the end
+ // of it.
+ if (cls() == sclass && sclass != 0) {
+ // Move forward to end of the current word
+ if (skip_chars(sclass, FORWARD)) {
+ return FAIL;
+ }
+ } else if (!stop || sclass == 0) {
+ // We were at the end of a word. Go to the end of the next word.
+ // First skip white space, if 'empty' is true, stop at empty line.
+ while (cls() == 0) {
+ if (empty && curwin->w_cursor.col == 0
+ && LINEEMPTY(curwin->w_cursor.lnum)) {
+ goto finished;
+ }
+ if (inc_cursor() == -1) { // hit end of file, stop here
+ return FAIL;
+ }
+ }
+
+ // Move forward to the end of this word.
+ if (skip_chars(cls(), FORWARD)) {
+ return FAIL;
+ }
+ }
+ dec_cursor(); // overshot - one char backward
+finished:
+ stop = false; // we move only one word less
+ }
+ return OK;
+}
+
+/// Move back to the end of the word.
+///
+/// @param bigword true for "B"
+/// @param eol if true, then stop at end of line.
+///
+/// @return FAIL if start of the file was reached.
+int bckend_word(long count, bool bigword, bool eol)
+{
+ int sclass; // starting class
+ int i;
+
+ curwin->w_cursor.coladd = 0;
+ cls_bigword = bigword;
+ while (--count >= 0) {
+ sclass = cls();
+ if ((i = dec_cursor()) == -1) {
+ return FAIL;
+ }
+ if (eol && i == 1) {
+ return OK;
+ }
+
+ // Move backward to before the start of this word.
+ if (sclass != 0) {
+ while (cls() == sclass) {
+ if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
+ return OK;
+ }
+ }
+ }
+
+ // Move backward to end of the previous word
+ while (cls() == 0) {
+ if (curwin->w_cursor.col == 0 && LINEEMPTY(curwin->w_cursor.lnum)) {
+ break;
+ }
+ if ((i = dec_cursor()) == -1 || (eol && i == 1)) {
+ return OK;
+ }
+ }
+ }
+ return OK;
+}
+
+/// Skip a row of characters of the same class.
+///
+/// @return true when end-of-file reached, false otherwise.
+static bool skip_chars(int cclass, int dir)
+{
+ while (cls() == cclass) {
+ if ((dir == FORWARD ? inc_cursor() : dec_cursor()) == -1) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Go back to the start of the word or the start of white space
+static void back_in_line(void)
+{
+ int sclass; // starting class
+
+ sclass = cls();
+ for (;;) {
+ if (curwin->w_cursor.col == 0) { // stop at start of line
+ break;
+ }
+ dec_cursor();
+ if (cls() != sclass) { // stop at start of word
+ inc_cursor();
+ break;
+ }
+ }
+}
+
+static void find_first_blank(pos_T *posp)
+{
+ int c;
+
+ while (decl(posp) != -1) {
+ c = gchar_pos(posp);
+ if (!ascii_iswhite(c)) {
+ incl(posp);
+ break;
+ }
+ }
+}
+
+/// Skip count/2 sentences and count/2 separating white spaces.
+///
+/// @param at_start_sent cursor is at start of sentence
+static void findsent_forward(long count, bool at_start_sent)
+{
+ while (count--) {
+ findsent(FORWARD, 1L);
+ if (at_start_sent) {
+ find_first_blank(&curwin->w_cursor);
+ }
+ if (count == 0 || at_start_sent) {
+ decl(&curwin->w_cursor);
+ }
+ at_start_sent = !at_start_sent;
+ }
+}
+
+/// Find word under cursor, cursor at end.
+/// Used while an operator is pending, and in Visual mode.
+///
+/// @param include true: include word and white space
+/// @param bigword false == word, true == WORD
+int current_word(oparg_T *oap, long count, bool include, bool bigword)
+{
+ pos_T start_pos;
+ pos_T pos;
+ bool inclusive = true;
+ int include_white = false;
+
+ cls_bigword = bigword;
+ clearpos(&start_pos);
+
+ // Correct cursor when 'selection' is exclusive
+ if (VIsual_active && *p_sel == 'e' && lt(VIsual, curwin->w_cursor)) {
+ dec_cursor();
+ }
+
+ // When Visual mode is not active, or when the VIsual area is only one
+ // character, select the word and/or white space under the cursor.
+ if (!VIsual_active || equalpos(curwin->w_cursor, VIsual)) {
+ // Go to start of current word or white space.
+ back_in_line();
+ start_pos = curwin->w_cursor;
+
+ // If the start is on white space, and white space should be included
+ // (" word"), or start is not on white space, and white space should
+ // not be included ("word"), find end of word.
+ if ((cls() == 0) == include) {
+ if (end_word(1L, bigword, true, true) == FAIL) {
+ return FAIL;
+ }
+ } else {
+ // If the start is not on white space, and white space should be
+ // included ("word "), or start is on white space and white
+ // space should not be included (" "), find start of word.
+ // If we end up in the first column of the next line (single char
+ // word) back up to end of the line.
+ fwd_word(1L, bigword, true);
+ if (curwin->w_cursor.col == 0) {
+ decl(&curwin->w_cursor);
+ } else {
+ oneleft();
+ }
+
+ if (include) {
+ include_white = true;
+ }
+ }
+
+ if (VIsual_active) {
+ // should do something when inclusive == false !
+ VIsual = start_pos;
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ } else {
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ }
+ count--;
+ }
+
+ // When count is still > 0, extend with more objects.
+ while (count > 0) {
+ inclusive = true;
+ if (VIsual_active && lt(curwin->w_cursor, VIsual)) {
+ // In Visual mode, with cursor at start: move cursor back.
+ if (decl(&curwin->w_cursor) == -1) {
+ return FAIL;
+ }
+ if (include != (cls() != 0)) {
+ if (bck_word(1L, bigword, true) == FAIL) {
+ return FAIL;
+ }
+ } else {
+ if (bckend_word(1L, bigword, true) == FAIL) {
+ return FAIL;
+ }
+ (void)incl(&curwin->w_cursor);
+ }
+ } else {
+ // Move cursor forward one word and/or white area.
+ if (incl(&curwin->w_cursor) == -1) {
+ return FAIL;
+ }
+ if (include != (cls() == 0)) {
+ if (fwd_word(1L, bigword, true) == FAIL && count > 1) {
+ return FAIL;
+ }
+ // If end is just past a new-line, we don't want to include
+ // the first character on the line.
+ // Put cursor on last char of white.
+ if (oneleft() == FAIL) {
+ inclusive = false;
+ }
+ } else {
+ if (end_word(1L, bigword, true, true) == FAIL) {
+ return FAIL;
+ }
+ }
+ }
+ count--;
+ }
+
+ if (include_white && (cls() != 0
+ || (curwin->w_cursor.col == 0 && !inclusive))) {
+ // If we don't include white space at the end, move the start
+ // to include some white space there. This makes "daw" work
+ // better on the last word in a sentence (and "2daw" on last-but-one
+ // word). Also when "2daw" deletes "word." at the end of the line
+ // (cursor is at start of next line).
+ // But don't delete white space at start of line (indent).
+ pos = curwin->w_cursor; // save cursor position
+ curwin->w_cursor = start_pos;
+ if (oneleft() == OK) {
+ back_in_line();
+ if (cls() == 0 && curwin->w_cursor.col > 0) {
+ if (VIsual_active) {
+ VIsual = curwin->w_cursor;
+ } else {
+ oap->start = curwin->w_cursor;
+ }
+ }
+ }
+ curwin->w_cursor = pos; // put cursor back at end
+ }
+
+ if (VIsual_active) {
+ if (*p_sel == 'e' && inclusive && ltoreq(VIsual, curwin->w_cursor)) {
+ inc_cursor();
+ }
+ if (VIsual_mode == 'V') {
+ VIsual_mode = 'v';
+ redraw_cmdline = true; // show mode later
+ }
+ } else {
+ oap->inclusive = inclusive;
+ }
+
+ return OK;
+}
+
+/// Find sentence(s) under the cursor, cursor at end.
+/// When Visual active, extend it by one or more sentences.
+int current_sent(oparg_T *oap, long count, bool include)
+{
+ pos_T start_pos;
+ pos_T pos;
+ bool start_blank;
+ int c;
+ bool at_start_sent;
+ long ncount;
+
+ start_pos = curwin->w_cursor;
+ pos = start_pos;
+ findsent(FORWARD, 1L); // Find start of next sentence.
+
+ // When the Visual area is bigger than one character: Extend it.
+ if (VIsual_active && !equalpos(start_pos, VIsual)) {
+extend:
+ if (lt(start_pos, VIsual)) {
+ // Cursor at start of Visual area.
+ // Find out where we are:
+ // - in the white space before a sentence
+ // - in a sentence or just after it
+ // - at the start of a sentence
+ at_start_sent = true;
+ decl(&pos);
+ while (lt(pos, curwin->w_cursor)) {
+ c = gchar_pos(&pos);
+ if (!ascii_iswhite(c)) {
+ at_start_sent = false;
+ break;
+ }
+ incl(&pos);
+ }
+ if (!at_start_sent) {
+ findsent(BACKWARD, 1L);
+ if (equalpos(curwin->w_cursor, start_pos)) {
+ at_start_sent = true; // exactly at start of sentence
+ } else {
+ // inside a sentence, go to its end (start of next)
+ findsent(FORWARD, 1L);
+ }
+ }
+ if (include) { // "as" gets twice as much as "is"
+ count *= 2;
+ }
+ while (count--) {
+ if (at_start_sent) {
+ find_first_blank(&curwin->w_cursor);
+ }
+ c = gchar_cursor();
+ if (!at_start_sent || (!include && !ascii_iswhite(c))) {
+ findsent(BACKWARD, 1L);
+ }
+ at_start_sent = !at_start_sent;
+ }
+ } else {
+ // Cursor at end of Visual area.
+ // Find out where we are:
+ // - just before a sentence
+ // - just before or in the white space before a sentence
+ // - in a sentence
+ incl(&pos);
+ at_start_sent = true;
+ if (!equalpos(pos, curwin->w_cursor)) { // not just before a sentence
+ at_start_sent = false;
+ while (lt(pos, curwin->w_cursor)) {
+ c = gchar_pos(&pos);
+ if (!ascii_iswhite(c)) {
+ at_start_sent = true;
+ break;
+ }
+ incl(&pos);
+ }
+ if (at_start_sent) { // in the sentence
+ findsent(BACKWARD, 1L);
+ } else { // in/before white before a sentence
+ curwin->w_cursor = start_pos;
+ }
+ }
+
+ if (include) { // "as" gets twice as much as "is"
+ count *= 2;
+ }
+ findsent_forward(count, at_start_sent);
+ if (*p_sel == 'e') {
+ curwin->w_cursor.col++;
+ }
+ }
+ return OK;
+ }
+
+ // If the cursor started on a blank, check if it is just before the start
+ // of the next sentence.
+ while (c = gchar_pos(&pos), ascii_iswhite(c)) {
+ incl(&pos);
+ }
+ if (equalpos(pos, curwin->w_cursor)) {
+ start_blank = true;
+ find_first_blank(&start_pos); // go back to first blank
+ } else {
+ start_blank = false;
+ findsent(BACKWARD, 1L);
+ start_pos = curwin->w_cursor;
+ }
+ if (include) {
+ ncount = count * 2;
+ } else {
+ ncount = count;
+ if (start_blank) {
+ ncount--;
+ }
+ }
+ if (ncount > 0) {
+ findsent_forward(ncount, true);
+ } else {
+ decl(&curwin->w_cursor);
+ }
+
+ if (include) {
+ // If the blank in front of the sentence is included, exclude the
+ // blanks at the end of the sentence, go back to the first blank.
+ // If there are no trailing blanks, try to include leading blanks.
+ if (start_blank) {
+ find_first_blank(&curwin->w_cursor);
+ c = gchar_pos(&curwin->w_cursor);
+ if (ascii_iswhite(c)) {
+ decl(&curwin->w_cursor);
+ }
+ } else if (c = gchar_cursor(), !ascii_iswhite(c)) {
+ find_first_blank(&start_pos);
+ }
+ }
+
+ if (VIsual_active) {
+ // Avoid getting stuck with "is" on a single space before a sentence.
+ if (equalpos(start_pos, curwin->w_cursor)) {
+ goto extend;
+ }
+ if (*p_sel == 'e') {
+ curwin->w_cursor.col++;
+ }
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ redraw_cmdline = true; // show mode later
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ } else {
+ // include a newline after the sentence, if there is one
+ if (incl(&curwin->w_cursor) == -1) {
+ oap->inclusive = true;
+ } else {
+ oap->inclusive = false;
+ }
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ }
+ return OK;
+}
+
+/// Find block under the cursor, cursor at end.
+/// "what" and "other" are two matching parenthesis/brace/etc.
+///
+/// @param include true == include white space
+/// @param what '(', '{', etc.
+/// @param other ')', '}', etc.
+int current_block(oparg_T *oap, long count, bool include, int what, int other)
+{
+ pos_T old_pos;
+ pos_T *pos = NULL;
+ pos_T start_pos;
+ pos_T *end_pos;
+ pos_T old_start, old_end;
+ char *save_cpo;
+ bool sol = false; // '{' at start of line
+
+ old_pos = curwin->w_cursor;
+ old_end = curwin->w_cursor; // remember where we started
+ old_start = old_end;
+
+ // If we start on '(', '{', ')', '}', etc., use the whole block inclusive.
+ if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
+ setpcmark();
+ if (what == '{') { // ignore indent
+ while (inindent(1)) {
+ if (inc_cursor() != 0) {
+ break;
+ }
+ }
+ }
+ if (gchar_cursor() == what) {
+ // cursor on '(' or '{', move cursor just after it
+ curwin->w_cursor.col++;
+ }
+ } else if (lt(VIsual, curwin->w_cursor)) {
+ old_start = VIsual;
+ curwin->w_cursor = VIsual; // cursor at low end of Visual
+ } else {
+ old_end = VIsual;
+ }
+
+ // Search backwards for unclosed '(', '{', etc..
+ // Put this position in start_pos.
+ // Ignore quotes here. Keep the "M" flag in 'cpo', as that is what the
+ // user wants.
+ save_cpo = p_cpo;
+ p_cpo = vim_strchr(p_cpo, CPO_MATCHBSL) != NULL ? "%M" : "%";
+ if ((pos = findmatch(NULL, what)) != NULL) {
+ while (count-- > 0) {
+ if ((pos = findmatch(NULL, what)) == NULL) {
+ break;
+ }
+ curwin->w_cursor = *pos;
+ start_pos = *pos; // the findmatch for end_pos will overwrite *pos
+ }
+ } else {
+ while (count-- > 0) {
+ if ((pos = findmatchlimit(NULL, what, FM_FORWARD, 0)) == NULL) {
+ break;
+ }
+ curwin->w_cursor = *pos;
+ start_pos = *pos; // the findmatch for end_pos will overwrite *pos
+ }
+ }
+ p_cpo = save_cpo;
+
+ // Search for matching ')', '}', etc.
+ // Put this position in curwin->w_cursor.
+ if (pos == NULL || (end_pos = findmatch(NULL, other)) == NULL) {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ curwin->w_cursor = *end_pos;
+
+ // Try to exclude the '(', '{', ')', '}', etc. when "include" is false.
+ // If the ending '}', ')' or ']' is only preceded by indent, skip that
+ // indent. But only if the resulting area is not smaller than what we
+ // started with.
+ while (!include) {
+ incl(&start_pos);
+ sol = (curwin->w_cursor.col == 0);
+ decl(&curwin->w_cursor);
+ while (inindent(1)) {
+ sol = true;
+ if (decl(&curwin->w_cursor) != 0) {
+ break;
+ }
+ }
+
+ // In Visual mode, when the resulting area is not bigger than what we
+ // started with, extend it to the next block, and then exclude again.
+ // Don't try to expand the area if the area is empty.
+ if (!lt(start_pos, old_start) && !lt(old_end, curwin->w_cursor)
+ && !equalpos(start_pos, curwin->w_cursor)
+ && VIsual_active) {
+ curwin->w_cursor = old_start;
+ decl(&curwin->w_cursor);
+ if ((pos = findmatch(NULL, what)) == NULL) {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ start_pos = *pos;
+ curwin->w_cursor = *pos;
+ if ((end_pos = findmatch(NULL, other)) == NULL) {
+ curwin->w_cursor = old_pos;
+ return FAIL;
+ }
+ curwin->w_cursor = *end_pos;
+ } else {
+ break;
+ }
+ }
+
+ if (VIsual_active) {
+ if (*p_sel == 'e') {
+ inc(&curwin->w_cursor);
+ }
+ if (sol && gchar_cursor() != NUL) {
+ inc(&curwin->w_cursor); // include the line break
+ }
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ showmode();
+ } else {
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ oap->inclusive = false;
+ if (sol) {
+ incl(&curwin->w_cursor);
+ } else if (ltoreq(start_pos, curwin->w_cursor)) {
+ // Include the character under the cursor.
+ oap->inclusive = true;
+ } else {
+ // End is before the start (no text in between <>, [], etc.): don't
+ // operate on any text.
+ curwin->w_cursor = start_pos;
+ }
+ }
+
+ return OK;
+}
+
+/// @param end_tag when true, return true if the cursor is on "</aaa>".
+///
+/// @return true if the cursor is on a "<aaa>" tag. Ignore "<aaa/>".
+static bool in_html_tag(bool end_tag)
+{
+ char_u *line = get_cursor_line_ptr();
+ char_u *p;
+ int c;
+ int lc = NUL;
+ pos_T pos;
+
+ for (p = line + curwin->w_cursor.col; p > line;) {
+ if (*p == '<') { // find '<' under/before cursor
+ break;
+ }
+ MB_PTR_BACK(line, p);
+ if (*p == '>') { // find '>' before cursor
+ break;
+ }
+ }
+ if (*p != '<') {
+ return false;
+ }
+
+ pos.lnum = curwin->w_cursor.lnum;
+ pos.col = (colnr_T)(p - line);
+
+ MB_PTR_ADV(p);
+ if (end_tag) {
+ // check that there is a '/' after the '<'
+ return *p == '/';
+ }
+
+ // check that there is no '/' after the '<'
+ if (*p == '/') {
+ return false;
+ }
+
+ // check that the matching '>' is not preceded by '/'
+ for (;;) {
+ if (inc(&pos) < 0) {
+ return false;
+ }
+ c = *ml_get_pos(&pos);
+ if (c == '>') {
+ break;
+ }
+ lc = c;
+ }
+ return lc != '/';
+}
+
+/// Find tag block under the cursor, cursor at end.
+///
+/// @param include true == include white space
+int current_tagblock(oparg_T *oap, long count_arg, bool include)
+{
+ long count = count_arg;
+ pos_T old_pos;
+ pos_T start_pos;
+ pos_T end_pos;
+ pos_T old_start, old_end;
+ char_u *p;
+ char_u *cp;
+ int len;
+ bool do_include = include;
+ bool save_p_ws = p_ws;
+ int retval = FAIL;
+ int is_inclusive = true;
+
+ p_ws = false;
+
+ old_pos = curwin->w_cursor;
+ old_end = curwin->w_cursor; // remember where we started
+ old_start = old_end;
+ if (!VIsual_active || *p_sel == 'e') {
+ decl(&old_end); // old_end is inclusive
+ }
+
+ // If we start on "<aaa>" select that block.
+ if (!VIsual_active || equalpos(VIsual, curwin->w_cursor)) {
+ setpcmark();
+
+ // ignore indent
+ while (inindent(1)) {
+ if (inc_cursor() != 0) {
+ break;
+ }
+ }
+
+ if (in_html_tag(false)) {
+ // cursor on start tag, move to its '>'
+ while (*get_cursor_pos_ptr() != '>') {
+ if (inc_cursor() < 0) {
+ break;
+ }
+ }
+ } else if (in_html_tag(true)) {
+ // cursor on end tag, move to just before it
+ while (*get_cursor_pos_ptr() != '<') {
+ if (dec_cursor() < 0) {
+ break;
+ }
+ }
+ dec_cursor();
+ old_end = curwin->w_cursor;
+ }
+ } else if (lt(VIsual, curwin->w_cursor)) {
+ old_start = VIsual;
+ curwin->w_cursor = VIsual; // cursor at low end of Visual
+ } else {
+ old_end = VIsual;
+ }
+
+again:
+ // Search backwards for unclosed "<aaa>".
+ // Put this position in start_pos.
+ for (long n = 0; n < count; n++) {
+ if (do_searchpair("<[^ \t>/!]\\+\\%(\\_s\\_[^>]\\{-}[^/]>\\|$\\|\\_s\\=>\\)",
+ "",
+ "</[^>]*>", BACKWARD, NULL, 0,
+ NULL, (linenr_T)0, 0L) <= 0) {
+ curwin->w_cursor = old_pos;
+ goto theend;
+ }
+ }
+ start_pos = curwin->w_cursor;
+
+ // Search for matching "</aaa>". First isolate the "aaa".
+ inc_cursor();
+ p = get_cursor_pos_ptr();
+ for (cp = p;
+ *cp != NUL && *cp != '>' && !ascii_iswhite(*cp);
+ MB_PTR_ADV(cp)) {}
+ len = (int)(cp - p);
+ if (len == 0) {
+ curwin->w_cursor = old_pos;
+ goto theend;
+ }
+ const size_t spat_len = (size_t)len + 39;
+ char *const spat = xmalloc(spat_len);
+ const size_t epat_len = (size_t)len + 9;
+ char *const epat = xmalloc(epat_len);
+ snprintf(spat, spat_len,
+ "<%.*s\\>\\%%(\\_s\\_[^>]\\{-}\\_[^/]>\\|\\_s\\?>\\)\\c", len, p);
+ snprintf(epat, epat_len, "</%.*s>\\c", len, p);
+
+ const int r = (int)do_searchpair(spat, "", epat, FORWARD, NULL, 0, NULL, (linenr_T)0, 0L);
+
+ xfree(spat);
+ xfree(epat);
+
+ if (r < 1 || lt(curwin->w_cursor, old_end)) {
+ // Can't find other end or it's before the previous end. Could be a
+ // HTML tag that doesn't have a matching end. Search backwards for
+ // another starting tag.
+ count = 1;
+ curwin->w_cursor = start_pos;
+ goto again;
+ }
+
+ if (do_include) {
+ // Include up to the '>'.
+ while (*get_cursor_pos_ptr() != '>') {
+ if (inc_cursor() < 0) {
+ break;
+ }
+ }
+ } else {
+ char_u *c = get_cursor_pos_ptr();
+ // Exclude the '<' of the end tag.
+ // If the closing tag is on new line, do not decrement cursor, but make
+ // operation exclusive, so that the linefeed will be selected
+ if (*c == '<' && !VIsual_active && curwin->w_cursor.col == 0) {
+ // do not decrement cursor
+ is_inclusive = false;
+ } else if (*c == '<') {
+ dec_cursor();
+ }
+ }
+ end_pos = curwin->w_cursor;
+
+ if (!do_include) {
+ // Exclude the start tag.
+ curwin->w_cursor = start_pos;
+ while (inc_cursor() >= 0) {
+ if (*get_cursor_pos_ptr() == '>') {
+ inc_cursor();
+ start_pos = curwin->w_cursor;
+ break;
+ }
+ }
+ curwin->w_cursor = end_pos;
+
+ // If we are in Visual mode and now have the same text as before set
+ // "do_include" and try again.
+ if (VIsual_active
+ && equalpos(start_pos, old_start)
+ && equalpos(end_pos, old_end)) {
+ do_include = true;
+ curwin->w_cursor = old_start;
+ count = count_arg;
+ goto again;
+ }
+ }
+
+ if (VIsual_active) {
+ // If the end is before the start there is no text between tags, select
+ // the char under the cursor.
+ if (lt(end_pos, start_pos)) {
+ curwin->w_cursor = start_pos;
+ } else if (*p_sel == 'e') {
+ inc_cursor();
+ }
+ VIsual = start_pos;
+ VIsual_mode = 'v';
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ showmode();
+ } else {
+ oap->start = start_pos;
+ oap->motion_type = kMTCharWise;
+ if (lt(end_pos, start_pos)) {
+ // End is before the start: there is no text between tags; operate
+ // on an empty area.
+ curwin->w_cursor = start_pos;
+ oap->inclusive = false;
+ } else {
+ oap->inclusive = is_inclusive;
+ }
+ }
+ retval = OK;
+
+theend:
+ p_ws = save_p_ws;
+ return retval;
+}
+
+/// @param include true == include white space
+/// @param type 'p' for paragraph, 'S' for section
+int current_par(oparg_T *oap, long count, bool include, int type)
+{
+ linenr_T start_lnum;
+ linenr_T end_lnum;
+ int white_in_front;
+ int dir;
+ int start_is_white;
+ int prev_start_is_white;
+ int retval = OK;
+ int do_white = false;
+ int t;
+ int i;
+
+ if (type == 'S') { // not implemented yet
+ return FAIL;
+ }
+
+ start_lnum = curwin->w_cursor.lnum;
+
+ // When visual area is more than one line: extend it.
+ if (VIsual_active && start_lnum != VIsual.lnum) {
+extend:
+ if (start_lnum < VIsual.lnum) {
+ dir = BACKWARD;
+ } else {
+ dir = FORWARD;
+ }
+ for (i = (int)count; --i >= 0;) {
+ if (start_lnum ==
+ (dir == BACKWARD ? 1 : curbuf->b_ml.ml_line_count)) {
+ retval = FAIL;
+ break;
+ }
+
+ prev_start_is_white = -1;
+ for (t = 0; t < 2; t++) {
+ start_lnum += dir;
+ start_is_white = linewhite(start_lnum);
+ if (prev_start_is_white == start_is_white) {
+ start_lnum -= dir;
+ break;
+ }
+ for (;;) {
+ if (start_lnum == (dir == BACKWARD
+ ? 1 : curbuf->b_ml.ml_line_count)) {
+ break;
+ }
+ if (start_is_white != linewhite(start_lnum + dir)
+ || (!start_is_white
+ && startPS(start_lnum + (dir > 0
+ ? 1 : 0), 0, 0))) {
+ break;
+ }
+ start_lnum += dir;
+ }
+ if (!include) {
+ break;
+ }
+ if (start_lnum == (dir == BACKWARD
+ ? 1 : curbuf->b_ml.ml_line_count)) {
+ break;
+ }
+ prev_start_is_white = start_is_white;
+ }
+ }
+ curwin->w_cursor.lnum = start_lnum;
+ curwin->w_cursor.col = 0;
+ return retval;
+ }
+
+ // First move back to the start_lnum of the paragraph or white lines
+ white_in_front = linewhite(start_lnum);
+ while (start_lnum > 1) {
+ if (white_in_front) { // stop at first white line
+ if (!linewhite(start_lnum - 1)) {
+ break;
+ }
+ } else { // stop at first non-white line of start of paragraph
+ if (linewhite(start_lnum - 1) || startPS(start_lnum, 0, 0)) {
+ break;
+ }
+ }
+ start_lnum--;
+ }
+
+ // Move past the end of any white lines.
+ end_lnum = start_lnum;
+ while (end_lnum <= curbuf->b_ml.ml_line_count && linewhite(end_lnum)) {
+ end_lnum++;
+ }
+
+ end_lnum--;
+ i = (int)count;
+ if (!include && white_in_front) {
+ i--;
+ }
+ while (i--) {
+ if (end_lnum == curbuf->b_ml.ml_line_count) {
+ return FAIL;
+ }
+
+ if (!include) {
+ do_white = linewhite(end_lnum + 1);
+ }
+
+ if (include || !do_white) {
+ end_lnum++;
+ // skip to end of paragraph
+ while (end_lnum < curbuf->b_ml.ml_line_count
+ && !linewhite(end_lnum + 1)
+ && !startPS(end_lnum + 1, 0, 0)) {
+ end_lnum++;
+ }
+ }
+
+ if (i == 0 && white_in_front && include) {
+ break;
+ }
+
+ // skip to end of white lines after paragraph
+ if (include || do_white) {
+ while (end_lnum < curbuf->b_ml.ml_line_count
+ && linewhite(end_lnum + 1)) {
+ end_lnum++;
+ }
+ }
+ }
+
+ // If there are no empty lines at the end, try to find some empty lines at
+ // the start (unless that has been done already).
+ if (!white_in_front && !linewhite(end_lnum) && include) {
+ while (start_lnum > 1 && linewhite(start_lnum - 1)) {
+ start_lnum--;
+ }
+ }
+
+ if (VIsual_active) {
+ // Problem: when doing "Vipipip" nothing happens in a single white
+ // line, we get stuck there. Trap this here.
+ if (VIsual_mode == 'V' && start_lnum == curwin->w_cursor.lnum) {
+ goto extend;
+ }
+ if (VIsual.lnum != start_lnum) {
+ VIsual.lnum = start_lnum;
+ VIsual.col = 0;
+ }
+ VIsual_mode = 'V';
+ redraw_curbuf_later(UPD_INVERTED); // update the inversion
+ showmode();
+ } else {
+ oap->start.lnum = start_lnum;
+ oap->start.col = 0;
+ oap->motion_type = kMTLineWise;
+ }
+ curwin->w_cursor.lnum = end_lnum;
+ curwin->w_cursor.col = 0;
+
+ return OK;
+}
+
+/// Search quote char from string line[col].
+/// Quote character escaped by one of the characters in "escape" is not counted
+/// as a quote.
+///
+/// @param escape escape characters, can be NULL
+///
+/// @return column number of "quotechar" or -1 when not found.
+static int find_next_quote(char_u *line, int col, int quotechar, char_u *escape)
+{
+ int c;
+
+ for (;;) {
+ c = line[col];
+ if (c == NUL) {
+ return -1;
+ } else if (escape != NULL && vim_strchr((char *)escape, c)) {
+ col++;
+ if (line[col] == NUL) {
+ return -1;
+ }
+ } else if (c == quotechar) {
+ break;
+ }
+ col += utfc_ptr2len((char *)line + col);
+ }
+ return col;
+}
+
+/// Search backwards in "line" from column "col_start" to find "quotechar".
+/// Quote character escaped by one of the characters in "escape" is not counted
+/// as a quote.
+///
+/// @param escape escape characters, can be NULL
+///
+/// @return the found column or zero.
+static int find_prev_quote(char_u *line, int col_start, int quotechar, char_u *escape)
+{
+ int n;
+
+ while (col_start > 0) {
+ col_start--;
+ col_start -= utf_head_off(line, line + col_start);
+ n = 0;
+ if (escape != NULL) {
+ while (col_start - n > 0 && vim_strchr((char *)escape,
+ line[col_start - n - 1]) != NULL) {
+ n++;
+ }
+ }
+ if (n & 1) {
+ col_start -= n; // uneven number of escape chars, skip it
+ } else if (line[col_start] == quotechar) {
+ break;
+ }
+ }
+ return col_start;
+}
+
+/// Find quote under the cursor, cursor at end.
+///
+/// @param include true == include quote char
+/// @param quotechar Quote character
+///
+/// @return true if found, else false.
+bool current_quote(oparg_T *oap, long count, bool include, int quotechar)
+ FUNC_ATTR_NONNULL_ALL
+{
+ char_u *line = get_cursor_line_ptr();
+ int col_end;
+ int col_start = curwin->w_cursor.col;
+ bool inclusive = false;
+ bool vis_empty = true; // Visual selection <= 1 char
+ bool vis_bef_curs = false; // Visual starts before cursor
+ bool did_exclusive_adj = false; // adjusted pos for 'selection'
+ bool inside_quotes = false; // Looks like "i'" done before
+ bool selected_quote = false; // Has quote inside selection
+ int i;
+ bool restore_vis_bef = false; // resotre VIsual on abort
+
+ // When 'selection' is "exclusive" move the cursor to where it would be
+ // with 'selection' "inclusive", so that the logic is the same for both.
+ // The cursor then is moved forward after adjusting the area.
+ if (VIsual_active) {
+ // this only works within one line
+ if (VIsual.lnum != curwin->w_cursor.lnum) {
+ return false;
+ }
+
+ vis_bef_curs = lt(VIsual, curwin->w_cursor);
+ vis_empty = equalpos(VIsual, curwin->w_cursor);
+ if (*p_sel == 'e') {
+ if (vis_bef_curs) {
+ dec_cursor();
+ did_exclusive_adj = true;
+ } else if (!vis_empty) {
+ dec(&VIsual);
+ did_exclusive_adj = true;
+ }
+ vis_empty = equalpos(VIsual, curwin->w_cursor);
+ if (!vis_bef_curs && !vis_empty) {
+ // VIsual needs to be start of Visual selection.
+ pos_T t = curwin->w_cursor;
+
+ curwin->w_cursor = VIsual;
+ VIsual = t;
+ vis_bef_curs = true;
+ restore_vis_bef = true;
+ }
+ }
+ }
+
+ if (!vis_empty) {
+ // Check if the existing selection exactly spans the text inside
+ // quotes.
+ if (vis_bef_curs) {
+ inside_quotes = VIsual.col > 0
+ && line[VIsual.col - 1] == quotechar
+ && line[curwin->w_cursor.col] != NUL
+ && line[curwin->w_cursor.col + 1] == quotechar;
+ i = VIsual.col;
+ col_end = curwin->w_cursor.col;
+ } else {
+ inside_quotes = curwin->w_cursor.col > 0
+ && line[curwin->w_cursor.col - 1] == quotechar
+ && line[VIsual.col] != NUL
+ && line[VIsual.col + 1] == quotechar;
+ i = curwin->w_cursor.col;
+ col_end = VIsual.col;
+ }
+
+ // Find out if we have a quote in the selection.
+ while (i <= col_end) {
+ // check for going over the end of the line, which can happen if
+ // the line was changed after the Visual area was selected.
+ if (line[i] == NUL) {
+ break;
+ }
+ if (line[i++] == quotechar) {
+ selected_quote = true;
+ break;
+ }
+ }
+ }
+
+ if (!vis_empty && line[col_start] == quotechar) {
+ // Already selecting something and on a quote character. Find the
+ // next quoted string.
+ if (vis_bef_curs) {
+ // Assume we are on a closing quote: move to after the next
+ // opening quote.
+ col_start = find_next_quote(line, col_start + 1, quotechar, NULL);
+ if (col_start < 0) {
+ goto abort_search;
+ }
+ col_end = find_next_quote(line, col_start + 1, quotechar, (char_u *)curbuf->b_p_qe);
+ if (col_end < 0) {
+ // We were on a starting quote perhaps?
+ col_end = col_start;
+ col_start = curwin->w_cursor.col;
+ }
+ } else {
+ col_end = find_prev_quote(line, col_start, quotechar, NULL);
+ if (line[col_end] != quotechar) {
+ goto abort_search;
+ }
+ col_start = find_prev_quote(line, col_end, quotechar, (char_u *)curbuf->b_p_qe);
+ if (line[col_start] != quotechar) {
+ // We were on an ending quote perhaps?
+ col_start = col_end;
+ col_end = curwin->w_cursor.col;
+ }
+ }
+ } else if (line[col_start] == quotechar || !vis_empty) {
+ int first_col = col_start;
+
+ if (!vis_empty) {
+ if (vis_bef_curs) {
+ first_col = find_next_quote(line, col_start, quotechar, NULL);
+ } else {
+ first_col = find_prev_quote(line, col_start, quotechar, NULL);
+ }
+ }
+ // The cursor is on a quote, we don't know if it's the opening or
+ // closing quote. Search from the start of the line to find out.
+ // Also do this when there is a Visual area, a' may leave the cursor
+ // in between two strings.
+ col_start = 0;
+ for (;;) {
+ // Find open quote character.
+ col_start = find_next_quote(line, col_start, quotechar, NULL);
+ if (col_start < 0 || col_start > first_col) {
+ goto abort_search;
+ }
+ // Find close quote character.
+ col_end = find_next_quote(line, col_start + 1, quotechar, (char_u *)curbuf->b_p_qe);
+ if (col_end < 0) {
+ goto abort_search;
+ }
+ // If is cursor between start and end quote character, it is
+ // target text object.
+ if (col_start <= first_col && first_col <= col_end) {
+ break;
+ }
+ col_start = col_end + 1;
+ }
+ } else {
+ // Search backward for a starting quote.
+ col_start = find_prev_quote(line, col_start, quotechar, (char_u *)curbuf->b_p_qe);
+ if (line[col_start] != quotechar) {
+ // No quote before the cursor, look after the cursor.
+ col_start = find_next_quote(line, col_start, quotechar, NULL);
+ if (col_start < 0) {
+ goto abort_search;
+ }
+ }
+
+ // Find close quote character.
+ col_end = find_next_quote(line, col_start + 1, quotechar, (char_u *)curbuf->b_p_qe);
+ if (col_end < 0) {
+ goto abort_search;
+ }
+ }
+
+ // When "include" is true, include spaces after closing quote or before
+ // the starting quote.
+ if (include) {
+ if (ascii_iswhite(line[col_end + 1])) {
+ while (ascii_iswhite(line[col_end + 1])) {
+ col_end++;
+ }
+ } else {
+ while (col_start > 0 && ascii_iswhite(line[col_start - 1])) {
+ col_start--;
+ }
+ }
+ }
+
+ // Set start position. After vi" another i" must include the ".
+ // For v2i" include the quotes.
+ if (!include && count < 2 && (vis_empty || !inside_quotes)) {
+ col_start++;
+ }
+ curwin->w_cursor.col = col_start;
+ if (VIsual_active) {
+ // Set the start of the Visual area when the Visual area was empty, we
+ // were just inside quotes or the Visual area didn't start at a quote
+ // and didn't include a quote.
+ if (vis_empty
+ || (vis_bef_curs
+ && !selected_quote
+ && (inside_quotes
+ || (line[VIsual.col] != quotechar
+ && (VIsual.col == 0
+ || line[VIsual.col - 1] != quotechar))))) {
+ VIsual = curwin->w_cursor;
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+ } else {
+ oap->start = curwin->w_cursor;
+ oap->motion_type = kMTCharWise;
+ }
+
+ // Set end position.
+ curwin->w_cursor.col = col_end;
+ if ((include || count > 1
+ // After vi" another i" must include the ".
+ || (!vis_empty && inside_quotes)) && inc_cursor() == 2) {
+ inclusive = true;
+ }
+ if (VIsual_active) {
+ if (vis_empty || vis_bef_curs) {
+ // decrement cursor when 'selection' is not exclusive
+ if (*p_sel != 'e') {
+ dec_cursor();
+ }
+ } else {
+ // Cursor is at start of Visual area. Set the end of the Visual
+ // area when it was just inside quotes or it didn't end at a
+ // quote.
+ if (inside_quotes
+ || (!selected_quote
+ && line[VIsual.col] != quotechar
+ && (line[VIsual.col] == NUL
+ || line[VIsual.col + 1] != quotechar))) {
+ dec_cursor();
+ VIsual = curwin->w_cursor;
+ }
+ curwin->w_cursor.col = col_start;
+ }
+ if (VIsual_mode == 'V') {
+ VIsual_mode = 'v';
+ redraw_cmdline = true; // show mode later
+ }
+ } else {
+ // Set inclusive and other oap's flags.
+ oap->inclusive = inclusive;
+ }
+
+ return true;
+
+abort_search:
+ if (VIsual_active && *p_sel == 'e') {
+ if (did_exclusive_adj) {
+ inc_cursor();
+ }
+ if (restore_vis_bef) {
+ pos_T t = curwin->w_cursor;
+
+ curwin->w_cursor = VIsual;
+ VIsual = t;
+ }
+ }
+ return false;
+}
diff --git a/src/nvim/textobject.h b/src/nvim/textobject.h
new file mode 100644
index 0000000000..26f88613fd
--- /dev/null
+++ b/src/nvim/textobject.h
@@ -0,0 +1,11 @@
+#ifndef NVIM_TEXTOBJECT_H
+#define NVIM_TEXTOBJECT_H
+
+#include "nvim/normal.h" // for oparg_T
+#include "nvim/pos.h" // for linenr_T
+#include "nvim/vim.h" // for Direction
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "textobject.h.generated.h"
+#endif
+#endif // NVIM_TEXTOBJECT_H
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 61a59bcf06..d269878f46 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -233,12 +233,12 @@ static void tinput_wait_enqueue(void **argv)
if (ui_client_channel_id) {
Array args = ARRAY_DICT_INIT;
Error err = ERROR_INIT;
- ADD(args, STRING_OBJ(copy_string(keys)));
+ ADD(args, STRING_OBJ(copy_string(keys, NULL)));
// TODO(bfredl): could be non-blocking now with paste?
ArenaMem res_mem = NULL;
Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err);
consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0;
- arena_mem_free(res_mem, NULL);
+ arena_mem_free(res_mem);
} else {
consumed = input_enqueue(keys);
}
@@ -398,6 +398,14 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
button = last_pressed_button;
}
+ if (ev == TERMKEY_MOUSE_UNKNOWN && !(key->code.mouse[0] & 0x20)) {
+ int code = key->code.mouse[0] & ~0x3c;
+ if (code == 66 || code == 67) {
+ ev = TERMKEY_MOUSE_PRESS;
+ button = code - 60;
+ }
+ }
+
if (button == 0 || (ev != TERMKEY_MOUSE_PRESS && ev != TERMKEY_MOUSE_DRAG
&& ev != TERMKEY_MOUSE_RELEASE)) {
return;
@@ -431,8 +439,11 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
if (button == 4) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelUp");
} else if (button == 5) {
- len += (size_t)snprintf(buf + len, sizeof(buf) - len,
- "ScrollWheelDown");
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelDown");
+ } else if (button == 6) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelLeft");
+ } else if (button == 7) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "ScrollWheelRight");
} else {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Mouse");
last_pressed_button = button;
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index e2289eb9ce..38e8c15762 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -171,7 +171,7 @@ UI *tui_start(void)
ui->option_set = tui_option_set;
ui->raw_line = tui_raw_line;
- memset(ui->ui_ext, 0, sizeof(ui->ui_ext));
+ CLEAR_FIELD(ui->ui_ext);
ui->ui_ext[kUILinegrid] = true;
ui->ui_ext[kUITermColors] = true;
@@ -875,6 +875,53 @@ safe_move:
ugrid_goto(grid, row, col);
}
+static void print_spaces(UI *ui, int width)
+{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+
+ out(ui, data->space_buf, (size_t)width);
+ grid->col += width;
+ if (data->immediate_wrap_after_last_column) {
+ // Printing at the right margin immediately advances the cursor.
+ final_column_wrap(ui);
+ }
+}
+
+/// Move cursor to the position given by `row` and `col` and print the character in `cell`.
+/// This allows the grid and the host terminal to assume different widths of ambiguous-width chars.
+///
+/// @param is_doublewidth whether the character is double-width on the grid.
+/// If true and the character is ambiguous-width, clear two cells.
+static void print_cell_at_pos(UI *ui, int row, int col, UCell *cell, bool is_doublewidth)
+{
+ TUIData *data = ui->data;
+ UGrid *grid = &data->grid;
+
+ if (grid->row == -1 && cell->data[0] == NUL) {
+ // If cursor needs to repositioned and there is nothing to print, don't move cursor.
+ return;
+ }
+
+ cursor_goto(ui, row, col);
+
+ bool is_ambiwidth = utf_ambiguous_width(utf_ptr2char(cell->data));
+ if (is_ambiwidth && is_doublewidth) {
+ // Clear the two screen cells.
+ // If the character is single-width in the host terminal it won't change the second cell.
+ update_attrs(ui, cell->attr);
+ print_spaces(ui, 2);
+ cursor_goto(ui, row, col);
+ }
+
+ print_cell(ui, cell);
+
+ if (is_ambiwidth) {
+ // Force repositioning cursor after printing an ambiguous-width character.
+ grid->row = -1;
+ }
+}
+
static void clear_region(UI *ui, int top, int bot, int left, int right, int attr_id)
{
TUIData *data = ui->data;
@@ -888,7 +935,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr
&& left == 0 && right == ui->width && bot == ui->height) {
if (top == 0) {
unibi_out(ui, unibi_clear_screen);
- ugrid_goto(&data->grid, top, left);
+ ugrid_goto(grid, top, left);
} else {
cursor_goto(ui, top, 0);
unibi_out(ui, unibi_clr_eos);
@@ -905,12 +952,7 @@ static void clear_region(UI *ui, int top, int bot, int left, int right, int attr
UNIBI_SET_NUM_VAR(data->params[0], width);
unibi_out(ui, unibi_erase_chars);
} else {
- out(ui, data->space_buf, (size_t)width);
- grid->col += width;
- if (data->immediate_wrap_after_last_column) {
- // Printing at the right margin immediately advances the cursor.
- final_column_wrap(ui);
- }
+ print_spaces(ui, width);
}
}
}
@@ -1302,8 +1344,8 @@ static void tui_flush(UI *ui)
}
UGRID_FOREACH_CELL(grid, row, r.left, clear_col, {
- cursor_goto(ui, row, curcol);
- print_cell(ui, cell);
+ print_cell_at_pos(ui, row, curcol, cell,
+ curcol < clear_col - 1 && (cell + 1)->data[0] == NUL);
});
if (clear_col < r.right) {
clear_region(ui, row, row + 1, clear_col, r.right, clear_attr);
@@ -1439,8 +1481,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
grid->cells[linerow][c].attr = attrs[c - startcol];
}
UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, {
- cursor_goto(ui, (int)linerow, curcol);
- print_cell(ui, cell);
+ print_cell_at_pos(ui, (int)linerow, curcol, cell,
+ curcol < endcol - 1 && (cell + 1)->data[0] == NUL);
});
if (clearcol > endcol) {
@@ -1458,8 +1500,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, I
if (endcol != grid->width) {
// Print the last char of the row, if we haven't already done so.
int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1;
- cursor_goto(ui, (int)linerow, grid->width - size);
- print_cell(ui, &grid->cells[linerow][grid->width - size]);
+ print_cell_at_pos(ui, (int)linerow, grid->width - size,
+ &grid->cells[linerow][grid->width - size], size == 2);
}
// Wrap the cursor over to the next line. The next line will be
diff --git a/src/nvim/types.h b/src/nvim/types.h
index 00b9e6fc09..fb10bf21d9 100644
--- a/src/nvim/types.h
+++ b/src/nvim/types.h
@@ -22,12 +22,23 @@ typedef int handle_T;
// absent callback etc.
typedef int LuaRef;
-typedef void (*FunPtr)(void);
+/// Type used for VimL VAR_FLOAT values
+typedef double float_T;
+
+typedef struct MsgpackRpcRequestHandler MsgpackRpcRequestHandler;
+
+typedef union {
+ float_T (*float_func)(float_T);
+ const MsgpackRpcRequestHandler *api_handler;
+ void *nullptr;
+} EvalFuncData;
typedef handle_T NS;
typedef struct expand expand_T;
+typedef uint64_t proftime_T;
+
typedef enum {
kNone = -1,
kFalse = 0,
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 4fcfee1192..46f4d137c4 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -13,11 +13,12 @@
#include "nvim/cursor.h"
#include "nvim/cursor_shape.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/event/loop.h"
-#include "nvim/ex_cmds2.h"
#include "nvim/ex_getln.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/log.h"
#include "nvim/main.h"
@@ -31,8 +32,7 @@
#include "nvim/os/signal.h"
#include "nvim/os/time.h"
#include "nvim/os_unix.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
#include "nvim/vim.h"
@@ -336,7 +336,7 @@ void vim_beep(unsigned val)
// When 'debug' contains "beep" produce a message. If we are sourcing
// a script or executing a function give the user a hint where the beep
// comes from.
- if (vim_strchr((char *)p_debug, 'e') != NULL) {
+ if (vim_strchr(p_debug, 'e') != NULL) {
msg_source(HL_ATTR(HLF_W));
msg_attr(_("Beep!"), HL_ATTR(HLF_W));
}
@@ -517,11 +517,10 @@ void ui_flush(void)
}
if (pending_mode_info_update) {
Arena arena = ARENA_EMPTY;
- arena_start(&arena, &ui_ext_fixblk);
Array style = mode_style_array(&arena);
bool enabled = (*p_guicursor != NUL);
ui_call_mode_info_set(enabled, style);
- arena_mem_free(arena_finish(&arena), &ui_ext_fixblk);
+ arena_mem_free(arena_finish(&arena));
pending_mode_info_update = false;
}
if (pending_mode_update && !starting) {
@@ -566,7 +565,7 @@ void ui_check_mouse(void)
// - 'a' is in 'mouse' and "c" is in MOUSE_A, or
// - the current buffer is a help file and 'h' is in 'mouse' and we are in a
// normal editing mode (not at hit-return message).
- for (char_u *p = p_mouse; *p; p++) {
+ for (char_u *p = (char_u *)p_mouse; *p; p++) {
switch (*p) {
case 'a':
if (vim_strchr(MOUSE_A, checkfor) != NULL) {
@@ -663,6 +662,6 @@ void ui_grid_resize(handle_T grid_handle, int width, int height, Error *error)
// non-positive indicates no request
wp->w_height_request = MAX(height, 0);
wp->w_width_request = MAX(width, 0);
- win_set_inner_size(wp);
+ win_set_inner_size(wp, true);
}
}
diff --git a/src/nvim/ui.h b/src/nvim/ui.h
index 7dd2f5bce3..996b3467a6 100644
--- a/src/nvim/ui.h
+++ b/src/nvim/ui.h
@@ -47,8 +47,6 @@ enum {
typedef int LineFlags;
-EXTERN ArenaMem ui_ext_fixblk INIT(= NULL);
-
struct ui_t {
bool rgb;
bool override; ///< Force highest-requested UI capabilities.
diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c
index 84098e9476..809d278029 100644
--- a/src/nvim/ui_bridge.c
+++ b/src/nvim/ui_bridge.c
@@ -194,9 +194,9 @@ static void ui_bridge_suspend_event(void **argv)
static void ui_bridge_option_set(UI *ui, String name, Object value)
{
- String copy_name = copy_string(name);
+ String copy_name = copy_string(name, NULL);
Object *copy_value = xmalloc(sizeof(Object));
- *copy_value = copy_object(value);
+ *copy_value = copy_object(value, NULL);
UI_BRIDGE_CALL(ui, option_set, 4, ui, copy_name.data,
INT2PTR(copy_name.size), copy_value);
// TODO(bfredl): when/if TUI/bridge teardown is refactored to use events, the
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index a586fec3bf..265c54f72d 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -55,7 +55,7 @@ UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len,
/// async 'redraw' events, which are expected when nvim acts as an ui client.
/// get handled in msgpack_rpc/unpacker.c and directly dispatched to handlers
/// of specific ui events, like ui_client_event_grid_resize and so on.
-Object handle_ui_client_redraw(uint64_t channel_id, Array args, Error *error)
+Object handle_ui_client_redraw(uint64_t channel_id, Array args, Arena *arena, Error *error)
{
api_set_error(error, kErrorTypeValidation, "'redraw' cannot be sent as a request");
return NIL;
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index 5df70d0d8e..2216e25db9 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -13,6 +13,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
+#include "nvim/grid.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lib/kvec.h"
@@ -22,8 +23,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/os/os.h"
-#include "nvim/popupmnu.h"
-#include "nvim/screen.h"
+#include "nvim/popupmenu.h"
#include "nvim/ugrid.h"
#include "nvim/ui.h"
#include "nvim/ui_compositor.h"
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 45c083b034..97a925f3ad 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -1,73 +1,71 @@
// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-/*
- * undo.c: multi level undo facility
- *
- * The saved lines are stored in a list of lists (one for each buffer):
- *
- * b_u_oldhead------------------------------------------------+
- * |
- * V
- * +--------------+ +--------------+ +--------------+
- * b_u_newhead--->| u_header | | u_header | | u_header |
- * | uh_next------>| uh_next------>| uh_next---->NULL
- * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
- * | uh_entry | | uh_entry | | uh_entry |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ +--------------+ +--------------+
- * | u_entry | | u_entry | | u_entry |
- * | ue_next | | ue_next | | ue_next |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ NULL NULL
- * | u_entry |
- * | ue_next |
- * +--------|-----+
- * |
- * V
- * etc.
- *
- * Each u_entry list contains the information for one undo or redo.
- * curbuf->b_u_curhead points to the header of the last undo (the next redo),
- * or is NULL if nothing has been undone (end of the branch).
- *
- * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
- * each point in the list a branch may appear for an alternate to redo. The
- * uh_seq field is numbered sequentially to be able to find a newer or older
- * branch.
- *
- * +---------------+ +---------------+
- * b_u_oldhead --->| u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next ----> NULL
- * NULL <----- uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next | | uh_alt_next |
- * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * NULL +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next |
- * | uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * etc. etc.
- *
- *
- * All data is allocated and will all be freed when the buffer is unloaded.
- */
+// undo.c: multi level undo facility
+
+// The saved lines are stored in a list of lists (one for each buffer):
+//
+// b_u_oldhead------------------------------------------------+
+// |
+// V
+// +--------------+ +--------------+ +--------------+
+// b_u_newhead--->| u_header | | u_header | | u_header |
+// | uh_next------>| uh_next------>| uh_next---->NULL
+// NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
+// | uh_entry | | uh_entry | | uh_entry |
+// +--------|-----+ +--------|-----+ +--------|-----+
+// | | |
+// V V V
+// +--------------+ +--------------+ +--------------+
+// | u_entry | | u_entry | | u_entry |
+// | ue_next | | ue_next | | ue_next |
+// +--------|-----+ +--------|-----+ +--------|-----+
+// | | |
+// V V V
+// +--------------+ NULL NULL
+// | u_entry |
+// | ue_next |
+// +--------|-----+
+// |
+// V
+// etc.
+//
+// Each u_entry list contains the information for one undo or redo.
+// curbuf->b_u_curhead points to the header of the last undo (the next redo),
+// or is NULL if nothing has been undone (end of the branch).
+//
+// For keeping alternate undo/redo branches the uh_alt field is used. Thus at
+// each point in the list a branch may appear for an alternate to redo. The
+// uh_seq field is numbered sequentially to be able to find a newer or older
+// branch.
+//
+// +---------------+ +---------------+
+// b_u_oldhead --->| u_header | | u_header |
+// | uh_alt_next ---->| uh_alt_next ----> NULL
+// NULL <----- uh_alt_prev |<------ uh_alt_prev |
+// | uh_prev | | uh_prev |
+// +-----|---------+ +-----|---------+
+// | |
+// V V
+// +---------------+ +---------------+
+// | u_header | | u_header |
+// | uh_alt_next | | uh_alt_next |
+// b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
+// | uh_prev | | uh_prev |
+// +-----|---------+ +-----|---------+
+// | |
+// V V
+// NULL +---------------+ +---------------+
+// | u_header | | u_header |
+// | uh_alt_next ---->| uh_alt_next |
+// | uh_alt_prev |<------ uh_alt_prev |
+// | uh_prev | | uh_prev |
+// +-----|---------+ +-----|---------+
+// | |
+// etc. etc.
+//
+//
+// All data is allocated and will all be freed when the buffer is unloaded.
// Uncomment the next line for including the u_check() function. This warns
// for errors in the debug information.
@@ -88,6 +86,7 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/ex_getln.h"
#include "nvim/extmark.h"
@@ -113,6 +112,12 @@
#include "nvim/types.h"
#include "nvim/undo.h"
+/// Structure passed around between undofile functions.
+typedef struct {
+ buf_T *bi_buf;
+ FILE *bi_fp;
+} bufinfo_T;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "undo.c.generated.h"
#endif
@@ -120,31 +125,25 @@
// used in undo_end() to report number of added and deleted lines
static long u_newcount, u_oldcount;
-/*
- * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
- * the action that "u" should do.
- */
+// When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
+// the action that "u" should do.
static bool undo_undoes = false;
static int lastmark = 0;
#if defined(U_DEBUG)
-/*
- * Check the undo structures for being valid. Print a warning when something
- * looks wrong.
- */
+// Check the undo structures for being valid. Print a warning when something
+// looks wrong.
static int seen_b_u_curhead;
static int seen_b_u_newhead;
static int header_count;
static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *exp_uh_alt_prev)
{
- u_entry_T *uep;
-
if (uhp == NULL) {
return;
}
- ++header_count;
+ header_count++;
if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1) {
emsg("b_u_curhead found twice (looping?)");
return;
@@ -170,7 +169,7 @@ static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *e
}
// Check the undo tree at this header.
- for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) {
+ for (u_entry_T *uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) {
if (uep->ue_magic != UE_MAGIC) {
emsg("ue_magic wrong (may be using freed memory)");
break;
@@ -209,11 +208,9 @@ static void u_check(int newhead_may_be_NULL)
#endif
-/*
- * Save the current line for both the "u" and "U" command.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns OK or FAIL.
- */
+/// Save the current line for both the "u" and "U" command.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns OK or FAIL.
int u_save_cursor(void)
{
linenr_T cur = curwin->w_cursor.lnum;
@@ -223,12 +220,10 @@ int u_save_cursor(void)
return u_save(top, bot);
}
-/*
- * Save the lines between "top" and "bot" for both the "u" and "U" command.
- * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Save the lines between "top" and "bot" for both the "u" and "U" command.
+/// "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_save(linenr_T top, linenr_T bot)
{
if (top >= bot || bot > (curbuf->b_ml.ml_line_count + 1)) {
@@ -242,35 +237,29 @@ int u_save(linenr_T top, linenr_T bot)
return u_savecommon(curbuf, top, bot, (linenr_T)0, false);
}
-/*
- * Save the line "lnum" (used by ":s" and "~" command).
- * The line is replaced, so the new bottom line is lnum + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Save the line "lnum" (used by ":s" and "~" command).
+/// The line is replaced, so the new bottom line is lnum + 1.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_savesub(linenr_T lnum)
{
return u_savecommon(curbuf, lnum - 1, lnum + 1, lnum + 1, false);
}
-/*
- * A new line is inserted before line "lnum" (used by :s command).
- * The line is inserted, so the new bottom line is lnum + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// A new line is inserted before line "lnum" (used by :s command).
+/// The line is inserted, so the new bottom line is lnum + 1.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_inssub(linenr_T lnum)
{
return u_savecommon(curbuf, lnum - 1, lnum, lnum + 1, false);
}
-/*
- * Save the lines "lnum" - "lnum" + nlines (used by delete command).
- * The lines are deleted, so the new bottom line is lnum, unless the buffer
- * becomes empty.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Save the lines "lnum" - "lnum" + nlines (used by delete command).
+/// The lines are deleted, so the new bottom line is lnum, unless the buffer
+/// becomes empty.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_savedel(linenr_T lnum, long nlines)
{
return u_savecommon(curbuf, lnum - 1, lnum + (linenr_T)nlines,
@@ -322,25 +311,15 @@ static inline void zero_fmark_additional_data(fmark_T *fmarks)
}
}
-/*
- * Common code for various ways to save text before a change.
- * "top" is the line above the first changed line.
- * "bot" is the line below the last changed line.
- * "newbot" is the new bottom line. Use zero when not known.
- * "reload" is TRUE when saving for a buffer reload.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
+/// Common code for various ways to save text before a change.
+/// "top" is the line above the first changed line.
+/// "bot" is the line below the last changed line.
+/// "newbot" is the new bottom line. Use zero when not known.
+/// "reload" is true when saving for a buffer reload.
+/// Careful: may trigger autocommands that reload the buffer.
+/// Returns FAIL when lines could not be saved, OK otherwise.
int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int reload)
{
- linenr_T lnum;
- long i;
- u_header_T *uhp;
- u_header_T *old_curhead;
- u_entry_T *uep;
- u_entry_T *prev_uep;
- long size;
-
if (!reload) {
// When making changes is not allowed return FAIL. It's a crude way
// to make all change commands fail.
@@ -365,16 +344,19 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
}
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
- size = bot - top - 1;
+ u_entry_T *uep;
+ u_entry_T *prev_uep;
+ long size = bot - top - 1;
// If curbuf->b_u_synced == true make a new header.
if (buf->b_u_synced) {
// Need to create new entry in b_changelist.
buf->b_new_change = true;
+ u_header_T *uhp;
if (get_undolevel(buf) >= 0) {
// Make a new header entry. Do this first so that we don't mess
// up the undo info when out of memory.
@@ -387,19 +369,15 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
uhp = NULL;
}
- /*
- * If we undid more than we redid, move the entry lists before and
- * including curbuf->b_u_curhead to an alternate branch.
- */
- old_curhead = buf->b_u_curhead;
+ // If we undid more than we redid, move the entry lists before and
+ // including curbuf->b_u_curhead to an alternate branch.
+ u_header_T *old_curhead = buf->b_u_curhead;
if (old_curhead != NULL) {
buf->b_u_newhead = old_curhead->uh_next.ptr;
buf->b_u_curhead = NULL;
}
- /*
- * free headers to keep the size right
- */
+ // free headers to keep the size right
while (buf->b_u_numhead > get_undolevel(buf)
&& buf->b_u_oldhead != NULL) {
u_header_T *uhfree = buf->b_u_oldhead;
@@ -418,7 +396,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
u_freebranch(buf, uhfree, &old_curhead);
}
#ifdef U_DEBUG
- u_check(TRUE);
+ u_check(true);
#endif
}
@@ -490,19 +468,17 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
return OK;
}
- /*
- * When saving a single line, and it has been saved just before, it
- * doesn't make sense saving it again. Saves a lot of memory when
- * making lots of changes inside the same line.
- * This is only possible if the previous change didn't increase or
- * decrease the number of lines.
- * Check the ten last changes. More doesn't make sense and takes too
- * long.
- */
+ // When saving a single line, and it has been saved just before, it
+ // doesn't make sense saving it again. Saves a lot of memory when
+ // making lots of changes inside the same line.
+ // This is only possible if the previous change didn't increase or
+ // decrease the number of lines.
+ // Check the ten last changes. More doesn't make sense and takes too
+ // long.
if (size == 1) {
uep = u_get_headentry(buf);
prev_uep = NULL;
- for (i = 0; i < 10; ++i) {
+ for (long i = 0; i < 10; i++) {
if (uep == NULL) {
break;
}
@@ -514,7 +490,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
!= (uep->ue_bot == 0
? buf->b_ml.ml_line_count + 1
: uep->ue_bot))
- : uep->ue_lcount != buf->b_ml.ml_line_count)
+ : uep->ue_lcount != buf->b_ml.ml_line_count)
|| (uep->ue_size > 1
&& top >= uep->ue_top
&& top + 2 <= uep->ue_top + uep->ue_size + 1)) {
@@ -561,11 +537,9 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
u_getbot(buf);
}
- /*
- * add lines in front of entry list
- */
+ // add lines in front of entry list
uep = xmalloc(sizeof(u_entry_T));
- memset(uep, 0, sizeof(u_entry_T));
+ CLEAR_POINTER(uep);
#ifdef U_DEBUG
uep->ue_magic = UE_MAGIC;
#endif
@@ -585,7 +559,9 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
if (size > 0) {
uep->ue_array = xmalloc(sizeof(char_u *) * (size_t)size);
- for (i = 0, lnum = top + 1; i < size; ++i) {
+ linenr_T lnum;
+ long i;
+ for (i = 0, lnum = top + 1; i < size; i++) {
fast_breakcheck();
if (got_int) {
u_freeentry(uep, i);
@@ -607,7 +583,7 @@ int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int re
undo_undoes = false;
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
return OK;
}
@@ -643,12 +619,9 @@ static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
void u_compute_hash(buf_T *buf, char_u *hash)
{
context_sha256_T ctx;
- linenr_T lnum;
- char_u *p;
-
sha256_start(&ctx);
- for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
- p = ml_get_buf(buf, lnum, false);
+ for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) {
+ char_u *p = ml_get_buf(buf, lnum, false);
sha256_update(&ctx, p, (uint32_t)(STRLEN(p) + 1));
}
sha256_finish(&ctx, hash);
@@ -668,21 +641,14 @@ void u_compute_hash(buf_T *buf, char_u *hash)
char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
FUNC_ATTR_WARN_UNUSED_RESULT
{
- char *dirp;
- char dir_name[MAXPATHL + 1];
- char *munged_name = NULL;
- char *undo_file_name = NULL;
const char *ffname = buf_ffname;
-#ifdef HAVE_READLINK
- char fname_buf[MAXPATHL];
-#endif
- char *p;
if (ffname == NULL) {
return NULL;
}
#ifdef HAVE_READLINK
+ char fname_buf[MAXPATHL];
// Expand symlink in the file name, so that we put the undo file with the
// actual file instead of with the symlink.
if (resolve_symlink((const char_u *)ffname, (char_u *)fname_buf) == OK) {
@@ -690,9 +656,13 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
}
#endif
+ char dir_name[MAXPATHL + 1];
+ char *munged_name = NULL;
+ char *undo_file_name = NULL;
+
// Loop over 'undodir'. When reading find the first file that exists.
// When not reading use the first directory that exists or ".".
- dirp = (char *)p_udir;
+ char *dirp = (char *)p_udir;
while (*dirp != NUL) {
size_t dir_len = copy_option_part(&dirp, dir_name, MAXPATHL, ",");
if (dir_len == 1 && dir_name[0] == '.') {
@@ -710,12 +680,12 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
dir_name[dir_len] = NUL;
// Remove trailing pathseps from directory name
- p = &dir_name[dir_len - 1];
+ char *p = &dir_name[dir_len - 1];
while (vim_ispathsep(*p)) {
*p-- = NUL;
}
- bool has_directory = os_isdir((char_u *)dir_name);
+ bool has_directory = os_isdir(dir_name);
if (!has_directory && *dirp == NUL && !reading) {
// Last directory in the list does not exist, create it.
int ret;
@@ -731,9 +701,9 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading)
if (has_directory) {
if (munged_name == NULL) {
munged_name = xstrdup(ffname);
- for (p = munged_name; *p != NUL; MB_PTR_ADV(p)) {
- if (vim_ispathsep(*p)) {
- *p = '%';
+ for (char *c = munged_name; *c != NUL; MB_PTR_ADV(c)) {
+ if (vim_ispathsep(*c)) {
+ *c = '%';
}
}
}
@@ -765,12 +735,9 @@ static void corruption_error(const char *const mesg, const char *const file_name
static void u_free_uhp(u_header_T *uhp)
{
- u_entry_T *nuep;
- u_entry_T *uep;
-
- uep = uhp->uh_entry;
+ u_entry_T *uep = uhp->uh_entry;
while (uep != NULL) {
- nuep = uep->ue_next;
+ u_entry_T *nuep = uep->ue_next;
u_freeentry(uep, uep->ue_size);
uep = nuep;
}
@@ -806,7 +773,7 @@ static bool serialize_header(bufinfo_T *bi, char_u *hash)
undo_write_bytes(bi, (uintmax_t)buf->b_ml.ml_line_count, 4);
size_t len = buf->b_u_line_ptr ? STRLEN(buf->b_u_line_ptr) : 0;
undo_write_bytes(bi, len, 4);
- if (len > 0 && !undo_write(bi, buf->b_u_line_ptr, len)) {
+ if (len > 0 && !undo_write(bi, (char_u *)buf->b_u_line_ptr, len)) {
return false;
}
undo_write_bytes(bi, (uintmax_t)buf->b_u_line_lnum, 4);
@@ -895,7 +862,7 @@ static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp)
static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name)
{
u_header_T *uhp = xmalloc(sizeof(u_header_T));
- memset(uhp, 0, sizeof(u_header_T));
+ CLEAR_POINTER(uhp);
#ifdef U_DEBUG
uhp->uh_magic = UH_MAGIC;
#endif
@@ -1016,23 +983,21 @@ static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup)
static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, const char *filename)
{
- UndoObjectType type;
uint8_t *buf = NULL;
- size_t n_elems;
ExtmarkUndoObject *extup = xmalloc(sizeof(ExtmarkUndoObject));
- type = (UndoObjectType)undo_read_4c(bi);
+ UndoObjectType type = (UndoObjectType)undo_read_4c(bi);
extup->type = type;
if (type == kExtmarkSplice) {
- n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t);
+ size_t n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t);
buf = xcalloc(n_elems, sizeof(uint8_t));
if (!undo_read(bi, buf, n_elems)) {
goto error;
}
extup->data.splice = *(ExtmarkSplice *)buf;
} else if (type == kExtmarkMove) {
- n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t);
+ size_t n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t);
buf = xcalloc(n_elems, sizeof(uint8_t));
if (!undo_read(bi, buf, n_elems)) {
goto error;
@@ -1083,7 +1048,7 @@ static bool serialize_uep(bufinfo_T *bi, u_entry_T *uep)
static u_entry_T *unserialize_uep(bufinfo_T *bi, bool *error, const char *file_name)
{
u_entry_T *uep = xmalloc(sizeof(u_entry_T));
- memset(uep, 0, sizeof(u_entry_T));
+ CLEAR_POINTER(uep);
#ifdef U_DEBUG
uep->ue_magic = UE_MAGIC;
#endif
@@ -1173,17 +1138,12 @@ static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info)
void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, char_u *const hash)
FUNC_ATTR_NONNULL_ARG(3, 4)
{
- u_header_T *uhp;
char *file_name;
- int mark;
#ifdef U_DEBUG
int headers_written = 0;
#endif
- int fd;
FILE *fp = NULL;
- int perm;
bool write_ok = false;
- bufinfo_T bi;
if (name == NULL) {
file_name = u_get_undo_file_name(buf->b_ffname, false);
@@ -1199,12 +1159,10 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
file_name = (char *)name;
}
- /*
- * Decide about the permission to use for the undo file. If the buffer
- * has a name use the permission of the original file. Otherwise only
- * allow the user to access the undo file.
- */
- perm = 0600;
+ // Decide about the permission to use for the undo file. If the buffer
+ // has a name use the permission of the original file. Otherwise only
+ // allow the user to access the undo file.
+ int perm = 0600;
if (buf->b_ffname != NULL) {
perm = os_getperm((const char *)buf->b_ffname);
if (perm < 0) {
@@ -1215,6 +1173,8 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
// Strip any sticky and executable bits.
perm = perm & 0666;
+ int fd;
+
// If the undo file already exists, verify that it actually is an undo
// file, and delete it.
if (os_path_exists((char_u *)file_name)) {
@@ -1279,15 +1239,13 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
#ifdef U_DEBUG
// Check there is no problem in undo info before writing.
- u_check(FALSE);
+ u_check(false);
#endif
#ifdef UNIX
- /*
- * Try to set the group of the undo file same as the original file. If
- * this fails, set the protection bits for the group same as the
- * protection bits for others.
- */
+ // Try to set the group of the undo file same as the original file. If
+ // this fails, set the protection bits for the group same as the
+ // protection bits for others.
FileInfo file_info_old;
FileInfo file_info_new;
if (buf->b_ffname != NULL
@@ -1310,26 +1268,24 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf,
// Undo must be synced.
u_sync(true);
- /*
- * Write the header.
- */
- bi.bi_buf = buf;
- bi.bi_fp = fp;
+ // Write the header.
+ bufinfo_T bi = {
+ .bi_buf = buf,
+ .bi_fp = fp,
+ };
if (!serialize_header(&bi, hash)) {
goto write_error;
}
- /*
- * Iteratively serialize UHPs and their UEPs from the top down.
- */
- mark = ++lastmark;
- uhp = buf->b_u_oldhead;
+ // Iteratively serialize UHPs and their UEPs from the top down.
+ int mark = ++lastmark;
+ u_header_T *uhp = buf->b_u_oldhead;
while (uhp != NULL) {
// Serialize current UHP if we haven't seen it
if (uhp->uh_walk != mark) {
uhp->uh_walk = mark;
#ifdef U_DEBUG
- ++headers_written;
+ headers_written++;
#endif
if (!serialize_uhp(&bi, uhp)) {
goto write_error;
@@ -1437,9 +1393,10 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
goto error;
}
- bufinfo_T bi;
- bi.bi_buf = curbuf;
- bi.bi_fp = fp;
+ bufinfo_T bi = {
+ .bi_buf = curbuf,
+ .bi_fp = fp,
+ };
// Read the undo file header.
char_u magic_buf[UF_START_MAGIC_LEN];
@@ -1568,7 +1525,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
// We have put all of the headers into a table. Now we iterate through the
// table and swizzle each sequence number we have stored in uh_*_seq into
// a pointer corresponding to the header with that sequence number.
- short old_idx = -1, new_idx = -1, cur_idx = -1;
+ int16_t old_idx = -1, new_idx = -1, cur_idx = -1;
for (int i = 0; i < num_head; i++) {
u_header_T *uhp = uhp_table[i];
if (uhp == NULL) {
@@ -1614,18 +1571,18 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
}
}
if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq) {
- assert(i <= SHRT_MAX);
- old_idx = (short)i;
+ assert(i <= INT16_MAX);
+ old_idx = (int16_t)i;
SET_FLAG(i);
}
if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq) {
- assert(i <= SHRT_MAX);
- new_idx = (short)i;
+ assert(i <= INT16_MAX);
+ new_idx = (int16_t)i;
SET_FLAG(i);
}
if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq) {
- assert(i <= SHRT_MAX);
- cur_idx = (short)i;
+ assert(i <= INT16_MAX);
+ cur_idx = (int16_t)i;
SET_FLAG(i);
}
}
@@ -1636,7 +1593,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx];
curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx];
curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx];
- curbuf->b_u_line_ptr = line_ptr;
+ curbuf->b_u_line_ptr = (char *)line_ptr;
curbuf->b_u_line_lnum = line_lnum;
curbuf->b_u_line_colnr = line_colnr;
curbuf->b_u_numhead = num_head;
@@ -1656,7 +1613,7 @@ void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_AT
}
}
xfree(uhp_table_used);
- u_check(TRUE);
+ u_check(true);
#endif
if (name != NULL) {
@@ -1781,17 +1738,13 @@ static uint8_t *undo_read_string(bufinfo_T *bi, size_t len)
return ptr;
}
-/*
- * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
- * If 'cpoptions' does not contain 'u': Always undo.
- */
+/// If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
+/// If 'cpoptions' does not contain 'u': Always undo.
void u_undo(int count)
{
- /*
- * If we get an undo command while executing a macro, we behave like the
- * original vi. If this happens twice in one macro the result will not
- * be compatible.
- */
+ // If we get an undo command while executing a macro, we behave like the
+ // original vi. If this happens twice in one macro the result will not
+ // be compatible.
if (curbuf->b_u_synced == false) {
u_sync(true);
count = 1;
@@ -1805,10 +1758,8 @@ void u_undo(int count)
u_doit(count, false, true);
}
-/*
- * If 'cpoptions' contains 'u': Repeat the previous undo or redo.
- * If 'cpoptions' does not contain 'u': Always redo.
- */
+/// If 'cpoptions' contains 'u': Repeat the previous undo or redo.
+/// If 'cpoptions' does not contain 'u': Always redo.
void u_redo(int count)
{
if (vim_strchr(p_cpo, CPO_UNDO) == NULL) {
@@ -1870,8 +1821,6 @@ bool u_undo_and_forget(int count)
/// @param do_buf_event If `true`, send the changedtick with the buffer updates
static void u_doit(int startcount, bool quiet, bool do_buf_event)
{
- int count = startcount;
-
if (!undo_allowed(curbuf)) {
return;
}
@@ -1881,6 +1830,8 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
u_oldcount = -1;
}
+
+ int count = startcount;
while (count--) {
// Do the change warning now, so that it triggers FileChangedRO when
// needed. This may cause the file to be reloaded, that must happen
@@ -1940,21 +1891,6 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
// "sec" must be false then.
void undo_time(long step, bool sec, bool file, bool absolute)
{
- long target;
- long closest;
- long closest_start;
- long closest_seq = 0;
- long val;
- u_header_T *uhp = NULL;
- u_header_T *last;
- int mark;
- int nomark = 0; // shut up compiler
- int round;
- bool dosec = sec;
- bool dofile = file;
- bool above = false;
- bool did_undo = true;
-
if (text_locked()) {
text_locked_msg();
return;
@@ -1971,6 +1907,14 @@ void undo_time(long step, bool sec, bool file, bool absolute)
u_oldcount = -1;
}
+ long target;
+ long closest;
+ u_header_T *uhp = NULL;
+ bool dosec = sec;
+ bool dofile = file;
+ bool above = false;
+ bool did_undo = true;
+
// "target" is the node below which we want to be.
// Init "closest" to a value we can't reach.
if (absolute) {
@@ -2034,8 +1978,10 @@ void undo_time(long step, bool sec, bool file, bool absolute)
}
}
}
- closest_start = closest;
- closest_seq = curbuf->b_u_seq_cur;
+ long closest_start = closest;
+ long closest_seq = curbuf->b_u_seq_cur;
+ int mark;
+ int nomark = 0; // shut up compiler
// When "target" is 0; Back to origin.
if (target == 0) {
@@ -2043,15 +1989,13 @@ void undo_time(long step, bool sec, bool file, bool absolute)
goto target_zero;
}
- /*
- * May do this twice:
- * 1. Search for "target", update "closest" to the best match found.
- * 2. If "target" not found search for "closest".
- *
- * When using the closest time we use the sequence number in the second
- * round, because there may be several entries with the same time.
- */
- for (round = 1; round <= 2; round++) {
+ // May do this twice:
+ // 1. Search for "target", update "closest" to the best match found.
+ // 2. If "target" not found search for "closest".
+ //
+ // When using the closest time we use the sequence number in the second
+ // round, because there may be several entries with the same time.
+ for (int round = 1; round <= 2; round++) {
// Find the path from the current state to where we want to go. The
// desired state can be anywhere in the undo tree, need to go all over
// it. We put "nomark" in uh_walk where we have been without success,
@@ -2067,13 +2011,9 @@ void undo_time(long step, bool sec, bool file, bool absolute)
while (uhp != NULL) {
uhp->uh_walk = mark;
- if (dosec) {
- val = (long)(uhp->uh_time);
- } else if (dofile) {
- val = uhp->uh_save_nr;
- } else {
- val = uhp->uh_seq;
- }
+ long val = dosec ? (long)(uhp->uh_time) :
+ dofile ? uhp->uh_save_nr
+ : uhp->uh_seq;
if (round == 1 && !(dofile && val == 0)) {
// Remember the header that is closest to the target.
@@ -2081,17 +2021,17 @@ void undo_time(long step, bool sec, bool file, bool absolute)
// "b_u_seq_cur"). When the timestamp is equal find the
// highest/lowest sequence number.
if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur
- : uhp->uh_seq > curbuf->b_u_seq_cur)
+ : uhp->uh_seq > curbuf->b_u_seq_cur)
&& ((dosec && val == closest)
? (step < 0
? uhp->uh_seq < closest_seq
: uhp->uh_seq > closest_seq)
- : closest == closest_start
+ : closest == closest_start
|| (val > target
? (closest > target
? val - target <= closest - target
: val - target <= target - closest)
- : (closest > target
+ : (closest > target
? target - val <= closest - target
: target - val <= target - closest)))) {
closest = val;
@@ -2110,11 +2050,10 @@ void undo_time(long step, bool sec, bool file, bool absolute)
if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
&& uhp->uh_prev.ptr->uh_walk != mark) {
uhp = uhp->uh_prev.ptr;
- }
- // go to alternate branch if we haven't been there
- else if (uhp->uh_alt_next.ptr != NULL
- && uhp->uh_alt_next.ptr->uh_walk != nomark
- && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ } else if (uhp->uh_alt_next.ptr != NULL
+ && uhp->uh_alt_next.ptr->uh_walk != nomark
+ && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ // go to alternate branch if we haven't been there
uhp = uhp->uh_alt_next.ptr;
} else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
// go up in the tree if we haven't been there and we are at the
@@ -2208,7 +2147,7 @@ target_zero:
}
// Find the last branch with a mark, that's the one.
- last = uhp;
+ u_header_T *last = uhp;
while (last->uh_alt_next.ptr != NULL
&& last->uh_alt_next.ptr->uh_walk == mark) {
last = last->uh_alt_next.ptr;
@@ -2285,19 +2224,10 @@ target_zero:
static void u_undoredo(int undo, bool do_buf_event)
{
char_u **newarray = NULL;
- linenr_T oldsize;
- linenr_T newsize;
- linenr_T top, bot;
- linenr_T lnum;
linenr_T newlnum = MAXLNUM;
- long i;
- u_entry_T *uep, *nuep;
+ u_entry_T *nuep;
u_entry_T *newlist = NULL;
- int old_flags;
- int new_flags;
fmark_T namedm[NMARKS];
- visualinfo_T visualinfo;
- bool empty_buffer; // buffer became empty
u_header_T *curhead = curbuf->b_u_curhead;
// Don't want autocommands using the undo structures here, they are
@@ -2305,28 +2235,26 @@ static void u_undoredo(int undo, bool do_buf_event)
block_autocmds();
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
- old_flags = curhead->uh_flags;
- new_flags = (curbuf->b_changed ? UH_CHANGED : 0)
- | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0)
- | (old_flags & UH_RELOAD);
+ int old_flags = curhead->uh_flags;
+ int new_flags = (curbuf->b_changed ? UH_CHANGED : 0)
+ | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0)
+ | (old_flags & UH_RELOAD);
setpcmark();
- /*
- * save marks before undo/redo
- */
+ // save marks before undo/redo
zero_fmark_additional_data(curbuf->b_namedm);
memmove(namedm, curbuf->b_namedm, sizeof(curbuf->b_namedm[0]) * NMARKS);
- visualinfo = curbuf->b_visual;
+ visualinfo_T visualinfo = curbuf->b_visual;
curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count;
curbuf->b_op_start.col = 0;
curbuf->b_op_end.lnum = 0;
curbuf->b_op_end.col = 0;
- for (uep = curhead->uh_entry; uep != NULL; uep = nuep) {
- top = uep->ue_top;
- bot = uep->ue_bot;
+ for (u_entry_T *uep = curhead->uh_entry; uep != NULL; uep = nuep) {
+ linenr_T top = uep->ue_top;
+ linenr_T bot = uep->ue_bot;
if (bot == 0) {
bot = curbuf->b_ml.ml_line_count + 1;
}
@@ -2338,21 +2266,22 @@ static void u_undoredo(int undo, bool do_buf_event)
return;
}
- oldsize = bot - top - 1; // number of lines before undo
- newsize = (linenr_T)uep->ue_size; // number of lines after undo
+ linenr_T oldsize = bot - top - 1; // number of lines before undo
+ linenr_T newsize = (linenr_T)uep->ue_size; // number of lines after undo
if (top < newlnum) {
- /* If the saved cursor is somewhere in this undo block, move it to
- * the remembered position. Makes "gwap" put the cursor back
- * where it was. */
- lnum = curhead->uh_cursor.lnum;
+ // If the saved cursor is somewhere in this undo block, move it to
+ // the remembered position. Makes "gwap" put the cursor back
+ // where it was.
+ linenr_T lnum = curhead->uh_cursor.lnum;
if (lnum >= top && lnum <= top + newsize + 1) {
curwin->w_cursor = curhead->uh_cursor;
newlnum = curwin->w_cursor.lnum - 1;
} else {
- /* Use the first line that actually changed. Avoids that
- * undoing auto-formatting puts the cursor in the previous
- * line. */
+ // Use the first line that actually changed. Avoids that
+ // undoing auto-formatting puts the cursor in the previous
+ // line.
+ long i;
for (i = 0; i < newsize && i < oldsize; i++) {
if (STRCMP(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) {
break;
@@ -2368,17 +2297,19 @@ static void u_undoredo(int undo, bool do_buf_event)
}
}
- empty_buffer = false;
+ bool empty_buffer = false;
// delete the lines between top and bot and save them in newarray
if (oldsize > 0) {
newarray = xmalloc(sizeof(char_u *) * (size_t)oldsize);
// delete backwards, it goes faster in most cases
- for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum) {
+ long i;
+ linenr_T lnum;
+ for (lnum = bot - 1, i = oldsize; --i >= 0; lnum--) {
// what can we do when we run out of memory?
newarray[i] = u_save_line(lnum);
- /* remember we deleted the last line in the buffer, and a
- * dummy empty line will be inserted */
+ // remember we deleted the last line in the buffer, and a
+ // dummy empty line will be inserted
if (curbuf->b_ml.ml_line_count == 1) {
empty_buffer = true;
}
@@ -2390,11 +2321,11 @@ static void u_undoredo(int undo, bool do_buf_event)
// insert the lines in u_array between top and bot
if (newsize) {
- for (lnum = top, i = 0; i < newsize; ++i, ++lnum) {
- /*
- * If the file is empty, there is an empty line 1 that we
- * should get rid of, by replacing it with the new line
- */
+ long i;
+ linenr_T lnum;
+ for (lnum = top, i = 0; i < newsize; i++, lnum++) {
+ // If the file is empty, there is an empty line 1 that we
+ // should get rid of, by replacing it with the new line
if (empty_buffer && lnum == 0) {
ml_replace((linenr_T)1, (char *)uep->ue_array[i], true);
} else {
@@ -2435,9 +2366,7 @@ static void u_undoredo(int undo, bool do_buf_event)
uep->ue_array = newarray;
uep->ue_bot = top + newsize + 1;
- /*
- * insert this entry in front of the new entry list
- */
+ // insert this entry in front of the new entry list
nuep = uep->ue_next;
uep->ue_next = newlist;
newlist = uep;
@@ -2454,13 +2383,13 @@ static void u_undoredo(int undo, bool do_buf_event)
// Adjust Extmarks
ExtmarkUndoObject undo_info;
if (undo) {
- for (i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) {
+ for (long i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) {
undo_info = kv_A(curhead->uh_extmark, i);
extmark_apply_undo(undo_info, undo);
}
// redo
} else {
- for (i = 0; i < (int)kv_size(curhead->uh_extmark); i++) {
+ for (long i = 0; i < (int)kv_size(curhead->uh_extmark); i++) {
undo_info = kv_A(curhead->uh_extmark, i);
extmark_apply_undo(undo_info, undo);
}
@@ -2490,10 +2419,8 @@ static void u_undoredo(int undo, bool do_buf_event)
buf_updates_changedtick(curbuf);
}
- /*
- * restore marks from before undo/redo
- */
- for (i = 0; i < NMARKS; ++i) {
+ // restore marks from before undo/redo
+ for (long i = 0; i < NMARKS; i++) {
if (curhead->uh_namedm[i].mark.lnum != 0) {
free_fmark(curbuf->b_namedm[i]);
curbuf->b_namedm[i] = curhead->uh_namedm[i];
@@ -2509,14 +2436,12 @@ static void u_undoredo(int undo, bool do_buf_event)
curhead->uh_visual = visualinfo;
}
- /*
- * If the cursor is only off by one line, put it at the same position as
- * before starting the change (for the "o" command).
- * Otherwise the cursor should go to the first undone line.
- */
+ // If the cursor is only off by one line, put it at the same position as
+ // before starting the change (for the "o" command).
+ // Otherwise the cursor should go to the first undone line.
if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum
&& curwin->w_cursor.lnum > 1) {
- --curwin->w_cursor.lnum;
+ curwin->w_cursor.lnum--;
}
if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) {
if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) {
@@ -2565,7 +2490,7 @@ static void u_undoredo(int undo, bool do_buf_event)
unblock_autocmds();
#ifdef U_DEBUG
- u_check(FALSE);
+ u_check(false);
#endif
}
@@ -2577,10 +2502,6 @@ static void u_undoredo(int undo, bool do_buf_event)
/// @param absolute used ":undo N"
static void u_undo_end(bool did_undo, bool absolute, bool quiet)
{
- char *msgstr;
- u_header_T *uhp;
- char_u msgbuf[80];
-
if ((fdo_flags & FDO_UNDO) && KeyTyped) {
foldOpenCursor();
}
@@ -2592,10 +2513,11 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
if (curbuf->b_ml.ml_flags & ML_EMPTY) {
- --u_newcount;
+ u_newcount--;
}
u_oldcount -= u_newcount;
+ char *msgstr;
if (u_oldcount == -1) {
msgstr = N_("more line");
} else if (u_oldcount < 0) {
@@ -2613,6 +2535,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
}
}
+ u_header_T *uhp;
if (curbuf->b_u_curhead != NULL) {
// For ":undo N" we prefer a "after #N" message.
if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL) {
@@ -2627,6 +2550,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
uhp = curbuf->b_u_newhead;
}
+ char_u msgbuf[80];
if (uhp == NULL) {
*msgbuf = NUL;
} else {
@@ -2636,7 +2560,7 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_buffer == curbuf && wp->w_p_cole > 0) {
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
}
@@ -2657,9 +2581,8 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
void undo_fmt_time(char_u *buf, size_t buflen, time_t tt)
{
- struct tm curtime;
-
if (time(NULL) - tt >= 100) {
+ struct tm curtime;
os_localtime_r(&tt, &curtime);
if (time(NULL) - tt < (60L * 60L * 12L)) {
// within 12 hours
@@ -2695,27 +2618,20 @@ void u_sync(bool force)
}
}
-/*
- * ":undolist": List the leafs of the undo tree
- */
+/// ":undolist": List the leafs of the undo tree
void ex_undolist(exarg_T *eap)
{
- garray_T ga;
- u_header_T *uhp;
- int mark;
- int nomark;
int changes = 1;
- /*
- * 1: walk the tree to find all leafs, put the info in "ga".
- * 2: sort the lines
- * 3: display the list
- */
- mark = ++lastmark;
- nomark = ++lastmark;
+ // 1: walk the tree to find all leafs, put the info in "ga".
+ // 2: sort the lines
+ // 3: display the list
+ int mark = ++lastmark;
+ int nomark = ++lastmark;
+ garray_T ga;
ga_init(&ga, (int)sizeof(char *), 20);
- uhp = curbuf->b_u_oldhead;
+ u_header_T *uhp = curbuf->b_u_oldhead;
while (uhp != NULL) {
if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark
&& uhp->uh_walk != mark) {
@@ -2736,12 +2652,11 @@ void ex_undolist(exarg_T *eap)
if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark
&& uhp->uh_prev.ptr->uh_walk != mark) {
uhp = uhp->uh_prev.ptr;
- ++changes;
- }
- // go to alternate branch if we haven't been there
- else if (uhp->uh_alt_next.ptr != NULL
- && uhp->uh_alt_next.ptr->uh_walk != nomark
- && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ changes++;
+ } else if (uhp->uh_alt_next.ptr != NULL
+ && uhp->uh_alt_next.ptr->uh_walk != nomark
+ && uhp->uh_alt_next.ptr->uh_walk != mark) {
+ // go to alternate branch if we haven't been there
uhp = uhp->uh_alt_next.ptr;
} else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL
// go up in the tree if we haven't been there and we are at the
@@ -2749,7 +2664,7 @@ void ex_undolist(exarg_T *eap)
&& uhp->uh_next.ptr->uh_walk != nomark
&& uhp->uh_next.ptr->uh_walk != mark) {
uhp = uhp->uh_next.ptr;
- --changes;
+ changes--;
} else {
// need to backtrack; mark this node as done
uhp->uh_walk = nomark;
@@ -2757,7 +2672,7 @@ void ex_undolist(exarg_T *eap)
uhp = uhp->uh_alt_prev.ptr;
} else {
uhp = uhp->uh_next.ptr;
- --changes;
+ changes--;
}
}
}
@@ -2783,9 +2698,7 @@ void ex_undolist(exarg_T *eap)
}
}
-/*
- * ":undojoin": continue adding to the last entry list
- */
+/// ":undojoin": continue adding to the last entry list
void ex_undojoin(exarg_T *eap)
{
if (curbuf->b_u_newhead == NULL) {
@@ -2805,35 +2718,30 @@ void ex_undojoin(exarg_T *eap)
}
}
-/*
- * Called after writing or reloading the file and setting b_changed to FALSE.
- * Now an undo means that the buffer is modified.
- */
+/// Called after writing or reloading the file and setting b_changed to false.
+/// Now an undo means that the buffer is modified.
void u_unchanged(buf_T *buf)
{
u_unch_branch(buf->b_u_oldhead);
buf->b_did_warn = false;
}
-/*
- * After reloading a buffer which was saved for 'undoreload': Find the first
- * line that was changed and set the cursor there.
- */
+/// After reloading a buffer which was saved for 'undoreload': Find the first
+/// line that was changed and set the cursor there.
void u_find_first_changed(void)
{
u_header_T *uhp = curbuf->b_u_newhead;
- u_entry_T *uep;
- linenr_T lnum;
if (curbuf->b_u_curhead != NULL || uhp == NULL) {
return; // undid something in an autocmd?
}
// Check that the last undo block was for the whole file.
- uep = uhp->uh_entry;
+ u_entry_T *uep = uhp->uh_entry;
if (uep->ue_top != 0 || uep->ue_bot != 0) {
return;
}
+ linenr_T lnum;
for (lnum = 1; lnum < curbuf->b_ml.ml_line_count
&& lnum <= uep->ue_size; lnum++) {
if (STRCMP(ml_get_buf(curbuf, lnum, false), uep->ue_array[lnum - 1]) != 0) {
@@ -2849,17 +2757,13 @@ void u_find_first_changed(void)
}
}
-/*
- * Increase the write count, store it in the last undo header, what would be
- * used for "u".
- */
+/// Increase the write count, store it in the last undo header, what would be
+/// used for "u".
void u_update_save_nr(buf_T *buf)
{
- u_header_T *uhp;
-
- ++buf->b_u_save_nr_last;
+ buf->b_u_save_nr_last++;
buf->b_u_save_nr_cur = buf->b_u_save_nr_last;
- uhp = buf->b_u_curhead;
+ u_header_T *uhp = buf->b_u_curhead;
if (uhp != NULL) {
uhp = uhp->uh_next.ptr;
} else {
@@ -2872,9 +2776,7 @@ void u_update_save_nr(buf_T *buf)
static void u_unch_branch(u_header_T *uhp)
{
- u_header_T *uh;
-
- for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr) {
+ for (u_header_T *uh = uhp; uh != NULL; uh = uh->uh_prev.ptr) {
uh->uh_flags |= UH_CHANGED;
if (uh->uh_alt_next.ptr != NULL) {
u_unch_branch(uh->uh_alt_next.ptr); // recursive
@@ -2882,10 +2784,8 @@ static void u_unch_branch(u_header_T *uhp)
}
}
-/*
- * Get pointer to last added entry.
- * If it's not valid, give an error message and return NULL.
- */
+/// Get pointer to last added entry.
+/// If it's not valid, give an error message and return NULL.
static u_entry_T *u_get_headentry(buf_T *buf)
{
if (buf->b_u_newhead == NULL || buf->b_u_newhead->uh_entry == NULL) {
@@ -2895,28 +2795,21 @@ static u_entry_T *u_get_headentry(buf_T *buf)
return buf->b_u_newhead->uh_entry;
}
-/*
- * u_getbot(): compute the line number of the previous u_save
- * It is called only when b_u_synced is false.
- */
+/// u_getbot(): compute the line number of the previous u_save
+/// It is called only when b_u_synced is false.
static void u_getbot(buf_T *buf)
{
- u_entry_T *uep;
- linenr_T extra;
-
- uep = u_get_headentry(buf); // check for corrupt undo list
+ u_entry_T *uep = u_get_headentry(buf); // check for corrupt undo list
if (uep == NULL) {
return;
}
uep = buf->b_u_newhead->uh_getbot_entry;
if (uep != NULL) {
- /*
- * the new ue_bot is computed from the number of lines that has been
- * inserted (0 - deleted) since calling u_save. This is equal to the
- * old line count subtracted from the current line count.
- */
- extra = buf->b_ml.ml_line_count - uep->ue_lcount;
+ // the new ue_bot is computed from the number of lines that has been
+ // inserted (0 - deleted) since calling u_save. This is equal to the
+ // old line count subtracted from the current line count.
+ linenr_T extra = buf->b_ml.ml_line_count - uep->ue_lcount;
uep->ue_bot = uep->ue_top + (linenr_T)uep->ue_size + 1 + extra;
if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) {
iemsg(_("E440: undo line missing"));
@@ -2937,8 +2830,6 @@ static void u_getbot(buf_T *buf)
/// @param uhpp if not NULL reset when freeing this header
static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
{
- u_header_T *uhap;
-
// When there is an alternate redo list free that branch completely,
// because we can never go there.
if (uhp->uh_alt_next.ptr != NULL) {
@@ -2959,7 +2850,7 @@ static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
if (uhp->uh_prev.ptr == NULL) {
buf->b_u_newhead = uhp->uh_next.ptr;
} else {
- for (uhap = uhp->uh_prev.ptr; uhap != NULL;
+ for (u_header_T *uhap = uhp->uh_prev.ptr; uhap != NULL;
uhap = uhap->uh_alt_next.ptr) {
uhap->uh_next.ptr = uhp->uh_next.ptr;
}
@@ -2973,8 +2864,6 @@ static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
/// @param uhpp if not NULL reset when freeing this header
static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
{
- u_header_T *tofree, *next;
-
// If this is the top branch we may need to use u_freeheader() to update
// all the pointers.
if (uhp == buf->b_u_oldhead) {
@@ -2988,9 +2877,9 @@ static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL;
}
- next = uhp;
+ u_header_T *next = uhp;
while (next != NULL) {
- tofree = next;
+ u_header_T *tofree = next;
if (tofree->uh_alt_next.ptr != NULL) {
u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive
}
@@ -3029,12 +2918,10 @@ static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
uhp->uh_magic = 0;
#endif
xfree((char_u *)uhp);
- --buf->b_u_numhead;
+ buf->b_u_numhead--;
}
-/*
- * free entry 'uep' and 'n' lines in uep->ue_array[]
- */
+/// free entry 'uep' and 'n' lines in uep->ue_array[]
static void u_freeentry(u_entry_T *uep, long n)
{
while (n > 0) {
@@ -3047,9 +2934,7 @@ static void u_freeentry(u_entry_T *uep, long n)
xfree((char_u *)uep);
}
-/*
- * invalidate the undo buffer; called when storage has already been released
- */
+/// invalidate the undo buffer; called when storage has already been released
void u_clearall(buf_T *buf)
{
buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL;
@@ -3059,9 +2944,7 @@ void u_clearall(buf_T *buf)
buf->b_u_line_lnum = 0;
}
-/*
- * save the line "lnum" for the "U" command
- */
+/// save the line "lnum" for the "U" command
void u_saveline(linenr_T lnum)
{
if (lnum == curbuf->b_u_line_lnum) { // line is already saved
@@ -3077,13 +2960,11 @@ void u_saveline(linenr_T lnum)
} else {
curbuf->b_u_line_colnr = 0;
}
- curbuf->b_u_line_ptr = u_save_line(lnum);
+ curbuf->b_u_line_ptr = (char *)u_save_line(lnum);
}
-/*
- * clear the line saved for the "U" command
- * (this is used externally for crossing a line while in insert mode)
- */
+/// clear the line saved for the "U" command
+/// (this is used externally for crossing a line while in insert mode)
void u_clearline(void)
{
if (curbuf->b_u_line_ptr != NULL) {
@@ -3092,17 +2973,12 @@ void u_clearline(void)
}
}
-/*
- * Implementation of the "U" command.
- * Differentiation from vi: "U" can be undone with the next "U".
- * We also allow the cursor to be in another line.
- * Careful: may trigger autocommands that reload the buffer.
- */
+/// Implementation of the "U" command.
+/// Differentiation from vi: "U" can be undone with the next "U".
+/// We also allow the cursor to be in another line.
+/// Careful: may trigger autocommands that reload the buffer.
void u_undoline(void)
{
- colnr_T t;
- char_u *oldp;
-
if (curbuf->b_u_line_ptr == NULL
|| curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count) {
beep_flush();
@@ -3115,15 +2991,15 @@ void u_undoline(void)
return;
}
- oldp = u_save_line(curbuf->b_u_line_lnum);
- ml_replace(curbuf->b_u_line_lnum, (char *)curbuf->b_u_line_ptr, true);
+ char_u *oldp = u_save_line(curbuf->b_u_line_lnum);
+ ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true);
changed_bytes(curbuf->b_u_line_lnum, 0);
extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum - 1, 0, (colnr_T)STRLEN(oldp),
(colnr_T)STRLEN(curbuf->b_u_line_ptr), kExtmarkUndo);
xfree(curbuf->b_u_line_ptr);
- curbuf->b_u_line_ptr = oldp;
+ curbuf->b_u_line_ptr = (char *)oldp;
- t = curbuf->b_u_line_colnr;
+ colnr_T t = curbuf->b_u_line_colnr;
if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) {
curbuf->b_u_line_colnr = curwin->w_cursor.col;
}
@@ -3132,9 +3008,7 @@ void u_undoline(void)
check_cursor_col();
}
-/*
- * Free all allocated memory blocks for the buffer 'buf'.
- */
+/// Free all allocated memory blocks for the buffer 'buf'.
void u_blockfree(buf_T *buf)
{
while (buf->b_u_oldhead != NULL) {
diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h
index d8470b07b1..4b64f97919 100644
--- a/src/nvim/undo_defs.h
+++ b/src/nvim/undo_defs.h
@@ -74,10 +74,4 @@ struct u_header {
#define UH_EMPTYBUF 0x02 // buffer was empty
#define UH_RELOAD 0x04 // buffer was reloaded
-/// Structure passed around between undofile functions.
-typedef struct {
- buf_T *bi_buf;
- FILE *bi_fp;
-} bufinfo_T;
-
#endif // NVIM_UNDO_DEFS_H
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index 15197dc504..5b2101587f 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -12,10 +12,12 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/charset.h"
+#include "nvim/eval.h"
#include "nvim/ex_docmd.h"
#include "nvim/garray.h"
#include "nvim/lua/executor.h"
#include "nvim/os/input.h"
+#include "nvim/runtime.h"
#include "nvim/usercmd.h"
#include "nvim/window.h"
@@ -104,7 +106,7 @@ static struct {
/// Return NULL if there is no matching command.
///
/// @param *p end of the command (possibly including count)
-/// @param full set to TRUE for a full match
+/// @param full set to true for a full match
/// @param xp used for completion, NULL otherwise
/// @param complp completion flags or NULL
char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
@@ -125,7 +127,7 @@ char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
for (j = 0; j < gap->ga_len; j++) {
uc = USER_CMD_GA(gap, j);
cp = eap->cmd;
- np = (char *)uc->uc_name;
+ np = uc->uc_name;
k = 0;
while (k < len && *np != NUL && *cp++ == *np++) {
k++;
@@ -165,9 +167,9 @@ char *find_ucmd(exarg_T *eap, char *p, int *full, expand_T *xp, int *complp)
}
if (xp != NULL) {
xp->xp_luaref = uc->uc_compl_luaref;
- xp->xp_arg = (char *)uc->uc_compl_arg;
+ xp->xp_arg = uc->uc_compl_arg;
xp->xp_script_ctx = uc->uc_script_ctx;
- xp->xp_script_ctx.sc_lnum += sourcing_lnum;
+ xp->xp_script_ctx.sc_lnum += SOURCING_LNUM;
}
// Do not search for further abbreviations
// if this is an exact match.
@@ -214,7 +216,7 @@ const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in)
// Check for attributes
while (*arg == '-') {
arg++; // Skip "-".
- p = (const char *)skiptowhite((const char_u *)arg);
+ p = (const char *)skiptowhite(arg);
if (*p == NUL) {
// Cursor is still in the attribute.
p = strchr(arg, '=');
@@ -246,7 +248,7 @@ const char *set_context_in_user_cmd(expand_T *xp, const char *arg_in)
}
// After the attributes comes the new command name.
- p = (const char *)skiptowhite((const char_u *)arg);
+ p = (const char *)skiptowhite(arg);
if (*p == NUL) {
xp->xp_context = EXPAND_USER_COMMANDS;
xp->xp_pattern = (char *)arg;
@@ -270,12 +272,12 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
const buf_T *const buf = prevwin_curwin()->w_buffer;
if (idx < buf->b_ucmds.ga_len) {
- return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
+ return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
}
idx -= buf->b_ucmds.ga_len;
if (idx < ucmds.ga_len) {
- char *name = (char *)USER_CMD(idx)->uc_name;
+ char *name = USER_CMD(idx)->uc_name;
for (int i = 0; i < buf->b_ucmds.ga_len; i++) {
if (STRCMP(name, USER_CMD_GA(&buf->b_ucmds, i)->uc_name) == 0) {
@@ -295,14 +297,14 @@ char *get_user_commands(expand_T *xp FUNC_ATTR_UNUSED, int idx)
char *get_user_command_name(int idx, int cmdidx)
{
if (cmdidx == CMD_USER && idx < ucmds.ga_len) {
- return (char *)USER_CMD(idx)->uc_name;
+ return USER_CMD(idx)->uc_name;
}
if (cmdidx == CMD_USER_BUF) {
// In cmdwin, the alternative buffer should be used.
const buf_T *const buf = prevwin_curwin()->w_buffer;
if (idx < buf->b_ucmds.ga_len) {
- return (char *)USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
+ return USER_CMD_GA(&buf->b_ucmds, idx)->uc_name;
}
}
return NULL;
@@ -529,7 +531,7 @@ static void uc_list(char *name, size_t name_len)
}
}
- msg_outtrans_special(cmd->uc_rep, false,
+ msg_outtrans_special((char_u *)cmd->uc_rep, false,
name_len == 0 ? Columns - 47 : 0);
if (p_verbose > 0) {
last_set_msg(cmd->uc_script_ctx);
@@ -881,17 +883,17 @@ int uc_add_command(char *name, size_t name_len, const char *rep, uint32_t argt,
gap->ga_len++;
- cmd->uc_name = (char_u *)p;
+ cmd->uc_name = p;
}
- cmd->uc_rep = (char_u *)rep_buf;
+ cmd->uc_rep = rep_buf;
cmd->uc_argt = argt;
cmd->uc_def = def;
cmd->uc_compl = compl;
cmd->uc_script_ctx = current_sctx;
- cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
+ cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&cmd->uc_script_ctx);
- cmd->uc_compl_arg = (char_u *)compl_arg;
+ cmd->uc_compl_arg = compl_arg;
cmd->uc_compl_luaref = compl_luaref;
cmd->uc_preview_luaref = preview_luaref;
cmd->uc_addr_type = addr_type;
@@ -928,7 +930,7 @@ void ex_command(exarg_T *eap)
// Check for attributes
while (*p == '-') {
p++;
- end = (char *)skiptowhite((char_u *)p);
+ end = skiptowhite(p);
if (uc_scan_attr(p, (size_t)(end - p), &argt, &def, &flags, &compl, (char_u **)&compl_arg,
&addr_type_arg) == FAIL) {
return;
@@ -1059,11 +1061,11 @@ bool uc_split_args_iter(const char *arg, size_t arglen, size_t *end, char *buf,
buf[l++] = arg[++pos];
} else {
buf[l++] = arg[pos];
- if (ascii_iswhite(arg[pos + 1])) {
- *end = pos + 1;
- *len = l;
- return false;
- }
+ }
+ if (ascii_iswhite(arg[pos + 1])) {
+ *end = pos + 1;
+ *len = l;
+ return false;
}
}
@@ -1162,7 +1164,7 @@ static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc,
*q++ = ' ';
*q++ = '"';
} else {
- mb_copy_char((const char_u **)&p, (char_u **)&q);
+ mb_copy_char((const char **)&p, &q);
}
}
} else {
@@ -1175,7 +1177,7 @@ static char *uc_split_args(char *arg, char **args, size_t *arglens, size_t argc,
*q++ = '\\';
*q++ = *p++;
} else {
- mb_copy_char((const char_u **)&p, (char_u **)&q);
+ mb_copy_char((const char **)&p, &q);
}
}
if (i != argc - 1) {
@@ -1567,7 +1569,7 @@ int do_ucmd(exarg_T *eap, bool preview)
// Second round: copy result into "buf".
buf = NULL;
for (;;) {
- p = (char *)cmd->uc_rep; // source
+ p = cmd->uc_rep; // source
q = buf; // destination
totlen = 0;
diff --git a/src/nvim/usercmd.h b/src/nvim/usercmd.h
index 637862b216..4d2cf0d9de 100644
--- a/src/nvim/usercmd.h
+++ b/src/nvim/usercmd.h
@@ -4,14 +4,14 @@
#include "nvim/ex_cmds_defs.h"
typedef struct ucmd {
- char_u *uc_name; // The command name
+ char *uc_name; // The command name
uint32_t uc_argt; // The argument type
- char_u *uc_rep; // The command's replacement string
+ char *uc_rep; // The command's replacement string
long uc_def; // The default value for a range/count
int uc_compl; // completion type
cmd_addr_T uc_addr_type; // The command's address type
sctx_T uc_script_ctx; // SCTX where the command was defined
- char_u *uc_compl_arg; // completion argument if any
+ char *uc_compl_arg; // completion argument if any
LuaRef uc_compl_luaref; // Reference to Lua completion function
LuaRef uc_preview_luaref; // Reference to Lua preview function
LuaRef uc_luaref; // Reference to Lua function
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 3ffae6592c..5888d4614e 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -14,12 +14,13 @@
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
+#include "nvim/drawscreen.h"
+#include "nvim/grid.h"
#include "nvim/iconv.h"
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/screen.h"
#include "nvim/strings.h"
#include "nvim/version.h"
#include "nvim/vim.h"
@@ -2015,7 +2016,7 @@ void ex_version(exarg_T *eap)
/// Output a string for the version message. If it's going to wrap, output a
/// newline, unless the message is too long to fit on the screen anyway.
-/// When "wrap" is TRUE wrap the string in [].
+/// When "wrap" is true wrap the string in [].
/// @param s
/// @param wrap
static void version_msg_wrap(char *s, int wrap)
@@ -2063,7 +2064,7 @@ static void list_features(void)
/// List string items nicely aligned in columns.
/// When "size" is < 0 then the last entry is marked with NULL.
/// The entry with index "current" is inclosed in [].
-void list_in_columns(char_u **items, int size, int current)
+void list_in_columns(char **items, int size, int current)
{
int item_count = 0;
int width = 0;
@@ -2071,7 +2072,7 @@ void list_in_columns(char_u **items, int size, int current)
// Find the length of the longest item, use that + 1 as the column width.
int i;
for (i = 0; size < 0 ? items[i] != NULL : i < size; i++) {
- int l = vim_strsize((char *)items[i]) + (i == current ? 2 : 0);
+ int l = vim_strsize(items[i]) + (i == current ? 2 : 0);
if (l > width) {
width = l;
@@ -2083,7 +2084,7 @@ void list_in_columns(char_u **items, int size, int current)
if (Columns < width) {
// Not enough screen columns - show one per line
for (i = 0; i < item_count; i++) {
- version_msg_wrap((char *)items[i], i == current);
+ version_msg_wrap(items[i], i == current);
if (msg_col > 0 && i < item_count - 1) {
msg_putchar('\n');
}
@@ -2105,7 +2106,7 @@ void list_in_columns(char_u **items, int size, int current)
if (idx == current) {
msg_putchar('[');
}
- msg_puts((char *)items[idx]);
+ msg_puts(items[idx]);
if (idx == current) {
msg_putchar(']');
}
@@ -2199,7 +2200,7 @@ void maybe_intro_message(void)
if (buf_is_empty(curbuf)
&& (curbuf->b_fname == NULL)
&& (firstwin->w_next == NULL)
- && (vim_strchr((char *)p_shm, SHM_INTRO) == NULL)) {
+ && (vim_strchr(p_shm, SHM_INTRO) == NULL)) {
intro_message(false);
}
}
@@ -2208,7 +2209,7 @@ void maybe_intro_message(void)
/// Only used when starting Vim on an empty file, without a file name.
/// Or with the ":intro" command (for Sven :-).
///
-/// @param colon TRUE for ":intro"
+/// @param colon true for ":intro"
void intro_message(int colon)
{
int i;
@@ -2255,7 +2256,7 @@ void intro_message(int colon)
row = blanklines / 2;
if (((row >= 2) && (Columns >= 50)) || colon) {
- for (i = 0; i < (int)ARRAY_SIZE(lines); ++i) {
+ for (i = 0; i < (int)ARRAY_SIZE(lines); i++) {
p = lines[i];
if (sponsor != 0) {
@@ -2273,7 +2274,7 @@ void intro_message(int colon)
}
if (*p != NUL) {
- do_intro_line(row, (char_u *)_(p), 0);
+ do_intro_line(row, _(p), 0);
}
row++;
}
@@ -2286,15 +2287,14 @@ void intro_message(int colon)
}
}
-static void do_intro_line(long row, char_u *mesg, int attr)
+static void do_intro_line(long row, char *mesg, int attr)
{
- long col;
- char_u *p;
+ char *p;
int l;
int clen;
// Center the message horizontally.
- col = vim_strsize((char *)mesg);
+ long col = vim_strsize(mesg);
col = (Columns - col) / 2;
@@ -2309,8 +2309,8 @@ static void do_intro_line(long row, char_u *mesg, int attr)
for (l = 0;
p[l] != NUL && (l == 0 || (p[l] != '<' && p[l - 1] != '>'));
l++) {
- clen += ptr2cells((char *)p + l);
- l += utfc_ptr2len((char *)p + l) - 1;
+ clen += ptr2cells(p + l);
+ l += utfc_ptr2len(p + l) - 1;
}
assert(row <= INT_MAX && col <= INT_MAX);
grid_puts_len(&default_grid, p, l, (int)row, (int)col,
@@ -2325,6 +2325,6 @@ static void do_intro_line(long row, char_u *mesg, int attr)
void ex_intro(exarg_T *eap)
{
screenclear();
- intro_message(TRUE);
- wait_return(TRUE);
+ intro_message(true);
+ wait_return(true);
}
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 31ac5a67ff..ec8d6ab153 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -167,15 +167,6 @@ enum {
#define MIN_SWAP_PAGE_SIZE 1048
#define MAX_SWAP_PAGE_SIZE 50000
-// Boolean constants
-
-#ifndef TRUE
-# define FALSE 0 // note: this is an int, not a long!
-# define TRUE 1
-#endif
-
-#define MAYBE 2 // sometimes used for a variant on TRUE
-
#define STATUS_HEIGHT 1 // height of a status line under a window
#define QF_WINHEIGHT 10 // default height for quickfix window
@@ -199,6 +190,7 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
// Size in bytes of the hash used in the undo file.
#define UNDO_HASH_SIZE 32
+#define CLEAR_FIELD(field) memset(&(field), 0, sizeof(field))
#define CLEAR_POINTER(ptr) memset((ptr), 0, sizeof(*(ptr)))
// defines to avoid typecasts from (char_u *) to (char *) and back
@@ -238,10 +230,7 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
# endif
#endif
-#define STRRCHR(s, c) (char_u *)strrchr((const char *)(s), (c))
-
-#define STRCAT(d, s) strcat((char *)(d), (char *)(s))
-#define STRNCAT(d, s, n) strncat((char *)(d), (char *)(s), (size_t)(n))
+#define STRCAT(d, s) strcat((char *)(d), (char *)(s)) // NOLINT(runtime/printf)
#define STRLCAT(d, s, n) xstrlcat((char *)(d), (char *)(s), (size_t)(n))
// Character used as separated in autoload function/variable names.
@@ -274,8 +263,8 @@ enum { FOLD_TEXT_LEN = 51, }; //!< buffer size for get_foldtext()
(const char *)(y), \
(size_t)(n))
-// Enums need a typecast to be used as array index (for Ultrix).
-#define HL_ATTR(n) highlight_attr[(int)(n)]
+// Enums need a typecast to be used as array index.
+#define HL_ATTR(n) hl_attr_active[(int)(n)]
/// Maximum number of bytes in a multi-byte character. It can be one 32-bit
/// character of up to 6 bytes, or one 16-bit character of up to three bytes
diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c
index fd7dc17ee3..387b9d61f2 100644
--- a/src/nvim/viml/parser/expressions.c
+++ b/src/nvim/viml/parser/expressions.c
@@ -1828,13 +1828,13 @@ static void parse_quoted_string(ParserState *const pstate, ExprASTNode *const no
v_p += special_len;
} else {
is_unknown = true;
- mb_copy_char((const char_u **)&p, (char_u **)&v_p);
+ mb_copy_char(&p, &v_p);
}
break;
}
default:
is_unknown = true;
- mb_copy_char((const char_u **)&p, (char_u **)&v_p);
+ mb_copy_char(&p, &v_p);
break;
}
if (pstate->colors) {
diff --git a/src/nvim/viml/parser/parser.h b/src/nvim/viml/parser/parser.h
index b8835127e7..55f54cedbe 100644
--- a/src/nvim/viml/parser/parser.h
+++ b/src/nvim/viml/parser/parser.h
@@ -142,9 +142,7 @@ static inline void viml_preader_get_line(ParserInputReader *const preader,
.allocated = true,
.size = pline.size,
};
- cpline.data = (char *)string_convert(&preader->conv,
- (char_u *)pline.data,
- &cpline.size);
+ cpline.data = string_convert(&preader->conv, (char *)pline.data, &cpline.size);
if (pline.allocated) {
xfree((void *)pline.data);
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index b737215616..74ea1172ee 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6,11 +6,14 @@
#include <stdbool.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/api/vim.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/diff.h"
+#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
@@ -25,7 +28,9 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/globals.h"
+#include "nvim/grid.h"
#include "nvim/hashtab.h"
+#include "nvim/highlight.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mark.h"
@@ -37,13 +42,13 @@
#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
#include "nvim/strings.h"
@@ -122,7 +127,7 @@ void do_window(int nchar, long Prenum, int xchar)
{
long Prenum1;
win_T *wp;
- char_u *ptr;
+ char *ptr;
linenr_T lnum = -1;
int type = FIND_DEFINE;
size_t len;
@@ -210,7 +215,7 @@ newwindow:
case Ctrl_Q:
case 'q':
reset_VIsual_and_resel(); // stop Visual mode
- cmd_with_count("quit", (char_u *)cbuf, sizeof(cbuf), Prenum);
+ cmd_with_count("quit", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
break;
@@ -218,7 +223,7 @@ newwindow:
case Ctrl_C:
case 'c':
reset_VIsual_and_resel(); // stop Visual mode
- cmd_with_count("close", (char_u *)cbuf, sizeof(cbuf), Prenum);
+ cmd_with_count("close", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
break;
@@ -251,7 +256,7 @@ newwindow:
case 'o':
CHECK_CMDWIN;
reset_VIsual_and_resel(); // stop Visual mode
- cmd_with_count("only", (char_u *)cbuf, sizeof(cbuf), Prenum);
+ cmd_with_count("only", cbuf, sizeof(cbuf), Prenum);
do_cmdline_cmd(cbuf);
break;
@@ -483,14 +488,14 @@ newwindow:
wingotofile:
CHECK_CMDWIN;
- ptr = grab_file_name(Prenum1, &lnum);
+ ptr = (char *)grab_file_name(Prenum1, &lnum);
if (ptr != NULL) {
tabpage_T *oldtab = curtab;
win_T *oldwin = curwin;
setpcmark();
if (win_split(0, 0) == OK) {
RESET_BINDING(curwin);
- if (do_ecmd(0, (char *)ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) {
+ if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) {
// Failed to open the file, close the window opened for it.
win_close(curwin, false, false);
goto_tabpage_win(oldtab, oldwin);
@@ -518,9 +523,9 @@ wingotofile:
}
// Make a copy, if the line was changed it will be freed.
- ptr = vim_strnsave(ptr, len);
+ ptr = xstrnsave(ptr, len);
- find_pattern_in_path(ptr, 0, len, true, Prenum == 0,
+ find_pattern_in_path((char_u *)ptr, 0, len, true, Prenum == 0,
type, Prenum1, ACTION_SPLIT, 1, MAXLNUM);
xfree(ptr);
curwin->w_set_curswant = true;
@@ -617,12 +622,12 @@ wingotofile:
}
}
-static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Prenum)
+static void cmd_with_count(char *cmd, char *bufp, size_t bufsize, int64_t Prenum)
{
size_t len = STRLCPY(bufp, cmd, bufsize);
if (Prenum > 0 && len < bufsize) {
- vim_snprintf((char *)bufp + len, bufsize - len, "%" PRId64, Prenum);
+ vim_snprintf(bufp + len, bufsize - len, "%" PRId64, Prenum);
}
}
@@ -698,16 +703,16 @@ win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err)
win_remove(wp, NULL);
win_append(lastwin_nofloating(), wp);
}
- wp->w_floating = 1;
+ wp->w_floating = true;
wp->w_status_height = 0;
wp->w_winbar_height = 0;
wp->w_hsep_height = 0;
wp->w_vsep_width = 0;
win_config_float(wp, fconfig);
- win_set_inner_size(wp);
+ win_set_inner_size(wp, true);
wp->w_pos_changed = true;
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
return wp;
}
@@ -722,36 +727,38 @@ void win_set_minimal_style(win_T *wp)
// Hide EOB region: use " " fillchar and cleared highlighting
if (wp->w_p_fcs_chars.eob != ' ') {
- char_u *old = wp->w_p_fcs;
+ char_u *old = (char_u *)wp->w_p_fcs;
wp->w_p_fcs = ((*old == NUL)
- ? (char_u *)xstrdup("eob: ")
- : concat_str(old, (char_u *)",eob: "));
- free_string_option(old);
- }
- if (wp->w_hl_ids[HLF_EOB] != -1) {
- char_u *old = wp->w_p_winhl;
- wp->w_p_winhl = ((*old == NUL)
- ? (char_u *)xstrdup("EndOfBuffer:")
- : concat_str(old, (char_u *)",EndOfBuffer:"));
- free_string_option(old);
+ ? xstrdup("eob: ")
+ : concat_str((char *)old, ",eob: "));
+ free_string_option((char *)old);
}
+ // TODO(bfredl): this could use a highlight namespace directly,
+ // and avoid pecularities around window options
+ char_u *old = (char_u *)wp->w_p_winhl;
+ wp->w_p_winhl = ((*old == NUL)
+ ? xstrdup("EndOfBuffer:")
+ : concat_str((char *)old, ",EndOfBuffer:"));
+ free_string_option((char *)old);
+ parse_winhl_opt(wp);
+
// signcolumn: use 'auto'
if (wp->w_p_scl[0] != 'a' || STRLEN(wp->w_p_scl) >= 8) {
free_string_option(wp->w_p_scl);
- wp->w_p_scl = (char_u *)xstrdup("auto");
+ wp->w_p_scl = xstrdup("auto");
}
// foldcolumn: use '0'
if (wp->w_p_fdc[0] != '0') {
free_string_option(wp->w_p_fdc);
- wp->w_p_fdc = (char_u *)xstrdup("0");
+ wp->w_p_fdc = xstrdup("0");
}
// colorcolumn: cleared
if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) {
free_string_option(wp->w_p_cc);
- wp->w_p_cc = (char_u *)xstrdup("");
+ wp->w_p_cc = xstrdup("");
}
}
@@ -789,13 +796,13 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
wp->w_width = MIN(wp->w_width, Columns - win_border_width(wp));
}
- win_set_inner_size(wp);
- must_redraw = MAX(must_redraw, VALID);
+ win_set_inner_size(wp, true);
+ must_redraw = MAX(must_redraw, UPD_VALID);
wp->w_pos_changed = true;
if (change_external || change_border) {
wp->w_hl_needs_update = true;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
// compute initial position
@@ -832,7 +839,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig)
// changing border style while keeping border only requires redrawing border
if (fconfig.border) {
wp->w_redr_border = true;
- redraw_later(wp, VALID);
+ redraw_later(wp, UPD_VALID);
}
}
@@ -921,7 +928,7 @@ void ui_ext_win_position(win_T *wp)
wp->w_grid_alloc.focusable = wp->w_float_config.focusable;
if (!valid) {
wp->w_grid_alloc.valid = false;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
}
} else {
@@ -1270,7 +1277,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
wp->w_floating = false;
// non-floating window doesn't store float config or have a border.
wp->w_float_config = FLOAT_CONFIG_INIT;
- memset(wp->w_border_adj, 0, sizeof(wp->w_border_adj));
+ CLEAR_FIELD(wp->w_border_adj);
}
/*
@@ -1292,9 +1299,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
} else {
curfrp = oldwin->w_frame;
if (flags & WSP_BELOW) {
- before = FALSE;
+ before = false;
} else if (flags & WSP_ABOVE) {
- before = TRUE;
+ before = true;
} else if (flags & WSP_VERT) {
before = !p_spr;
} else {
@@ -1467,8 +1474,8 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir)
// Both windows need redrawing. Update all status lines, in case they
// show something related to the window count or position.
- redraw_later(wp, NOT_VALID);
- redraw_later(oldwin, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
+ redraw_later(oldwin, UPD_NOT_VALID);
status_redraw_all();
if (need_status) {
@@ -1563,10 +1570,10 @@ static void win_init(win_T *newp, win_T *oldp, int flags)
taggy_T *tag = &newp->w_tagstack[i];
*tag = oldp->w_tagstack[i];
if (tag->tagname != NULL) {
- tag->tagname = vim_strsave(tag->tagname);
+ tag->tagname = xstrdup(tag->tagname);
}
if (tag->user_data != NULL) {
- tag->user_data = vim_strsave(tag->user_data);
+ tag->user_data = xstrdup(tag->user_data);
}
}
newp->w_tagstackidx = oldp->w_tagstackidx;
@@ -1590,7 +1597,7 @@ static void win_init_some(win_T *newp, win_T *oldp)
{
// Use the same argument list.
newp->w_alist = oldp->w_alist;
- ++newp->w_alist->al_refcount;
+ newp->w_alist->al_refcount++;
newp->w_arg_idx = oldp->w_arg_idx;
// copy options from existing window
@@ -1670,7 +1677,7 @@ int win_count(void)
int count = 0;
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- ++count;
+ count++;
}
return count;
}
@@ -1716,7 +1723,7 @@ int make_windows(int count, bool vertical)
block_autocmds();
// todo is number of windows left to create
- for (todo = count - 1; todo > 0; --todo) {
+ for (todo = count - 1; todo > 0; todo--) {
if (vertical) {
if (win_split(curwin->w_width - (curwin->w_width - todo)
/ (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL) {
@@ -1830,8 +1837,8 @@ static void win_exchange(long Prenum)
}
win_enter(wp, true);
- redraw_later(curwin, NOT_VALID);
- redraw_later(wp, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
// rotate windows: if upwards true the second window becomes the first one
@@ -1915,7 +1922,7 @@ static void win_rotate(bool upwards, int count)
wp1->w_pos_changed = true;
wp2->w_pos_changed = true;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
/*
@@ -2027,7 +2034,7 @@ void win_move_after(win_T *win1, win_T *win2)
frame_append(win2->w_frame, win1->w_frame);
(void)win_comp_pos(); // recompute w_winrow for all windows
- redraw_later(curwin, NOT_VALID);
+ redraw_later(curwin, UPD_NOT_VALID);
}
win_enter(win1, false);
@@ -2075,7 +2082,7 @@ static int get_maximum_wincount(frame_T *fr, int height)
void win_equal(win_T *next_curwin, bool current, int dir)
{
if (dir == 0) {
- dir = *p_ead;
+ dir = (unsigned char)(*p_ead);
}
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
topframe, dir, 0, tabline_height(),
@@ -2118,7 +2125,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
frame_new_height(topfr, height, false, false);
topfr->fr_win->w_wincol = col;
frame_new_width(topfr, width, false, false);
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
} else if (topfr->fr_layout == FR_ROW) {
topfr->fr_width = width;
@@ -2193,7 +2200,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
if (has_next_curwin) {
- --totwincount; // don't count curwin
+ totwincount--; // don't count curwin
}
}
@@ -2323,7 +2330,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
}
if (has_next_curwin) {
- --totwincount; // don't count curwin
+ totwincount--; // don't count curwin
}
}
@@ -2429,7 +2436,7 @@ void entering_window(win_T *const win)
void win_init_empty(win_T *wp)
{
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
wp->w_lines_valid = 0;
wp->w_cursor.lnum = 1;
wp->w_curswant = wp->w_cursor.col = 0;
@@ -2459,7 +2466,7 @@ void close_windows(buf_T *buf, bool keep_curwin)
tabpage_T *tp, *nexttp;
int h = tabline_height();
- ++RedrawingDisabled;
+ RedrawingDisabled++;
// Start from lastwin to close floating windows with the same buffer first.
// When the autocommand window is involved win_close() may need to print an error message.
@@ -2496,7 +2503,7 @@ void close_windows(buf_T *buf, bool keep_curwin)
}
}
- --RedrawingDisabled;
+ RedrawingDisabled--;
redraw_tabline = true;
if (h != tabline_height()) {
@@ -2928,7 +2935,7 @@ int win_close(win_T *win, bool free_buf, bool force)
}
curwin->w_pos_changed = true;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
return OK;
}
@@ -3089,7 +3096,7 @@ void win_free_all(void)
cmdwin_type = 0;
while (first_tabpage->tp_next != NULL) {
- tabpage_close(TRUE);
+ tabpage_close(true);
}
while (lastwin != NULL && lastwin->w_floating) {
@@ -3688,7 +3695,7 @@ static void frame_add_vsep(const frame_T *frp)
wp = frp->fr_win;
if (wp->w_vsep_width == 0) {
if (wp->w_width > 0) { // don't make it negative
- --wp->w_width;
+ wp->w_width--;
}
wp->w_vsep_width = 1;
}
@@ -3820,7 +3827,7 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin)
m = (int)p_wmw + topfrp->fr_win->w_vsep_width;
// Current window is minimal one column wide
if (p_wmw == 0 && topfrp->fr_win == curwin && next_curwin == NULL) {
- ++m;
+ m++;
}
}
} else if (topfrp->fr_layout == FR_COL) {
@@ -4018,7 +4025,7 @@ static tabpage_T *alloc_tabpage(void)
// Init t: variables.
tp->tp_vars = tv_dict_alloc();
init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE);
- tp->tp_diff_invalid = TRUE;
+ tp->tp_diff_invalid = true;
tp->tp_ch_used = p_ch;
return tp;
@@ -4030,7 +4037,7 @@ void free_tabpage(tabpage_T *tp)
pmap_del(handle_T)(&tabpage_handles, tp->handle);
diff_clear(tp);
- for (idx = 0; idx < SNAP_COUNT; ++idx) {
+ for (idx = 0; idx < SNAP_COUNT; idx++) {
clear_snapshot(tp, idx);
}
vars_clear(&tp->tp_vars->dv_hashtab); // free all t: variables
@@ -4093,7 +4100,7 @@ int win_new_tabpage(int after, char_u *filename)
n = 2;
for (tp = first_tabpage; tp->tp_next != NULL
&& n < after; tp = tp->tp_next) {
- ++n;
+ n++;
}
}
newtp->tp_next = tp->tp_next;
@@ -4108,7 +4115,7 @@ int win_new_tabpage(int after, char_u *filename)
newtp->tp_topframe = topframe;
last_status(false);
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
tabpage_check_windows(old_curtab);
@@ -4166,7 +4173,7 @@ int make_tabpages(int maxcount)
*/
block_autocmds();
- for (todo = count - 1; todo > 0; --todo) {
+ for (todo = count - 1; todo > 0; todo--) {
if (win_new_tabpage(0, NULL) == FAIL) {
break;
}
@@ -4239,7 +4246,7 @@ tabpage_T *find_tabpage(int n)
int i = 1;
for (tp = first_tabpage; tp != NULL && i != n; tp = tp->tp_next) {
- ++i;
+ i++;
}
return tp;
}
@@ -4254,7 +4261,7 @@ int tabpage_index(tabpage_T *ftp)
tabpage_T *tp;
for (tp = first_tabpage; tp != NULL && tp != ftp; tp = tp->tp_next) {
- ++i;
+ i++;
}
return i;
}
@@ -4336,6 +4343,11 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
// Use the stored value of p_ch, so that it can be different for each tab page.
if (p_ch != curtab->tp_ch_used) {
clear_cmdline = true;
+ if (msg_grid.chars && p_ch < curtab->tp_ch_used) {
+ // TODO(bfredl): a bit expensive, should be enough to invalidate the
+ // region between the old and the new p_ch.
+ grid_invalidate(&msg_grid);
+ }
}
p_ch = curtab->tp_ch_used;
@@ -4366,7 +4378,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
}
}
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
/// tells external UI that windows and inline floats in old_curtab are invisible
@@ -4433,7 +4445,7 @@ void goto_tabpage(int n)
// "gT": go to previous tab page, wrap around end. "N gT" repeats
// this N times.
ttp = curtab;
- for (i = n; i < 0; ++i) {
+ for (i = n; i < 0; i++) {
for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL;
tp = tp->tp_next) {}
ttp = tp;
@@ -4516,7 +4528,7 @@ void tabpage_move(int nr)
}
for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) {
- ++n;
+ n++;
}
if (tp == curtab || (nr > 0 && tp->tp_next != NULL
@@ -4829,7 +4841,7 @@ static void win_enter_ext(win_T *const wp, const int flags)
}
if (!curwin_invalid) {
prevwin = curwin; // remember for CTRL-W p
- curwin->w_redr_status = TRUE;
+ curwin->w_redr_status = true;
}
curwin = wp;
curbuf = wp->w_buffer;
@@ -4860,13 +4872,20 @@ static void win_enter_ext(win_T *const wp, const int flags)
curwin->w_redr_status = true;
redraw_tabline = true;
if (restart_edit) {
- redraw_later(curwin, VALID); // causes status line redraw
+ redraw_later(curwin, UPD_VALID); // causes status line redraw
}
- if (HL_ATTR(HLF_INACTIVE)
- || (prevwin && prevwin->w_hl_ids[HLF_INACTIVE])
- || curwin->w_hl_ids[HLF_INACTIVE]) {
- redraw_all_later(NOT_VALID);
+ // change background color according to NormalNC,
+ // but only if actually defined (otherwise no extra redraw)
+ if (curwin->w_hl_attr_normal != curwin->w_hl_attr_normalnc) {
+ // TODO(bfredl): eventually we should be smart enough
+ // to only recompose the window, not redraw it.
+ redraw_later(curwin, UPD_NOT_VALID);
+ }
+ if (prevwin) {
+ if (prevwin->w_hl_attr_normal != prevwin->w_hl_attr_normalnc) {
+ redraw_later(prevwin, UPD_NOT_VALID);
+ }
}
// set window height to desired minimal value
@@ -5035,6 +5054,8 @@ static win_T *win_alloc(win_T *after, bool hidden)
new_wp->w_float_config = FLOAT_CONFIG_INIT;
new_wp->w_viewport_invalid = true;
+ new_wp->w_ns_hl = -1;
+
// use global option for global-local options
new_wp->w_p_so = -1;
new_wp->w_p_siso = -1;
@@ -5175,7 +5196,7 @@ void win_free_grid(win_T *wp, bool reinit)
grid_free(&wp->w_grid_alloc);
if (reinit) {
// if a float is turned into a split, the grid data structure will be reused
- memset(&wp->w_grid_alloc, 0, sizeof(wp->w_grid_alloc));
+ CLEAR_FIELD(wp->w_grid_alloc);
}
}
@@ -5346,6 +5367,7 @@ void may_trigger_winscrolled(void)
win_T *wp = curwin;
if (wp->w_last_topline != wp->w_topline
|| wp->w_last_leftcol != wp->w_leftcol
+ || wp->w_last_skipcol != wp->w_skipcol
|| wp->w_last_width != wp->w_width
|| wp->w_last_height != wp->w_height) {
char winid[NUMBUFLEN];
@@ -5359,6 +5381,7 @@ void may_trigger_winscrolled(void)
if (win_valid_any_tab(wp)) {
wp->w_last_topline = wp->w_topline;
wp->w_last_leftcol = wp->w_leftcol;
+ wp->w_last_skipcol = wp->w_skipcol;
wp->w_last_width = wp->w_width;
wp->w_last_height = wp->w_height;
}
@@ -5454,7 +5477,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col)
// position changed, redraw
wp->w_winrow = *row;
wp->w_wincol = *col;
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
wp->w_redr_status = true;
wp->w_pos_changed = true;
}
@@ -5497,7 +5520,7 @@ void win_setheight_win(int height, win_T *win)
if (win->w_floating) {
win->w_float_config.height = height;
win_config_float(win, win->w_float_config);
- redraw_later(win, NOT_VALID);
+ redraw_later(win, UPD_NOT_VALID);
} else {
frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
@@ -5517,7 +5540,7 @@ void win_setheight_win(int height, win_T *win)
curtab->tp_ch_used = p_ch;
msg_row = row;
msg_col = 0;
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
redraw_cmdline = true;
}
}
@@ -5551,7 +5574,7 @@ static void frame_setheight(frame_T *curfrp, int height)
}
if (curfrp->fr_parent == NULL) {
- // topframe: can only change the command line
+ // topframe: can only change the command line height
if (height > ROWS_AVAIL) {
// If height is greater than the available space, try to create space for
// the frame by reducing 'cmdheight' if possible, while making sure
@@ -5580,7 +5603,7 @@ static void frame_setheight(frame_T *curfrp, int height)
* 2: compute the room available and adjust the height to it.
* Try not to reduce the height of a window with 'winfixheight' set.
*/
- for (run = 1; run <= 2; ++run) {
+ for (run = 1; run <= 2; run++) {
room = 0;
room_reserved = 0;
FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) {
@@ -5653,7 +5676,7 @@ static void frame_setheight(frame_T *curfrp, int height)
* that is not enough, takes lines from frames above the current
* frame.
*/
- for (run = 0; run < 2; ++run) {
+ for (run = 0; run < 2; run++) {
if (run == 0) {
frp = curfrp->fr_next; // 1st run: start with next window
} else {
@@ -5719,13 +5742,13 @@ void win_setwidth_win(int width, win_T *wp)
if (wp->w_floating) {
wp->w_float_config.width = width;
win_config_float(wp, wp->w_float_config);
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
} else {
frame_setwidth(wp->w_frame, width + wp->w_vsep_width);
// recompute the window positions
(void)win_comp_pos();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
}
@@ -5772,7 +5795,7 @@ static void frame_setwidth(frame_T *curfrp, int width)
* containing frame.
* 2: compute the room available and adjust the width to it.
*/
- for (run = 1; run <= 2; ++run) {
+ for (run = 1; run <= 2; run++) {
room = 0;
room_reserved = 0;
FOR_ALL_FRAMES(frp, curfrp->fr_parent->fr_child) {
@@ -5825,7 +5848,7 @@ static void frame_setwidth(frame_T *curfrp, int width)
* that is not enough, takes lines from frames left of the current
* frame.
*/
- for (run = 0; run < 2; ++run) {
+ for (run = 0; run < 2; run++) {
if (run == 0) {
frp = curfrp->fr_next; // 1st run: start with next window
} else {
@@ -5914,6 +5937,13 @@ void win_drag_status_line(win_T *dragwin, int offset)
int row;
bool up; // if true, drag status line up, otherwise down
int n;
+ static bool p_ch_was_zero = false;
+
+ // If the user explicitly set 'cmdheight' to zero, then allow for dragging
+ // the status line making it zero again.
+ if (p_ch == 0) {
+ p_ch_was_zero = true;
+ }
fr = dragwin->w_frame;
curfr = fr;
@@ -5964,6 +5994,8 @@ void win_drag_status_line(win_T *dragwin, int offset)
room = Rows - cmdline_row;
if (curfr->fr_next != NULL) {
room -= (int)p_ch + global_stl_height();
+ } else if (!p_ch_was_zero) {
+ room--;
}
if (room < 0) {
room = 0;
@@ -6019,9 +6051,9 @@ void win_drag_status_line(win_T *dragwin, int offset)
clear_cmdline = true;
}
cmdline_row = row;
- p_ch = MAX(Rows - cmdline_row, 0);
+ p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1);
curtab->tp_ch_used = p_ch;
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
showmode();
}
@@ -6128,7 +6160,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
}
}
(void)win_comp_pos();
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
#define FRACTION_MULT 16384L
@@ -6162,7 +6194,7 @@ void win_new_height(win_T *wp, int height)
wp->w_height = height;
wp->w_pos_changed = true;
- win_set_inner_size(wp);
+ win_set_inner_size(wp, true);
}
void scroll_to_fraction(win_T *wp, int prev_height)
@@ -6225,7 +6257,7 @@ void scroll_to_fraction(win_T *wp, int prev_height)
if (lnum == 1) {
// first line in buffer is folded
line_size = 1;
- --sline;
+ sline--;
break;
}
lnum--;
@@ -6266,12 +6298,12 @@ void scroll_to_fraction(win_T *wp, int prev_height)
}
win_comp_scroll(wp);
- redraw_later(wp, SOME_VALID);
+ redraw_later(wp, UPD_SOME_VALID);
wp->w_redr_status = true;
invalidate_botline_win(wp);
}
-void win_set_inner_size(win_T *wp)
+void win_set_inner_size(win_T *wp, bool valid_cursor)
{
int width = wp->w_width_request;
if (width == 0) {
@@ -6285,7 +6317,7 @@ void win_set_inner_size(win_T *wp)
}
if (height != prev_height) {
- if (height > 0) {
+ if (height > 0 && valid_cursor) {
if (wp == curwin) {
// w_wrow needs to be valid. When setting 'laststatus' this may
// call win_new_height() recursively.
@@ -6304,22 +6336,24 @@ void win_set_inner_size(win_T *wp)
// There is no point in adjusting the scroll position when exiting. Some
// values might be invalid.
// Skip scroll_to_fraction() when 'cmdheight' was set to one from zero.
- if (!exiting && !made_cmdheight_nonzero) {
+ if (!exiting && !made_cmdheight_nonzero && valid_cursor) {
scroll_to_fraction(wp, prev_height);
}
- redraw_later(wp, NOT_VALID); // SOME_VALID??
+ redraw_later(wp, UPD_NOT_VALID); // UPD_SOME_VALID??
}
if (width != wp->w_width_inner) {
wp->w_width_inner = width;
wp->w_lines_valid = 0;
- changed_line_abv_curs_win(wp);
- invalidate_botline_win(wp);
- if (wp == curwin) {
- update_topline(wp);
- curs_columns(wp, true); // validate w_wrow
+ if (valid_cursor) {
+ changed_line_abv_curs_win(wp);
+ invalidate_botline_win(wp);
+ if (wp == curwin) {
+ update_topline(wp);
+ curs_columns(wp, true); // validate w_wrow
+ }
}
- redraw_later(wp, NOT_VALID);
+ redraw_later(wp, UPD_NOT_VALID);
}
if (wp->w_buffer->terminal) {
@@ -6346,7 +6380,7 @@ static int win_border_width(win_T *wp)
void win_new_width(win_T *wp, int width)
{
wp->w_width = width;
- win_set_inner_size(wp);
+ win_set_inner_size(wp, true);
wp->w_redr_status = true;
wp->w_pos_changed = true;
@@ -6381,6 +6415,19 @@ void command_height(void)
// p_ch was changed in another tab page.
curtab->tp_ch_used = p_ch;
+ // If the space for the command line is already more than 'cmdheight' there
+ // is nothing to do (window size must have decreased).
+ if (p_ch > old_p_ch && cmdline_row <= Rows - p_ch) {
+ return;
+ }
+
+ // If cmdline_row is smaller than what it is supposed to be for 'cmdheight'
+ // then set old_p_ch to what it would be, so that the windows get resized
+ // properly for the new value.
+ if (cmdline_row < Rows - p_ch) {
+ old_p_ch = Rows - cmdline_row;
+ }
+
// Find bottom frame with width of screen.
frp = lastwin_nofloating()->w_frame;
while (frp->fr_width != Columns && frp->fr_parent != NULL) {
@@ -6465,17 +6512,17 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC;
if (VIsual_active) {
size_t len;
- char_u *ptr;
+ char *ptr;
if (get_visual_text(NULL, &ptr, &len) == FAIL) {
return NULL;
}
// Only recognize ":123" here
if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) {
- char *p = (char *)ptr + len + 1;
+ char *p = ptr + len + 1;
*file_lnum = (linenr_T)getdigits_long(&p, false, 0);
}
- return find_file_name_in_path(ptr, len, options, count, (char_u *)curbuf->b_ffname);
+ return find_file_name_in_path((char_u *)ptr, len, options, count, (char_u *)curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
}
@@ -6542,7 +6589,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
/*
* Search forward for the last char of the file name.
- * Also allow "://" when ':' is not in 'isfname'.
+ * Also allow ":/" when ':' is not in 'isfname'.
*/
len = 0;
while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ')
@@ -6561,7 +6608,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
if (ptr[len] == '\\' && ptr[len + 1] == ' ') {
// Skip over the "\" in "\ ".
- ++len;
+ len++;
}
len += (size_t)(utfc_ptr2len(ptr + len));
}
@@ -6572,7 +6619,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u
*/
if (len > 2 && vim_strchr(".,:;!", ptr[len - 1]) != NULL
&& ptr[len - 2] != '.') {
- --len;
+ len--;
}
if (file_lnum != NULL) {
@@ -6726,7 +6773,7 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
wp->w_hsep_height = 0;
comp_col();
}
- redraw_all_later(SOME_VALID);
+ redraw_all_later(UPD_SOME_VALID);
} else {
// For a column or row frame, recursively call this function for all child frames
FOR_ALL_FRAMES(fp, fr->fr_child) {
@@ -6738,9 +6785,11 @@ static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global)
/// Add or remove window bar from window "wp".
///
/// @param make_room Whether to resize frames to make room for winbar.
+/// @param valid_cursor Whether the cursor is valid and should be used while
+/// resizing.
///
/// @return Success status.
-int set_winbar_win(win_T *wp, bool make_room)
+int set_winbar_win(win_T *wp, bool make_room, bool valid_cursor)
{
// Require the local value to be set in order to show winbar on a floating window.
int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0)
@@ -6756,7 +6805,7 @@ int set_winbar_win(win_T *wp, bool make_room)
}
}
wp->w_winbar_height = winbar_height;
- win_set_inner_size(wp);
+ win_set_inner_size(wp, valid_cursor);
wp->w_redr_status = wp->w_redr_status || winbar_height;
if (winbar_height == 0) {
@@ -6777,7 +6826,7 @@ int set_winbar_win(win_T *wp, bool make_room)
void set_winbar(bool make_room)
{
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (set_winbar_win(wp, make_room) == FAIL) {
+ if (set_winbar_win(wp, make_room, true) == FAIL) {
break;
}
}
@@ -7024,7 +7073,7 @@ void restore_snapshot(int idx, int close_curwin)
if (wp != NULL && close_curwin) {
win_goto(wp);
}
- redraw_all_later(NOT_VALID);
+ redraw_all_later(UPD_NOT_VALID);
}
clear_snapshot(curtab, idx);
}
@@ -7095,7 +7144,7 @@ int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_displa
// As switch_win() but without blocking autocommands.
int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display)
{
- memset(switchwin, 0, sizeof(switchwin_T));
+ CLEAR_POINTER(switchwin);
switchwin->sw_curwin = curwin;
if (win == curwin) {
switchwin->sw_same_win = true;
@@ -7228,6 +7277,88 @@ static bool frame_check_width(const frame_T *topfrp, int width)
return true;
}
+/// Simple int comparison function for use with qsort()
+static int int_cmp(const void *a, const void *b)
+{
+ return *(const int *)a - *(const int *)b;
+}
+
+/// Handle setting 'colorcolumn' or 'textwidth' in window "wp".
+///
+/// @return error message, NULL if it's OK.
+char *check_colorcolumn(win_T *wp)
+{
+ char *s;
+ int col;
+ unsigned int count = 0;
+ int color_cols[256];
+ int j = 0;
+
+ if (wp->w_buffer == NULL) {
+ return NULL; // buffer was closed
+ }
+
+ for (s = wp->w_p_cc; *s != NUL && count < 255;) {
+ if (*s == '-' || *s == '+') {
+ // -N and +N: add to 'textwidth'
+ col = (*s == '-') ? -1 : 1;
+ s++;
+ if (!ascii_isdigit(*s)) {
+ return e_invarg;
+ }
+ col = col * getdigits_int(&s, true, 0);
+ if (wp->w_buffer->b_p_tw == 0) {
+ goto skip; // 'textwidth' not set, skip this item
+ }
+ assert((col >= 0
+ && wp->w_buffer->b_p_tw <= INT_MAX - col
+ && wp->w_buffer->b_p_tw + col >= INT_MIN)
+ || (col < 0
+ && wp->w_buffer->b_p_tw >= INT_MIN - col
+ && wp->w_buffer->b_p_tw + col <= INT_MAX));
+ col += (int)wp->w_buffer->b_p_tw;
+ if (col < 0) {
+ goto skip;
+ }
+ } else if (ascii_isdigit(*s)) {
+ col = getdigits_int(&s, true, 0);
+ } else {
+ return e_invarg;
+ }
+ color_cols[count++] = col - 1; // 1-based to 0-based
+skip:
+ if (*s == NUL) {
+ break;
+ }
+ if (*s != ',') {
+ return e_invarg;
+ }
+ if (*++s == NUL) {
+ return e_invarg; // illegal trailing comma as in "set cc=80,"
+ }
+ }
+
+ xfree(wp->w_p_cc_cols);
+ if (count == 0) {
+ wp->w_p_cc_cols = NULL;
+ } else {
+ wp->w_p_cc_cols = xmalloc(sizeof(int) * (count + 1));
+ // sort the columns for faster usage on screen redraw inside
+ // win_line()
+ qsort(color_cols, count, sizeof(int), int_cmp);
+
+ for (unsigned int i = 0; i < count; i++) {
+ // skip duplicates
+ if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) {
+ wp->w_p_cc_cols[j++] = color_cols[i];
+ }
+ }
+ wp->w_p_cc_cols[j] = -1; // end marker
+ }
+
+ return NULL; // no error
+}
+
int win_getid(typval_T *argvars)
{
if (argvars[0].v_type == VAR_UNKNOWN) {
@@ -7265,19 +7396,6 @@ int win_getid(typval_T *argvars)
return 0;
}
-int win_gotoid(typval_T *argvars)
-{
- int id = (int)tv_get_number(&argvars[0]);
-
- FOR_ALL_TAB_WINDOWS(tp, wp) {
- if (wp->handle == id) {
- goto_tabpage_win(tp, wp);
- return 1;
- }
- }
- return 0;
-}
-
void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
{
*tabnr = 0;