aboutsummaryrefslogtreecommitdiff
path: root/src/nvim
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim')
-rw-r--r--src/nvim/CMakeLists.txt22
-rw-r--r--src/nvim/api/autocmd.c104
-rw-r--r--src/nvim/api/buffer.c351
-rw-r--r--src/nvim/api/command.c8
-rw-r--r--src/nvim/api/deprecated.c223
-rw-r--r--src/nvim/api/extmark.c157
-rw-r--r--src/nvim/api/extmark.h1
-rw-r--r--src/nvim/api/keysets_defs.h114
-rw-r--r--src/nvim/api/options.c60
-rw-r--r--src/nvim/api/private/converter.c3
-rw-r--r--src/nvim/api/private/defs.h2
-rw-r--r--src/nvim/api/private/helpers.c89
-rw-r--r--src/nvim/api/private/helpers.h16
-rw-r--r--src/nvim/api/tabpage.c13
-rw-r--r--src/nvim/api/ui.c4
-rw-r--r--src/nvim/api/ui.h1
-rw-r--r--src/nvim/api/ui_events.in.h6
-rw-r--r--src/nvim/api/vim.c416
-rw-r--r--src/nvim/api/vimscript.c70
-rw-r--r--src/nvim/api/win_config.c35
-rw-r--r--src/nvim/api/window.c77
-rw-r--r--src/nvim/arglist.c5
-rw-r--r--src/nvim/ascii_defs.h9
-rw-r--r--src/nvim/autocmd.c153
-rw-r--r--src/nvim/autocmd_defs.h4
-rw-r--r--src/nvim/buffer.c233
-rw-r--r--src/nvim/buffer_defs.h14
-rw-r--r--src/nvim/buffer_updates.c3
-rw-r--r--src/nvim/bufwrite.c15
-rw-r--r--src/nvim/change.c3
-rw-r--r--src/nvim/channel.c3
-rw-r--r--src/nvim/charset.c8
-rw-r--r--src/nvim/cmdexpand.c13
-rw-r--r--src/nvim/cmdhist.c1
-rw-r--r--src/nvim/cmdhist.h4
-rw-r--r--src/nvim/context.c1
-rw-r--r--src/nvim/context.h2
-rw-r--r--src/nvim/cursor.c9
-rw-r--r--src/nvim/cursor_shape.c3
-rw-r--r--src/nvim/cursor_shape.h3
-rw-r--r--src/nvim/debugger.c9
-rw-r--r--src/nvim/decoration.c342
-rw-r--r--src/nvim/decoration.h47
-rw-r--r--src/nvim/decoration_defs.h5
-rw-r--r--src/nvim/decoration_provider.c12
-rw-r--r--src/nvim/diff.c48
-rw-r--r--src/nvim/digraph.c30
-rw-r--r--src/nvim/drawline.c240
-rw-r--r--src/nvim/drawscreen.c128
-rw-r--r--src/nvim/drawscreen.h1
-rw-r--r--src/nvim/edit.c65
-rw-r--r--src/nvim/errors.h1
-rw-r--r--src/nvim/eval.c27
-rw-r--r--src/nvim/eval.h1
-rw-r--r--src/nvim/eval.lua237
-rw-r--r--src/nvim/eval/buffer.c6
-rw-r--r--src/nvim/eval/decode.c3
-rw-r--r--src/nvim/eval/decode.h2
-rw-r--r--src/nvim/eval/deprecated.c158
-rw-r--r--src/nvim/eval/deprecated.h8
-rw-r--r--src/nvim/eval/executor.c1
-rw-r--r--src/nvim/eval/funcs.c397
-rw-r--r--src/nvim/eval/typval.c38
-rw-r--r--src/nvim/eval/typval_defs.h7
-rw-r--r--src/nvim/eval/userfunc.c793
-rw-r--r--src/nvim/eval/vars.c35
-rw-r--r--src/nvim/eval/window.c2
-rw-r--r--src/nvim/event/proc.c3
-rw-r--r--src/nvim/event/proc.h5
-rw-r--r--src/nvim/event/rstream.c6
-rw-r--r--src/nvim/event/socket.c2
-rw-r--r--src/nvim/event/stream.c1
-rw-r--r--src/nvim/ex_cmds.c47
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_cmds2.c2
-rw-r--r--src/nvim/ex_cmds_defs.h29
-rw-r--r--src/nvim/ex_docmd.c63
-rw-r--r--src/nvim/ex_eval.c7
-rw-r--r--src/nvim/ex_eval_defs.h2
-rw-r--r--src/nvim/ex_getln.c272
-rw-r--r--src/nvim/ex_getln_defs.h2
-rw-r--r--src/nvim/ex_session.c87
-rw-r--r--src/nvim/extmark.c63
-rw-r--r--src/nvim/file_search.c4
-rw-r--r--src/nvim/fileio.c8
-rw-r--r--src/nvim/fold.c2
-rw-r--r--src/nvim/garray.c3
-rw-r--r--src/nvim/generators/c_grammar.lua333
-rw-r--r--src/nvim/generators/gen_api_dispatch.lua26
-rw-r--r--src/nvim/generators/gen_api_ui_events.lua2
-rw-r--r--src/nvim/generators/gen_declarations.lua468
-rw-r--r--src/nvim/generators/gen_eval.lua1
-rw-r--r--src/nvim/generators/gen_options.lua589
-rw-r--r--src/nvim/generators/gen_vimvim.lua8
-rw-r--r--src/nvim/generators/hashy.lua2
-rw-r--r--src/nvim/getchar.c106
-rw-r--r--src/nvim/getchar.h1
-rw-r--r--src/nvim/globals.h3
-rw-r--r--src/nvim/grid.c9
-rw-r--r--src/nvim/hashtab.c1
-rw-r--r--src/nvim/highlight.c7
-rw-r--r--src/nvim/highlight.h1
-rw-r--r--src/nvim/highlight_defs.h1
-rw-r--r--src/nvim/highlight_group.c26
-rw-r--r--src/nvim/indent.c7
-rw-r--r--src/nvim/indent_c.c81
-rw-r--r--src/nvim/input.c147
-rw-r--r--src/nvim/insexpand.c721
-rw-r--r--src/nvim/keycodes.c2
-rw-r--r--src/nvim/linematch.c16
-rw-r--r--src/nvim/linematch.h2
-rw-r--r--src/nvim/lua/api_wrappers.c30
-rw-r--r--src/nvim/lua/converter.c1
-rw-r--r--src/nvim/lua/executor.c10
-rw-r--r--src/nvim/lua/executor.h2
-rw-r--r--src/nvim/lua/secure.c2
-rw-r--r--src/nvim/lua/spell.c2
-rw-r--r--src/nvim/lua/stdlib.c51
-rw-r--r--src/nvim/lua/treesitter.c37
-rw-r--r--src/nvim/lua/xdiff.c1
-rw-r--r--src/nvim/main.c31
-rw-r--r--src/nvim/main.h1
-rw-r--r--src/nvim/mapping.c8
-rw-r--r--src/nvim/mark.c5
-rw-r--r--src/nvim/marktree.c3
-rw-r--r--src/nvim/marktree.h1
-rw-r--r--src/nvim/math.c6
-rw-r--r--src/nvim/mbyte.c12
-rw-r--r--src/nvim/mbyte.h1
-rw-r--r--src/nvim/mbyte_defs.h5
-rw-r--r--src/nvim/memline.c6
-rw-r--r--src/nvim/memory.c1
-rw-r--r--src/nvim/memory.h1
-rw-r--r--src/nvim/menu.c1
-rw-r--r--src/nvim/message.c478
-rw-r--r--src/nvim/mouse.c190
-rw-r--r--src/nvim/move.c23
-rw-r--r--src/nvim/msgpack_rpc/channel.c10
-rw-r--r--src/nvim/msgpack_rpc/packer.c9
-rw-r--r--src/nvim/msgpack_rpc/packer.h6
-rw-r--r--src/nvim/msgpack_rpc/server.c1
-rw-r--r--src/nvim/msgpack_rpc/unpacker.c1
-rw-r--r--src/nvim/msgpack_rpc/unpacker.h2
-rw-r--r--src/nvim/normal.c78
-rw-r--r--src/nvim/ops.c188
-rw-r--r--src/nvim/ops.h2
-rw-r--r--src/nvim/option.c274
-rw-r--r--src/nvim/option.h4
-rw-r--r--src/nvim/option_defs.h11
-rw-r--r--src/nvim/option_vars.h190
-rw-r--r--src/nvim/options.lua1202
-rw-r--r--src/nvim/optionstr.c1021
-rw-r--r--src/nvim/os/env.c3
-rw-r--r--src/nvim/os/fileio.c7
-rw-r--r--src/nvim/os/fs.c6
-rw-r--r--src/nvim/os/input.c4
-rw-r--r--src/nvim/os/nvim.rc2
-rw-r--r--src/nvim/os/pty_proc_unix.c1
-rw-r--r--src/nvim/os/shell.c22
-rw-r--r--src/nvim/os/signal.c3
-rw-r--r--src/nvim/path.c18
-rw-r--r--src/nvim/po/af.po4
-rw-r--r--src/nvim/po/ca.po4
-rw-r--r--src/nvim/po/cs.cp1250.po4
-rw-r--r--src/nvim/po/cs.po4
-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.po4
-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.po4
-rw-r--r--src/nvim/po/ja.euc-jp.po4
-rw-r--r--src/nvim/po/ja.po4
-rw-r--r--src/nvim/po/ko.UTF-8.po4
-rw-r--r--src/nvim/po/nb.po4
-rw-r--r--src/nvim/po/nl.po4
-rw-r--r--src/nvim/po/no.po4
-rw-r--r--src/nvim/po/pl.UTF-8.po4
-rw-r--r--src/nvim/po/pt_BR.po4
-rw-r--r--src/nvim/po/ru.po4
-rw-r--r--src/nvim/po/sk.cp1250.po4
-rw-r--r--src/nvim/po/sk.po4
-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.po6622
-rw-r--r--src/nvim/po/zh_CN.UTF-8.po4
-rw-r--r--src/nvim/po/zh_TW.UTF-8.po4
-rw-r--r--src/nvim/popupmenu.c368
-rw-r--r--src/nvim/profile.c1
-rw-r--r--src/nvim/quickfix.c9
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/runtime.c151
-rw-r--r--src/nvim/search.c79
-rw-r--r--src/nvim/search.h3
-rw-r--r--src/nvim/shada.c9
-rw-r--r--src/nvim/shada.h2
-rw-r--r--src/nvim/sign.c3
-rw-r--r--src/nvim/spell.c6
-rw-r--r--src/nvim/spellsuggest.c17
-rw-r--r--src/nvim/state.c16
-rw-r--r--src/nvim/state.h2
-rw-r--r--src/nvim/statusline.c172
-rw-r--r--src/nvim/statusline_defs.h66
-rw-r--r--src/nvim/strings.c18
-rw-r--r--src/nvim/strings.h2
-rw-r--r--src/nvim/syntax.c1
-rw-r--r--src/nvim/tag.c19
-rw-r--r--src/nvim/terminal.c309
-rw-r--r--src/nvim/textformat.c8
-rw-r--r--src/nvim/textobject.c1
-rw-r--r--src/nvim/tui/input.c53
-rw-r--r--src/nvim/tui/input.h2
-rw-r--r--src/nvim/tui/termkey/driver-csi.c57
-rw-r--r--src/nvim/tui/termkey/driver-csi.h2
-rw-r--r--src/nvim/tui/termkey/driver-ti.c6
-rw-r--r--src/nvim/tui/termkey/driver-ti.h2
-rw-r--r--src/nvim/tui/termkey/termkey-internal.h2
-rw-r--r--src/nvim/tui/termkey/termkey.c55
-rw-r--r--src/nvim/tui/termkey/termkey.h6
-rw-r--r--src/nvim/tui/termkey/termkey_defs.h9
-rw-r--r--src/nvim/tui/tui.c63
-rw-r--r--src/nvim/tui/tui_defs.h1
-rw-r--r--src/nvim/ui.c17
-rw-r--r--src/nvim/ui_client.c1
-rw-r--r--src/nvim/ui_compositor.c35
-rw-r--r--src/nvim/undo.c18
-rw-r--r--src/nvim/usercmd.c2
-rw-r--r--src/nvim/version.c3
-rw-r--r--src/nvim/vterm/LICENSE23
-rw-r--r--src/nvim/vterm/README.md1
-rw-r--r--src/nvim/vterm/encoding.c271
-rw-r--r--src/nvim/vterm/encoding.h10
-rw-r--r--src/nvim/vterm/keyboard.c318
-rw-r--r--src/nvim/vterm/keyboard.h10
-rw-r--r--src/nvim/vterm/mouse.c124
-rw-r--r--src/nvim/vterm/mouse.h10
-rw-r--r--src/nvim/vterm/parser.c411
-rw-r--r--src/nvim/vterm/parser.h9
-rw-r--r--src/nvim/vterm/pen.c644
-rw-r--r--src/nvim/vterm/pen.h9
-rw-r--r--src/nvim/vterm/screen.c1115
-rw-r--r--src/nvim/vterm/screen.h9
-rw-r--r--src/nvim/vterm/state.c2467
-rw-r--r--src/nvim/vterm/state.h7
-rw-r--r--src/nvim/vterm/vterm.c335
-rw-r--r--src/nvim/vterm/vterm.h161
-rw-r--r--src/nvim/vterm/vterm_defs.h322
-rw-r--r--src/nvim/vterm/vterm_internal_defs.h292
-rw-r--r--src/nvim/vterm/vterm_keycodes_defs.h58
-rw-r--r--src/nvim/vvars.lua26
-rw-r--r--src/nvim/window.c358
-rw-r--r--src/nvim/window.h3
-rw-r--r--src/nvim/winfloat.c10
258 files changed, 16205 insertions, 11186 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 344b4bef00..6e27e39f9a 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -31,7 +31,7 @@ target_link_libraries(main_lib INTERFACE ${LUV_LIBRARY})
find_package(Iconv REQUIRED)
find_package(Libuv 1.28.0 REQUIRED)
find_package(Lpeg REQUIRED)
-find_package(Treesitter 0.24.0 REQUIRED)
+find_package(Treesitter 0.25.0 REQUIRED)
find_package(Unibilium 2.0 REQUIRED)
find_package(UTF8proc REQUIRED)
@@ -49,7 +49,7 @@ if(ENABLE_LIBINTL)
endif()
if(ENABLE_WASMTIME)
- find_package(Wasmtime 25.0.2 EXACT REQUIRED)
+ find_package(Wasmtime 29.0.1 EXACT REQUIRED)
target_link_libraries(main_lib INTERFACE wasmtime)
target_compile_definitions(nvim_bin PRIVATE HAVE_WASMTIME)
endif()
@@ -120,8 +120,8 @@ elseif(MINGW)
# Use POSIX compatible stdio in Mingw
target_compile_definitions(main_lib INTERFACE __USE_MINGW_ANSI_STDIO)
- # Enable wmain
- target_link_libraries(nvim_bin PRIVATE -municode)
+ # wrapper for nvim.manifest
+ target_sources(main_lib INTERFACE ${CMAKE_CURRENT_LIST_DIR}/os/nvim.rc)
elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(main_lib INTERFACE
-Wno-conversion
@@ -322,6 +322,7 @@ set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h)
set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h)
set(GENERATED_OPTIONS_ENUM ${GENERATED_DIR}/options_enum.generated.h)
set(GENERATED_OPTIONS_MAP ${GENERATED_DIR}/options_map.generated.h)
+set(GENERATED_OPTION_VARS ${GENERATED_DIR}/option_vars.generated.h)
set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h)
set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h)
set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h)
@@ -360,8 +361,8 @@ file(MAKE_DIRECTORY ${TOUCHES_DIR} ${GENERATED_DIR} ${GENERATED_INCLUDES_DIR})
file(GLOB NVIM_SOURCES CONFIGURE_DEPENDS *.c)
file(GLOB NVIM_HEADERS CONFIGURE_DEPENDS *.h)
-file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c ../vterm/*.c)
-file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h ../vterm/*.h)
+file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS ../xdiff/*.c ../mpack/*.c ../cjson/*.c ../klib/*.c)
+file(GLOB EXTERNAL_HEADERS CONFIGURE_DEPENDS ../xdiff/*.h ../mpack/*.h ../cjson/*.h ../klib/*.h)
file(GLOB NLUA0_SOURCES CONFIGURE_DEPENDS ../mpack/*.c)
@@ -390,6 +391,7 @@ foreach(subdir
msgpack_rpc
tui
tui/termkey
+ vterm
event
eval
lua
@@ -555,7 +557,7 @@ foreach(sfile ${NVIM_SOURCES}
set(PREPROC_OUTPUT -w -E -o ${gf_i})
endif()
- set(depends "${HEADER_GENERATOR}" "${sfile}" "${LUA_GEN_DEPS}")
+ set(depends "${HEADER_GENERATOR}" "${sfile}" "${LUA_GEN_DEPS}" "${GENERATOR_C_GRAMMAR}")
if("${f}" STREQUAL "version.c")
# Ensure auto/versiondef_git.h exists after "make clean".
list(APPEND depends update_version_stamp "${NVIM_VERSION_GIT_H}" "${NVIM_VERSION_DEF_H}")
@@ -657,6 +659,7 @@ list(APPEND NVIM_GENERATED_FOR_HEADERS
"${GENERATED_EVENTS_ENUM}"
"${GENERATED_KEYSETS_DEFS}"
"${GENERATED_OPTIONS_ENUM}"
+ "${GENERATED_OPTION_VARS}"
)
list(APPEND NVIM_GENERATED_FOR_SOURCES
@@ -686,8 +689,8 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
)
-add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
- COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP}
+add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
+ COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
)
@@ -824,6 +827,7 @@ target_link_libraries(libnvim PRIVATE main_lib PUBLIC libuv)
#-------------------------------------------------------------------------------
find_program(CLANG_TIDY_PRG clang-tidy)
+mark_as_advanced(CLANG_TIDY_PRG)
set(EXCLUDE_CLANG_TIDY typval_encode.c.h ui_events.in.h)
if(WIN32)
list(APPEND EXCLUDE_CLANG_TIDY
diff --git a/src/nvim/api/autocmd.c b/src/nvim/api/autocmd.c
index 22932fd1a2..d436dbb7f1 100644
--- a/src/nvim/api/autocmd.c
+++ b/src/nvim/api/autocmd.c
@@ -2,7 +2,6 @@
#include <lauxlib.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -23,6 +22,7 @@
#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -68,32 +68,31 @@ static int64_t next_autocmd_id = 1;
/// match any combination of them.
///
/// @param opts Dict with at least one of the following:
-/// - group (string|integer): the autocommand group name or id to match against.
-/// - event (string|array): event or events to match against |autocmd-events|.
-/// - pattern (string|array): pattern or patterns to match against |autocmd-pattern|.
-/// Cannot be used with {buffer}
-/// - buffer: Buffer number or list of buffer numbers for buffer local autocommands
+/// - buffer: (integer) Buffer number or list of buffer numbers for buffer local autocommands
/// |autocmd-buflocal|. Cannot be used with {pattern}
+/// - event: (string|table) event or events to match against |autocmd-events|.
+/// - id: (integer) Autocommand ID to match.
+/// - group: (string|table) the autocommand group name or id to match against.
+/// - pattern: (string|table) pattern or patterns to match against |autocmd-pattern|.
+/// Cannot be used with {buffer}
/// @return Array of autocommands matching the criteria, with each item
/// containing the following fields:
-/// - id (number): the autocommand id (only when defined with the API).
-/// - group (integer): the autocommand group id.
-/// - group_name (string): the autocommand group name.
-/// - desc (string): the autocommand description.
-/// - event (string): the autocommand event.
-/// - command (string): the autocommand command. Note: this will be empty if a callback is set.
-/// - callback (function|string|nil): Lua function or name of a Vim script function
+/// - buffer: (integer) the buffer number.
+/// - buflocal: (boolean) true if the autocommand is buffer local.
+/// - command: (string) the autocommand command. Note: this will be empty if a callback is set.
+/// - callback: (function|string|nil): Lua function or name of a Vim script function
/// which is executed when this autocommand is triggered.
-/// - once (boolean): whether the autocommand is only run once.
-/// - pattern (string): the autocommand pattern.
+/// - desc: (string) the autocommand description.
+/// - event: (string) the autocommand event.
+/// - id: (integer) the autocommand id (only when defined with the API).
+/// - group: (integer) the autocommand group id.
+/// - group_name: (string) the autocommand group name.
+/// - once: (boolean) whether the autocommand is only run once.
+/// - pattern: (string) the autocommand pattern.
/// If the autocommand is buffer local |autocmd-buffer-local|:
-/// - buflocal (boolean): true if the autocommand is buffer local.
-/// - buffer (number): the buffer number.
Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(9)
{
- // TODO(tjdevries): Would be cool to add nvim_get_autocmds({ id = ... })
-
ArrayBuilder autocmd_list = KV_INITIAL_VALUE;
kvi_init(autocmd_list);
char *pattern_filters[AUCMD_MAX_PATTERNS];
@@ -127,6 +126,8 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
});
}
+ int id = (HAS_KEY(opts, get_autocmds, id)) ? (int)opts->id : -1;
+
if (HAS_KEY(opts, get_autocmds, event)) {
check_event = true;
@@ -237,6 +238,10 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
continue;
}
+ if (id != -1 && ac->id != id) {
+ continue;
+ }
+
// Skip autocmds from invalid groups if passed.
if (group != 0 && ap->group != group) {
continue;
@@ -285,10 +290,12 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
PUT_C(autocmd_info, "desc", CSTR_AS_OBJ(ac->desc));
}
- if (ac->exec.type == CALLABLE_CB) {
+ if (ac->handler_cmd) {
+ PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->handler_cmd));
+ } else {
PUT_C(autocmd_info, "command", STRING_OBJ(STRING_INIT));
- Callback *cb = &ac->exec.callable.cb;
+ Callback *cb = &ac->handler_fn;
switch (cb->type) {
case kCallbackLua:
if (nlua_ref_is_function(cb->data.luaref)) {
@@ -302,8 +309,6 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
case kCallbackNone:
abort();
}
- } else {
- PUT_C(autocmd_info, "command", CSTR_AS_OBJ(ac->exec.callable.cmd));
}
PUT_C(autocmd_info, "pattern", CSTR_AS_OBJ(ap->pat));
@@ -317,23 +322,6 @@ Array nvim_get_autocmds(Dict(get_autocmds) *opts, Arena *arena, Error *err)
PUT_C(autocmd_info, "buflocal", BOOLEAN_OBJ(false));
}
- // TODO(sctx): It would be good to unify script_ctx to actually work with lua
- // right now it's just super weird, and never really gives you the info that
- // you would expect from this.
- //
- // I think we should be able to get the line number, filename, etc. from lua
- // when we're executing something, and it should be easy to then save that
- // info here.
- //
- // I think it's a big loss not getting line numbers of where options, autocmds,
- // etc. are set (just getting "Sourced (lua)" or something is not that helpful.
- //
- // Once we do that, we can put these into the autocmd_info, but I don't think it's
- // useful to do that at this time.
- //
- // PUT_C(autocmd_info, "sid", INTEGER_OBJ(ac->script_ctx.sc_sid));
- // PUT_C(autocmd_info, "lnum", INTEGER_OBJ(ac->script_ctx.sc_lnum));
-
kvi_push(autocmd_list, DICT_OBJ(autocmd_info));
}
}
@@ -386,9 +374,9 @@ cleanup:
/// - id: (number) autocommand id
/// - event: (string) name of the triggered event |autocmd-events|
/// - group: (number|nil) autocommand group id, if any
-/// - match: (string) expanded value of [<amatch>]
-/// - buf: (number) expanded value of [<abuf>]
-/// - file: (string) expanded value of [<afile>]
+/// - file: (string) [<afile>] (not expanded to a full path)
+/// - match: (string) [<amatch>] (expanded to a full path)
+/// - buf: (number) [<abuf>]
/// - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]()
/// - command (string) optional: Vim command to execute on event. Cannot be used with
/// {callback}
@@ -406,8 +394,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
{
int64_t autocmd_id = -1;
char *desc = NULL;
- AucmdExecutable aucmd = AUCMD_EXECUTABLE_INIT;
- Callback cb = CALLBACK_NONE;
+ char *handler_cmd = NULL;
+ Callback handler_fn = CALLBACK_NONE;
Array event_array = unpack_string_or_array(event, "event", true, arena, err);
if (ERROR_SET(err)) {
@@ -432,13 +420,13 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
- cb.type = kCallbackLua;
- cb.data.luaref = callback->data.luaref;
+ handler_fn.type = kCallbackLua;
+ handler_fn.data.luaref = callback->data.luaref;
callback->data.luaref = LUA_NOREF;
break;
case kObjectTypeString:
- cb.type = kCallbackFuncref;
- cb.data.funcref = string_to_cstr(callback->data.string);
+ handler_fn.type = kCallbackFuncref;
+ handler_fn.data.funcref = string_to_cstr(callback->data.string);
break;
default:
VALIDATE_EXP(false, "callback", "Lua function or Vim function name",
@@ -446,12 +434,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
goto cleanup;
});
}
-
- aucmd.type = CALLABLE_CB;
- aucmd.callable.cb = cb;
} else if (HAS_KEY(opts, create_autocmd, command)) {
- aucmd.type = CALLABLE_EX;
- aucmd.callable.cmd = string_to_cstr(opts->command);
+ handler_cmd = string_to_cstr(opts->command);
} else {
VALIDATE(false, "%s", "Required: 'command' or 'callback'", {
goto cleanup;
@@ -491,7 +475,6 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
int retval;
FOREACH_ITEM(patterns, pat, {
- // See: TODO(sctx)
WITH_SCRIPT_CONTEXT(channel_id, {
retval = autocmd_register(autocmd_id,
event_nr,
@@ -501,7 +484,8 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
opts->once,
opts->nested,
desc,
- aucmd);
+ handler_cmd,
+ &handler_fn);
});
if (retval == FAIL) {
@@ -512,7 +496,11 @@ Integer nvim_create_autocmd(uint64_t channel_id, Object event, Dict(create_autoc
});
cleanup:
- aucmd_exec_free(&aucmd);
+ if (handler_cmd) {
+ XFREE_CLEAR(handler_cmd);
+ } else {
+ callback_free(&handler_fn);
+ }
return autocmd_id;
}
@@ -631,7 +619,7 @@ Integer nvim_create_augroup(uint64_t channel_id, String name, Dict(create_augrou
FUNC_API_SINCE(9)
{
char *augroup_name = name.data;
- bool clear_autocmds = api_object_to_bool(opts->clear, "clear", true, err);
+ bool clear_autocmds = GET_BOOL_OR_TRUE(opts, create_augroup, clear);
int augroup = -1;
WITH_SCRIPT_CONTEXT(channel_id, {
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 9480292d9a..aa349790b3 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -23,7 +23,6 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/drawscreen.h"
#include "nvim/ex_cmds.h"
#include "nvim/extmark.h"
#include "nvim/extmark_defs.h"
@@ -360,93 +359,91 @@ void nvim_buf_set_lines(uint64_t channel_id, Buffer buffer, Integer start, Integ
memchrsub(lines[i], NUL, NL, l.size);
}
- try_start();
-
- if (!MODIFIABLE(buf)) {
- api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
- goto end;
- }
-
- if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to save undo information");
- goto end;
- }
-
- bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
-
- // If the size of the range is reducing (ie, new_len < old_len) we
- // need to delete some old_len. We do this at the start, by
- // repeatedly deleting line "start".
- size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
- for (size_t i = 0; i < to_delete; i++) {
- if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to delete line");
+ TRY_WRAP(err, {
+ if (!MODIFIABLE(buf)) {
+ api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
goto end;
}
- }
- if (to_delete > 0) {
- extra -= (ptrdiff_t)to_delete;
- }
+ if (u_save_buf(buf, (linenr_T)(start - 1), (linenr_T)end) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to save undo information");
+ goto end;
+ }
- // For as long as possible, replace the existing old_len with the
- // new old_len. This is a more efficient operation, as it requires
- // less memory allocation and freeing.
- size_t to_replace = old_len < new_len ? old_len : new_len;
- bcount_t inserted_bytes = 0;
- for (size_t i = 0; i < to_replace; i++) {
- int64_t lnum = start + (int64_t)i;
+ bcount_t deleted_bytes = get_region_bytecount(buf, (linenr_T)start, (linenr_T)end, 0, 0);
- VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
- goto end;
- });
+ // If the size of the range is reducing (ie, new_len < old_len) we
+ // need to delete some old_len. We do this at the start, by
+ // repeatedly deleting line "start".
+ size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
+ for (size_t i = 0; i < to_delete; i++) {
+ if (ml_delete_buf(buf, (linenr_T)start, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to delete line");
+ goto end;
+ }
+ }
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to replace line");
- goto end;
+ if (to_delete > 0) {
+ extra -= (ptrdiff_t)to_delete;
}
- inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- }
+ // For as long as possible, replace the existing old_len with the
+ // new old_len. This is a more efficient operation, as it requires
+ // less memory allocation and freeing.
+ size_t to_replace = old_len < new_len ? old_len : new_len;
+ bcount_t inserted_bytes = 0;
+ for (size_t i = 0; i < to_replace; i++) {
+ int64_t lnum = start + (int64_t)i;
+
+ VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to replace line");
+ goto end;
+ }
- // Now we may need to insert the remaining new old_len
- for (size_t i = to_replace; i < new_len; i++) {
- int64_t lnum = start + (int64_t)i - 1;
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
+ }
- VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
- goto end;
- });
+ // Now we may need to insert the remaining new old_len
+ for (size_t i = to_replace; i < new_len; i++) {
+ int64_t lnum = start + (int64_t)i - 1;
- if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to insert line");
- goto end;
- }
+ VALIDATE(lnum < MAXLNUM, "%s", "Index out of bounds", {
+ goto end;
+ });
- inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
+ if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to insert line");
+ goto end;
+ }
- extra++;
- }
+ inserted_bytes += (bcount_t)strlen(lines[i]) + 1;
- // Adjust marks. Invalidate any which lie in the
- // changed range, and move any in the remainder of the buffer.
- linenr_T adjust = end > start ? MAXLNUM : 0;
- mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), adjust, (linenr_T)extra,
- true, true, kExtmarkNOOP);
+ extra++;
+ }
- extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
- deleted_bytes, (int)new_len, 0, inserted_bytes,
- kExtmarkUndo);
+ // Adjust marks. Invalidate any which lie in the
+ // changed range, and move any in the remainder of the buffer.
+ linenr_T adjust = end > start ? MAXLNUM : 0;
+ mark_adjust_buf(buf, (linenr_T)start, (linenr_T)(end - 1), adjust, (linenr_T)extra,
+ true, true, kExtmarkNOOP);
- changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
+ extmark_splice(buf, (int)start - 1, 0, (int)(end - start), 0,
+ deleted_bytes, (int)new_len, 0, inserted_bytes,
+ kExtmarkUndo);
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_buffer == buf) {
- fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra);
- }
- }
+ changed_lines(buf, (linenr_T)start, 0, (linenr_T)end, (linenr_T)extra, true);
-end:
- try_end(err);
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_buffer == buf) {
+ fix_cursor(win, (linenr_T)start, (linenr_T)end, (linenr_T)extra);
+ }
+ }
+ end:;
+ });
}
/// Sets (replaces) a range in the buffer
@@ -593,101 +590,99 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, Integer start_row, In
new_byte += (bcount_t)(last_item.size) + 1;
}
- try_start();
-
- if (!MODIFIABLE(buf)) {
- api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
- goto end;
- }
-
- // Small note about undo states: unlike set_lines, we want to save the
- // undo state of one past the end_row, since end_row is inclusive.
- if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to save undo information");
- goto end;
- }
-
- ptrdiff_t extra = 0; // lines added to text, can be negative
- size_t old_len = (size_t)(end_row - start_row + 1);
-
- // If the size of the range is reducing (ie, new_len < old_len) we
- // need to delete some old_len. We do this at the start, by
- // repeatedly deleting line "start".
- size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
- for (size_t i = 0; i < to_delete; i++) {
- if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to delete line");
+ TRY_WRAP(err, {
+ if (!MODIFIABLE(buf)) {
+ api_set_error(err, kErrorTypeException, "Buffer is not 'modifiable'");
goto end;
}
- }
- if (to_delete > 0) {
- extra -= (ptrdiff_t)to_delete;
- }
-
- // For as long as possible, replace the existing old_len with the
- // new old_len. This is a more efficient operation, as it requires
- // less memory allocation and freeing.
- size_t to_replace = old_len < new_len ? old_len : new_len;
- for (size_t i = 0; i < to_replace; i++) {
- int64_t lnum = start_row + (int64_t)i;
-
- VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ // Small note about undo states: unlike set_lines, we want to save the
+ // undo state of one past the end_row, since end_row is inclusive.
+ if (u_save_buf(buf, (linenr_T)start_row - 1, (linenr_T)end_row + 1) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to save undo information");
goto end;
- });
+ }
- if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to replace line");
- goto end;
+ ptrdiff_t extra = 0; // lines added to text, can be negative
+ size_t old_len = (size_t)(end_row - start_row + 1);
+
+ // If the size of the range is reducing (ie, new_len < old_len) we
+ // need to delete some old_len. We do this at the start, by
+ // repeatedly deleting line "start".
+ size_t to_delete = (new_len < old_len) ? old_len - new_len : 0;
+ for (size_t i = 0; i < to_delete; i++) {
+ if (ml_delete_buf(buf, (linenr_T)start_row, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to delete line");
+ goto end;
+ }
}
- }
- // Now we may need to insert the remaining new old_len
- for (size_t i = to_replace; i < new_len; i++) {
- int64_t lnum = start_row + (int64_t)i - 1;
+ if (to_delete > 0) {
+ extra -= (ptrdiff_t)to_delete;
+ }
- VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
- goto end;
- });
+ // For as long as possible, replace the existing old_len with the
+ // new old_len. This is a more efficient operation, as it requires
+ // less memory allocation and freeing.
+ size_t to_replace = old_len < new_len ? old_len : new_len;
+ for (size_t i = 0; i < to_replace; i++) {
+ int64_t lnum = start_row + (int64_t)i;
- if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
- api_set_error(err, kErrorTypeException, "Failed to insert line");
- goto end;
+ VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_replace_buf(buf, (linenr_T)lnum, lines[i], false, true) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to replace line");
+ goto end;
+ }
}
- extra++;
- }
+ // Now we may need to insert the remaining new old_len
+ for (size_t i = to_replace; i < new_len; i++) {
+ int64_t lnum = start_row + (int64_t)i - 1;
- colnr_T col_extent = (colnr_T)(end_col
- - ((end_row == start_row) ? start_col : 0));
-
- // Adjust marks. Invalidate any which lie in the
- // changed range, and move any in the remainder of the buffer.
- // Do not adjust any cursors. need to use column-aware logic (below)
- linenr_T adjust = end_row >= start_row ? MAXLNUM : 0;
- mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, adjust, (linenr_T)extra,
- true, true, kExtmarkNOOP);
-
- extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
- (int)(end_row - start_row), col_extent, old_byte,
- (int)new_len - 1, (colnr_T)last_item.size, new_byte,
- kExtmarkUndo);
-
- changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
-
- FOR_ALL_TAB_WINDOWS(tp, win) {
- if (win->w_buffer == buf) {
- if (win->w_cursor.lnum >= start_row && win->w_cursor.lnum <= end_row) {
- fix_cursor_cols(win, (linenr_T)start_row, (colnr_T)start_col, (linenr_T)end_row,
- (colnr_T)end_col, (linenr_T)new_len, (colnr_T)last_item.size);
- } else {
- fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
+ VALIDATE((lnum < MAXLNUM), "%s", "Index out of bounds", {
+ goto end;
+ });
+
+ if (ml_append_buf(buf, (linenr_T)lnum, lines[i], 0, false) == FAIL) {
+ api_set_error(err, kErrorTypeException, "Failed to insert line");
+ goto end;
}
+
+ extra++;
}
- }
-end:
- try_end(err);
+ colnr_T col_extent = (colnr_T)(end_col
+ - ((end_row == start_row) ? start_col : 0));
+
+ // Adjust marks. Invalidate any which lie in the
+ // changed range, and move any in the remainder of the buffer.
+ // Do not adjust any cursors. need to use column-aware logic (below)
+ linenr_T adjust = end_row >= start_row ? MAXLNUM : 0;
+ mark_adjust_buf(buf, (linenr_T)start_row, (linenr_T)end_row, adjust, (linenr_T)extra,
+ true, true, kExtmarkNOOP);
+
+ extmark_splice(buf, (int)start_row - 1, (colnr_T)start_col,
+ (int)(end_row - start_row), col_extent, old_byte,
+ (int)new_len - 1, (colnr_T)last_item.size, new_byte,
+ kExtmarkUndo);
+
+ changed_lines(buf, (linenr_T)start_row, 0, (linenr_T)end_row + 1, (linenr_T)extra, true);
+
+ FOR_ALL_TAB_WINDOWS(tp, win) {
+ if (win->w_buffer == buf) {
+ if (win->w_cursor.lnum >= start_row && win->w_cursor.lnum <= end_row) {
+ fix_cursor_cols(win, (linenr_T)start_row, (colnr_T)start_col, (linenr_T)end_row,
+ (colnr_T)end_col, (linenr_T)new_len, (colnr_T)last_item.size);
+ } else {
+ fix_cursor(win, (linenr_T)start_row, (linenr_T)end_row, (linenr_T)extra);
+ }
+ }
+ }
+ end:;
+ });
}
/// Gets a range from the buffer.
@@ -965,26 +960,27 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err)
return;
}
- try_start();
-
- const bool is_curbuf = buf == curbuf;
- const int save_acd = p_acd;
- if (!is_curbuf) {
- // Temporarily disable 'autochdir' when setting file name for another buffer.
- p_acd = false;
- }
+ int ren_ret = OK;
+ TRY_WRAP(err, {
+ const bool is_curbuf = buf == curbuf;
+ const int save_acd = p_acd;
+ if (!is_curbuf) {
+ // Temporarily disable 'autochdir' when setting file name for another buffer.
+ p_acd = false;
+ }
- // Using aucmd_*: autocommands will be executed by rename_buffer
- aco_save_T aco;
- aucmd_prepbuf(&aco, buf);
- int ren_ret = rename_buffer(name.data);
- aucmd_restbuf(&aco);
+ // Using aucmd_*: autocommands will be executed by rename_buffer
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+ ren_ret = rename_buffer(name.data);
+ aucmd_restbuf(&aco);
- if (!is_curbuf) {
- p_acd = save_acd;
- }
+ if (!is_curbuf) {
+ p_acd = save_acd;
+ }
+ });
- if (try_end(err)) {
+ if (ERROR_SET(err)) {
return;
}
@@ -1184,12 +1180,12 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Arena *arena,
/// This temporarily switches current buffer to "buffer".
/// If the current window already shows "buffer", the window is not switched.
/// If a window inside the current tabpage (including a float) already shows the
-/// buffer, then one of these windows will be set as current window temporarily.
+/// buffer, then one of those windows will be set as current window temporarily.
/// Otherwise a temporary scratch window (called the "autocmd window" for
/// historical reasons) will be used.
///
/// This is useful e.g. to call Vimscript functions that only work with the
-/// current buffer/window currently, like |termopen()|.
+/// current buffer/window currently, like `jobstart(…, {'term': v:true})`.
///
/// @param buffer Buffer handle, or 0 for current buffer
/// @param fun Function to call inside the buffer (currently Lua callable
@@ -1204,15 +1200,18 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
if (!buf) {
return NIL;
}
- try_start();
- aco_save_T aco;
- aucmd_prepbuf(&aco, buf);
- Array args = ARRAY_DICT_INIT;
- Object res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ Object res = OBJECT_INIT;
+ TRY_WRAP(err, {
+ aco_save_T aco;
+ aucmd_prepbuf(&aco, buf);
+
+ Array args = ARRAY_DICT_INIT;
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+
+ aucmd_restbuf(&aco);
+ });
- aucmd_restbuf(&aco);
- try_end(err);
return res;
}
diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c
index ab57d5c009..23e08bd3fe 100644
--- a/src/nvim/api/command.c
+++ b/src/nvim/api/command.c
@@ -26,6 +26,7 @@
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/ops.h"
#include "nvim/pos_defs.h"
#include "nvim/regexp.h"
@@ -226,8 +227,8 @@ Dict(cmd) nvim_parse_cmd(String str, Dict(empty) *opts, Arena *arena, Error *err
addr = "?";
break;
}
- PUT_KEY(result, cmd, addr, CSTR_AS_OBJ(addr));
- PUT_KEY(result, cmd, nextcmd, CSTR_AS_OBJ(ea.nextcmd));
+ PUT_KEY(result, cmd, addr, cstr_as_string(addr));
+ PUT_KEY(result, cmd, nextcmd, cstr_as_string(ea.nextcmd));
// TODO(bfredl): nested keydict would be nice..
Dict mods = arena_dict(arena, 20);
@@ -930,6 +931,9 @@ void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
gap = &ucmds;
} else {
buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (ERROR_SET(err)) {
+ return;
+ }
gap = &buf->b_ucmds;
}
diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c
index b38a7d4173..1d81b21be6 100644
--- a/src/nvim/api/deprecated.c
+++ b/src/nvim/api/deprecated.c
@@ -1,3 +1,5 @@
+// Island of misfit toys.
+
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
@@ -7,6 +9,7 @@
#include "nvim/api/extmark.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/vimscript.h"
@@ -18,14 +21,14 @@
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
+#include "nvim/marktree.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
-#include "nvim/msgpack_rpc/channel.h"
-#include "nvim/msgpack_rpc/channel_defs.h"
-#include "nvim/msgpack_rpc/unpacker.h"
+#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/pos_defs.h"
+#include "nvim/strings.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -82,6 +85,17 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err)
return buf->b_fnum;
}
+static uint32_t src2ns(Integer *src_id)
+{
+ if (*src_id == 0) {
+ *src_id = nvim_create_namespace((String)STRING_INIT);
+ }
+ if (*src_id < 0) {
+ return (((uint32_t)1) << 31) - 1;
+ }
+ return (uint32_t)(*src_id);
+}
+
/// Clears highlights and virtual text from namespace and range of lines
///
/// @deprecated use |nvim_buf_clear_namespace()|.
@@ -100,6 +114,80 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer ns_id, Integer line_start,
nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err);
}
+/// Adds a highlight to buffer.
+///
+/// @deprecated use |nvim_buf_set_extmark()| or |vim.hl.range()|
+///
+/// Namespaces are used for batch deletion/updating of a set of highlights. To
+/// create a namespace, use |nvim_create_namespace()| which returns a namespace
+/// id. Pass it in to this function as `ns_id` to add highlights to the
+/// namespace. All highlights in the same namespace can then be cleared with
+/// single call to |nvim_buf_clear_namespace()|. If the highlight never will be
+/// deleted by an API call, pass `ns_id = -1`.
+///
+/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
+/// highlight, the allocated id is then returned. If `hl_group` is the empty
+/// string no highlight is added, but a new `ns_id` is still returned. This is
+/// supported for backwards compatibility, new code should use
+/// |nvim_create_namespace()| to create a new empty namespace.
+///
+/// @param buffer Buffer handle, or 0 for current buffer
+/// @param ns_id namespace to use or -1 for ungrouped highlight
+/// @param hl_group Name of the highlight group to use
+/// @param line Line to highlight (zero-indexed)
+/// @param col_start Start of (byte-indexed) column range to highlight
+/// @param col_end End of (byte-indexed) column range to highlight,
+/// or -1 to highlight to end of line
+/// @param[out] err Error details, if any
+/// @return The ns_id that was used
+Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, Integer line,
+ Integer col_start, Integer col_end, Error *err)
+ FUNC_API_SINCE(1)
+ FUNC_API_DEPRECATED_SINCE(13)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
+ return 0;
+ });
+ VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
+ return 0;
+ });
+
+ if (col_end < 0 || col_end > MAXCOL) {
+ col_end = MAXCOL;
+ }
+
+ uint32_t ns = src2ns(&ns_id);
+
+ if (!(line < buf->b_ml.ml_line_count)) {
+ // safety check, we can't add marks outside the range
+ return ns_id;
+ }
+
+ int hl_id = 0;
+ if (hl_group.size > 0) {
+ hl_id = syn_check_group(hl_group.data, hl_group.size);
+ } else {
+ return ns_id;
+ }
+
+ int end_line = (int)line;
+ if (col_end == MAXCOL) {
+ col_end = 0;
+ end_line++;
+ }
+
+ DecorInline decor = DECOR_INLINE_INIT;
+ decor.data.hl.hl_id = hl_id;
+
+ extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
+ decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
+ return ns_id;
+}
/// Set the virtual text (annotation) for a buffer line.
///
/// @deprecated use nvim_buf_set_extmark to use full virtual text functionality.
@@ -636,23 +724,27 @@ void nvim_win_set_option(uint64_t channel_id, Window window, String name, Object
/// Gets the value of a global or local (buffer, window) option.
///
/// @param[in] from Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param name The option name.
/// @param[out] err Details of an error that may have occurred.
///
/// @return the option value.
-static Object get_option_from(void *from, OptScope req_scope, String name, Error *err)
+static Object get_option_from(void *from, OptScope scope, String name, Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return (Object)OBJECT_INIT;
});
OptIndex opt_idx = find_option(name.data);
+ VALIDATE_S(opt_idx != kOptInvalid, "option name", name.data, {
+ return (Object)OBJECT_INIT;
+ });
+
OptVal value = NIL_OPTVAL;
- if (option_has_scope(opt_idx, req_scope)) {
- value = get_option_value_for(opt_idx, req_scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
- req_scope, from, err);
+ if (option_has_scope(opt_idx, scope)) {
+ value = get_option_value_for(opt_idx, scope == kOptScopeGlobal ? OPT_GLOBAL : OPT_LOCAL,
+ scope, from, err);
if (ERROR_SET(err)) {
return (Object)OBJECT_INIT;
}
@@ -668,12 +760,12 @@ static Object get_option_from(void *from, OptScope req_scope, String name, Error
/// Sets the value of a global or local (buffer, window) option.
///
/// @param[in] to Pointer to buffer or window for local option value.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param name The option name.
/// @param value New option value.
/// @param[out] err Details of an error that may have occurred.
-static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, String name,
- Object value, Error *err)
+static void set_option_to(uint64_t channel_id, void *to, OptScope scope, String name, Object value,
+ Error *err)
{
VALIDATE_S(name.size > 0, "option name", "<empty>", {
return;
@@ -698,12 +790,12 @@ static void set_option_to(uint64_t channel_id, void *to, OptScope req_scope, Str
// For global-win-local options -> setlocal
// For win-local options -> setglobal and setlocal (opt_flags == 0)
const int opt_flags
- = (req_scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
+ = (scope == kOptScopeWin && !option_has_scope(opt_idx, kOptScopeGlobal))
? 0
- : ((req_scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
+ : ((scope == kOptScopeGlobal) ? OPT_GLOBAL : OPT_LOCAL);
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, opt_idx, optval, opt_flags, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, scope, to, err);
});
}
@@ -798,7 +890,8 @@ theend:
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
void nvim_subscribe(uint64_t channel_id, String event)
- FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
+// XXX: c_grammar.lua is order-sensitive.
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
@@ -808,7 +901,105 @@ void nvim_subscribe(uint64_t channel_id, String event)
/// @param channel_id Channel id (passed automatically by the dispatcher)
/// @param event Event type string
void nvim_unsubscribe(uint64_t channel_id, String event)
- FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY
+// XXX: c_grammar.lua is order-sensitive.
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY
{
// Does nothing. `rpcnotify(0,…)` broadcasts to all channels, there are no "subscriptions".
}
+
+enum { LINE_BUFFER_MIN_SIZE = 4096, };
+
+/// Writes a message to vim output or error buffer. The string is split
+/// and flushed after each newline. Incomplete lines are kept for writing
+/// later.
+///
+/// @param message Message to write
+/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
+/// @param writeln Append a trailing newline
+static void write_msg(String message, bool to_err, bool writeln)
+{
+ static StringBuilder out_line_buf = KV_INITIAL_VALUE;
+ static StringBuilder err_line_buf = KV_INITIAL_VALUE;
+ StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
+
+#define PUSH_CHAR(c) \
+ if (kv_max(*line_buf) == 0) { \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } \
+ if (c == NL) { \
+ kv_push(*line_buf, NUL); \
+ if (to_err) { \
+ emsg(line_buf->items); \
+ } else { \
+ msg(line_buf->items, 0); \
+ } \
+ if (msg_silent == 0) { \
+ msg_didout = true; \
+ } \
+ kv_drop(*line_buf, kv_size(*line_buf)); \
+ kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
+ } else if (c == NUL) { \
+ kv_push(*line_buf, NL); \
+ } else { \
+ kv_push(*line_buf, c); \
+ }
+
+ no_wait_return++;
+ for (uint32_t i = 0; i < message.size; i++) {
+ if (got_int) {
+ break;
+ }
+ PUSH_CHAR(message.data[i]);
+ }
+ if (writeln) {
+ PUSH_CHAR(NL);
+ }
+ no_wait_return--;
+ msg_end();
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_out_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, false, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_write(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, false);
+}
+
+/// @deprecated
+///
+/// @param str Message
+void nvim_err_writeln(String str)
+ FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13)
+{
+ write_msg(str, true, true);
+}
+
+/// @deprecated
+///
+/// Use `nvim_echo` or `nvim_exec_lua("vim.notify(...)", ...)` instead.
+///
+/// @param msg Message to display to the user
+/// @param log_level The log level
+/// @param opts Reserved for future use.
+/// @param[out] err Error details, if any
+Object nvim_notify(String msg, Integer log_level, Dict opts, Arena *arena, Error *err)
+ FUNC_API_SINCE(7) FUNC_API_DEPRECATED_SINCE(13)
+{
+ MAXSIZE_TEMP_ARRAY(args, 3);
+ ADD_C(args, STRING_OBJ(msg));
+ ADD_C(args, INTEGER_OBJ(log_level));
+ ADD_C(args, DICT_OBJ(opts));
+
+ return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
+}
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index c94b8df9ea..8b31196eef 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -27,6 +27,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/move.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -48,7 +49,7 @@ void api_extmark_free_all_mem(void)
/// Creates a new namespace or gets an existing one. [namespace]()
///
/// Namespaces are used for buffer highlights and virtual text, see
-/// |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|.
+/// |nvim_buf_set_extmark()|.
///
/// Namespaces can be named or anonymous. If `name` matches an existing
/// namespace, the associated id is returned. If `name` is an empty string
@@ -61,7 +62,7 @@ Integer nvim_create_namespace(String name)
{
handle_T id = map_get(String, int)(&namespace_ids, name);
if (id > 0) {
- return id;
+ return (Integer)id;
}
id = next_namespace_id++;
if (name.size > 0) {
@@ -384,6 +385,9 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// - hl_group : highlight group used for the text range. This and below
/// highlight groups can be supplied either as a string or as an integer,
/// the latter of which can be obtained using |nvim_get_hl_id_by_name()|.
+///
+/// Multiple highlight groups can be stacked by passing an array (highest
+/// priority last).
/// - hl_eol : when true, for a multiline highlight covering the
/// EOL of a line, continue the highlight for the rest
/// of the screen line (just like for diff and
@@ -396,6 +400,15 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e
/// (highest priority last).
/// - virt_text_pos : position of virtual text. Possible values:
/// - "eol": right after eol character (default).
+/// - "eol_right_align": display right aligned in the window
+/// unless the virtual text is longer than
+/// the space available. If the virtual
+/// text is too long, it is truncated to
+/// fit in the window after the EOL
+/// character. If the line is wrapped, the
+/// virtual text is shown after the end of
+/// the line rather than the previous
+/// screen line.
/// - "overlay": display over the specified column, without
/// shifting the underlying text.
/// - "right_align": display right aligned in the window.
@@ -498,6 +511,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
DecorVirtText virt_lines = DECOR_VIRT_LINES_INIT;
char *url = NULL;
bool has_hl = false;
+ bool has_hl_multiple = false;
buf_T *buf = find_buffer_by_handle(buffer, err);
if (!buf) {
@@ -550,8 +564,33 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
col2 = (int)val;
}
- hl.hl_id = (int)opts->hl_group;
- has_hl = hl.hl_id > 0;
+ if (HAS_KEY(opts, set_extmark, hl_group)) {
+ if (opts->hl_group.type == kObjectTypeArray) {
+ Array arr = opts->hl_group.data.array;
+ if (arr.size >= 1) {
+ hl.hl_id = object_to_hl_id(arr.items[0], "hl_group item", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
+ for (size_t i = 1; i < arr.size; i++) {
+ int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ if (hl_id) {
+ has_hl_multiple = true;
+ }
+ }
+ } else {
+ hl.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err);
+ if (ERROR_SET(err)) {
+ goto error;
+ }
+ }
+ has_hl = hl.hl_id > 0;
+ }
+
sign.hl_id = (int)opts->sign_hl_group;
sign.cursorline_hl_id = (int)opts->cursorline_hl_group;
sign.number_hl_id = (int)opts->number_hl_group;
@@ -590,6 +629,8 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
virt_text.pos = kVPosOverlay;
} else if (strequal("right_align", str.data)) {
virt_text.pos = kVPosRightAlign;
+ } else if (strequal("eol_right_align", str.data)) {
+ virt_text.pos = kVPosEndOfLineRightAlign;
} else if (strequal("inline", str.data)) {
virt_text.pos = kVPosInline;
} else {
@@ -793,6 +834,21 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer
}
}
+ if (has_hl_multiple) {
+ Array arr = opts->hl_group.data.array;
+ for (size_t i = arr.size - 1; i > 0; i--) { // skip hl_group[0], handled as hl.hl_id below
+ int hl_id = object_to_hl_id(arr.items[i], "hl_group item", err);
+ if (hl_id > 0) {
+ DecorSignHighlight sh = DECOR_SIGN_HIGHLIGHT_INIT;
+ sh.hl_id = hl_id;
+ sh.flags = opts->hl_eol ? kSHHlEol : 0;
+ sh.next = decor_indexed;
+ decor_indexed = decor_put_sh(sh);
+ decor_flags |= MT_FLAG_DECOR_HL;
+ }
+ }
+ }
+
DecorInline decor = DECOR_INLINE_INIT;
if (decor_alloc || decor_indexed != DECOR_ID_INVALID || url != NULL
|| schar_high(hl.conceal_char)) {
@@ -856,95 +912,6 @@ Boolean nvim_buf_del_extmark(Buffer buffer, Integer ns_id, Integer id, Error *er
return extmark_del_id(buf, (uint32_t)ns_id, (uint32_t)id);
}
-uint32_t src2ns(Integer *src_id)
-{
- if (*src_id == 0) {
- *src_id = nvim_create_namespace((String)STRING_INIT);
- }
- if (*src_id < 0) {
- return (((uint32_t)1) << 31) - 1;
- }
- return (uint32_t)(*src_id);
-}
-
-/// Adds a highlight to buffer.
-///
-/// Useful for plugins that dynamically generate highlights to a buffer
-/// (like a semantic highlighter or linter). The function adds a single
-/// highlight to a buffer. Unlike |matchaddpos()| highlights follow changes to
-/// line numbering (as lines are inserted/removed above the highlighted line),
-/// like signs and marks do.
-///
-/// Namespaces are used for batch deletion/updating of a set of highlights. To
-/// create a namespace, use |nvim_create_namespace()| which returns a namespace
-/// id. Pass it in to this function as `ns_id` to add highlights to the
-/// namespace. All highlights in the same namespace can then be cleared with
-/// single call to |nvim_buf_clear_namespace()|. If the highlight never will be
-/// deleted by an API call, pass `ns_id = -1`.
-///
-/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the
-/// highlight, the allocated id is then returned. If `hl_group` is the empty
-/// string no highlight is added, but a new `ns_id` is still returned. This is
-/// supported for backwards compatibility, new code should use
-/// |nvim_create_namespace()| to create a new empty namespace.
-///
-/// @param buffer Buffer handle, or 0 for current buffer
-/// @param ns_id namespace to use or -1 for ungrouped highlight
-/// @param hl_group Name of the highlight group to use
-/// @param line Line to highlight (zero-indexed)
-/// @param col_start Start of (byte-indexed) column range to highlight
-/// @param col_end End of (byte-indexed) column range to highlight,
-/// or -1 to highlight to end of line
-/// @param[out] err Error details, if any
-/// @return The ns_id that was used
-Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, Integer line,
- Integer col_start, Integer col_end, Error *err)
- FUNC_API_SINCE(1)
-{
- buf_T *buf = find_buffer_by_handle(buffer, err);
- if (!buf) {
- return 0;
- }
-
- VALIDATE_RANGE((line >= 0 && line < MAXLNUM), "line number", {
- return 0;
- });
- VALIDATE_RANGE((col_start >= 0 && col_start <= MAXCOL), "column", {
- return 0;
- });
-
- if (col_end < 0 || col_end > MAXCOL) {
- col_end = MAXCOL;
- }
-
- uint32_t ns = src2ns(&ns_id);
-
- if (!(line < buf->b_ml.ml_line_count)) {
- // safety check, we can't add marks outside the range
- return ns_id;
- }
-
- int hl_id = 0;
- if (hl_group.size > 0) {
- hl_id = syn_check_group(hl_group.data, hl_group.size);
- } else {
- return ns_id;
- }
-
- int end_line = (int)line;
- if (col_end == MAXCOL) {
- col_end = 0;
- end_line++;
- }
-
- DecorInline decor = DECOR_INLINE_INIT;
- decor.data.hl.hl_id = hl_id;
-
- extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end,
- decor, MT_FLAG_DECOR_HL, true, false, false, false, NULL);
- return ns_id;
-}
-
/// Clears |namespace|d objects (highlights, |extmarks|, virtual text) from
/// a region.
///
@@ -1012,8 +979,8 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start,
/// ```
/// ["start", tick]
/// ```
-/// - on_buf: called for each buffer being redrawn (before
-/// window callbacks)
+/// - on_buf: called for each buffer being redrawn (once per edit,
+/// before window callbacks)
/// ```
/// ["buf", bufnr, tick]
/// ```
diff --git a/src/nvim/api/extmark.h b/src/nvim/api/extmark.h
index af2d51c95c..0b4d84110b 100644
--- a/src/nvim/api/extmark.h
+++ b/src/nvim/api/extmark.h
@@ -1,5 +1,6 @@
#pragma once
+#include <stdbool.h>
#include <stdint.h> // IWYU pragma: keep
#include "nvim/api/keysets_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h
index 96aabb851f..6625908cda 100644
--- a/src/nvim/api/keysets_defs.h
+++ b/src/nvim/api/keysets_defs.h
@@ -8,18 +8,19 @@ typedef struct {
typedef struct {
OptionalKeys is_set__context_;
- Array types;
+ ArrayOf(String) types;
} Dict(context);
typedef struct {
OptionalKeys is_set__set_decoration_provider_;
- LuaRef on_start;
- LuaRef on_buf;
- LuaRef on_win;
- LuaRef on_line;
- LuaRef on_end;
- LuaRef _on_hl_def;
- LuaRef _on_spell_nav;
+ LuaRefOf(("start" _, Integer tick), *Boolean) on_start;
+ LuaRefOf(("buf" _, Integer bufnr, Integer tick)) on_buf;
+ LuaRefOf(("win" _, Integer winid, Integer bufnr, Integer toprow, Integer botrow),
+ *Boolean) on_win;
+ LuaRefOf(("line" _, Integer winid, Integer bufnr, Integer row), *Boolean) on_line;
+ LuaRefOf(("end" _, Integer tick)) on_end;
+ LuaRefOf(("hl_def" _)) _on_hl_def;
+ LuaRefOf(("spell_nav" _)) _on_spell_nav;
} Dict(set_decoration_provider);
typedef struct {
@@ -28,7 +29,7 @@ typedef struct {
Integer end_line;
Integer end_row;
Integer end_col;
- HLGroupID hl_group;
+ Object hl_group;
Array virt_text;
String virt_text_pos;
Integer virt_text_win_col;
@@ -116,7 +117,7 @@ typedef struct {
String relative;
String split;
Window win;
- Array bufpos;
+ ArrayOf(Integer) bufpos;
Boolean external;
Boolean focusable;
Boolean mouse;
@@ -172,17 +173,17 @@ typedef struct {
Boolean altfont;
Boolean nocombine;
Boolean default_ DictKey(default);
- Object cterm;
- Object foreground;
- Object fg;
- Object background;
- Object bg;
- Object ctermfg;
- Object ctermbg;
- Object special;
- Object sp;
- Object link;
- Object global_link;
+ Union(Integer, String) cterm;
+ Union(Integer, String) foreground;
+ Union(Integer, String) fg;
+ Union(Integer, String) background;
+ Union(Integer, String) bg;
+ Union(Integer, String) ctermfg;
+ Union(Integer, String) ctermbg;
+ Union(Integer, String) special;
+ Union(Integer, String) sp;
+ HLGroupID link;
+ HLGroupID global_link;
Boolean fallback;
Integer blend;
Boolean fg_indexed;
@@ -230,9 +231,9 @@ typedef struct {
typedef struct {
OptionalKeys is_set__clear_autocmds_;
Buffer buffer;
- Object event;
- Object group;
- Object pattern;
+ Union(String, ArrayOf(String)) event;
+ Union(Integer, String) group;
+ Union(String, ArrayOf(String)) pattern;
} Dict(clear_autocmds);
typedef struct {
@@ -241,31 +242,33 @@ typedef struct {
Object callback;
String command;
String desc;
- Object group;
+ Union(Integer, String) group;
Boolean nested;
Boolean once;
- Object pattern;
+ Union(String, ArrayOf(String)) pattern;
} Dict(create_autocmd);
typedef struct {
OptionalKeys is_set__exec_autocmds_;
Buffer buffer;
- Object group;
+ Union(Integer, String) group;
Boolean modeline;
- Object pattern;
+ Union(String, ArrayOf(String)) pattern;
Object data;
} Dict(exec_autocmds);
typedef struct {
OptionalKeys is_set__get_autocmds_;
- Object event;
- Object group;
- Object pattern;
- Object buffer;
+ Union(String, ArrayOf(String)) event;
+ Union(Integer, String) group;
+ Union(String, ArrayOf(String)) pattern;
+ Union(Integer, ArrayOf(Integer)) buffer;
+ Integer id;
} Dict(get_autocmds);
typedef struct {
- Object clear;
+ OptionalKeys is_set__create_augroup_;
+ Boolean clear;
} Dict(create_augroup);
typedef struct {
@@ -275,12 +278,12 @@ typedef struct {
Integer count;
String reg;
Boolean bang;
- Array args;
+ ArrayOf(String) args;
Dict magic;
Dict mods;
- Object nargs;
- Object addr;
- Object nextcmd;
+ Union(Integer, String) nargs;
+ String addr;
+ String nextcmd;
} Dict(cmd);
typedef struct {
@@ -324,6 +327,7 @@ typedef struct {
} Dict(cmd_opts);
typedef struct {
+ Boolean err;
Boolean verbose;
} Dict(echo_opts);
@@ -333,11 +337,30 @@ typedef struct {
typedef struct {
OptionalKeys is_set__buf_attach_;
- LuaRef on_lines;
- LuaRef on_bytes;
- LuaRef on_changedtick;
- LuaRef on_detach;
- LuaRef on_reload;
+ LuaRefOf(("lines" _,
+ Integer bufnr,
+ Integer changedtick,
+ Integer first,
+ Integer last_old,
+ Integer last_new,
+ Integer byte_count,
+ Integer *deleted_codepoints,
+ Integer *deleted_codeunits), *Boolean) on_lines;
+ LuaRefOf(("bytes" _,
+ Integer bufnr,
+ Integer changedtick,
+ Integer start_row,
+ Integer start_col,
+ Integer start_byte,
+ Integer old_end_row,
+ Integer old_end_col,
+ Integer old_end_byte,
+ Integer new_end_row,
+ Integer new_end_col,
+ Integer new_end_byte), *Boolean) on_bytes;
+ LuaRefOf(("changedtick" _, Integer bufnr, Integer changedtick)) on_changedtick;
+ LuaRefOf(("detach" _, Integer bufnr)) on_detach;
+ LuaRefOf(("reload" _, Integer bufnr)) on_reload;
Boolean utf_sizes;
Boolean preview;
} Dict(buf_attach);
@@ -350,7 +373,7 @@ typedef struct {
typedef struct {
OptionalKeys is_set__open_term_;
- LuaRef on_input;
+ LuaRefOf(("input" _, Integer term, Integer bufnr, any data)) on_input;
Boolean force_crlf;
} Dict(open_term);
@@ -361,12 +384,13 @@ typedef struct {
typedef struct {
OptionalKeys is_set__xdl_diff_;
- LuaRef on_hunk;
+ LuaRefOf((Integer start_a, Integer count_a, Integer start_b, Integer count_b),
+ *Integer) on_hunk;
String result_type;
String algorithm;
Integer ctxlen;
Integer interhunkctxlen;
- Object linematch;
+ Union(Boolean, Integer) linematch;
Boolean ignore_whitespace;
Boolean ignore_whitespace_change;
Boolean ignore_whitespace_change_at_eol;
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index 3289daeb6f..64f8a35d54 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -14,6 +14,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -23,15 +24,15 @@
#endif
static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *opt_idxp,
- int *scope, OptScope *req_scope, void **from, char **filetype,
+ int *opt_flags, OptScope *scope, void **from, char **filetype,
Error *err)
{
#define HAS_KEY_X(d, v) HAS_KEY(d, option, v)
if (HAS_KEY_X(opts, scope)) {
if (!strcmp(opts->scope.data, "local")) {
- *scope = OPT_LOCAL;
+ *opt_flags = OPT_LOCAL;
} else if (!strcmp(opts->scope.data, "global")) {
- *scope = OPT_GLOBAL;
+ *opt_flags = OPT_GLOBAL;
} else {
VALIDATE_EXP(false, "scope", "'local' or 'global'", NULL, {
return FAIL;
@@ -39,14 +40,14 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- *req_scope = kOptScopeGlobal;
+ *scope = kOptScopeGlobal;
if (filetype != NULL && HAS_KEY_X(opts, filetype)) {
*filetype = opts->filetype.data;
}
if (HAS_KEY_X(opts, win)) {
- *req_scope = kOptScopeWin;
+ *scope = kOptScopeWin;
*from = find_window_by_handle(opts->win, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -54,12 +55,12 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
if (HAS_KEY_X(opts, buf)) {
- VALIDATE(!(HAS_KEY_X(opts, scope) && *scope == OPT_GLOBAL), "%s",
+ VALIDATE(!(HAS_KEY_X(opts, scope) && *opt_flags == OPT_GLOBAL), "%s",
"cannot use both global 'scope' and 'buf'", {
return FAIL;
});
- *scope = OPT_LOCAL;
- *req_scope = kOptScopeBuf;
+ *opt_flags = OPT_LOCAL;
+ *scope = kOptScopeBuf;
*from = find_buffer_by_handle(opts->buf, err);
if (ERROR_SET(err)) {
return FAIL;
@@ -81,10 +82,10 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
if (*opt_idxp == kOptInvalid) {
// unknown option
api_set_error(err, kErrorTypeValidation, "Unknown option '%s'", name);
- } else if (*req_scope == kOptScopeBuf || *req_scope == kOptScopeWin) {
+ } else if (*scope == kOptScopeBuf || *scope == kOptScopeWin) {
// if 'buf' or 'win' is passed, make sure the option supports it
- if (!option_has_scope(*opt_idxp, *req_scope)) {
- char *tgt = *req_scope == kOptScopeBuf ? "buf" : "win";
+ if (!option_has_scope(*opt_idxp, *scope)) {
+ char *tgt = *scope == kOptScopeBuf ? "buf" : "win";
char *global = option_has_scope(*opt_idxp, kOptScopeGlobal) ? "global " : "";
char *req = option_has_scope(*opt_idxp, kOptScopeBuf)
? "buffer-local "
@@ -95,7 +96,7 @@ static int validate_option_value_args(Dict(option) *opts, char *name, OptIndex *
}
}
- return OK;
+ return ERROR_SET(err) ? FAIL : OK;
#undef HAS_KEY_X
}
@@ -151,13 +152,13 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
FUNC_API_SINCE(9) FUNC_API_RET_ALLOC
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *from = NULL;
char *filetype = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, &filetype,
- err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from,
+ &filetype, err)) {
return (Object)OBJECT_INIT;
}
@@ -181,7 +182,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err)
from = ftbuf;
}
- OptVal value = get_option_value_for(opt_idx, scope, req_scope, from, err);
+ OptVal value = get_option_value_for(opt_idx, opt_flags, scope, from, err);
if (ftbuf != NULL) {
// restore curwin/curbuf and a few other things
@@ -224,10 +225,11 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
FUNC_API_SINCE(9)
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *to = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &to, NULL, err)) {
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &to, NULL,
+ err)) {
return;
}
@@ -237,9 +239,9 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
// - option is global or local to window (global-local)
//
// Then force scope to local since we don't want to change the global option
- if (req_scope == kOptScopeWin && scope == 0) {
+ if (scope == kOptScopeWin && opt_flags == 0) {
if (option_has_scope(opt_idx, kOptScopeGlobal)) {
- scope = OPT_LOCAL;
+ opt_flags = OPT_LOCAL;
}
}
@@ -255,7 +257,7 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict(
});
WITH_SCRIPT_CONTEXT(channel_id, {
- set_option_value_for(name.data, opt_idx, optval, scope, req_scope, to, err);
+ set_option_value_for(name.data, opt_idx, optval, opt_flags, scope, to, err);
});
}
@@ -310,16 +312,16 @@ Dict nvim_get_option_info2(String name, Dict(option) *opts, Arena *arena, Error
FUNC_API_SINCE(11)
{
OptIndex opt_idx = 0;
- int scope = 0;
- OptScope req_scope = kOptScopeGlobal;
+ int opt_flags = 0;
+ OptScope scope = kOptScopeGlobal;
void *from = NULL;
- if (!validate_option_value_args(opts, name.data, &opt_idx, &scope, &req_scope, &from, NULL,
+ if (!validate_option_value_args(opts, name.data, &opt_idx, &opt_flags, &scope, &from, NULL,
err)) {
return (Dict)ARRAY_DICT_INIT;
}
- buf_T *buf = (req_scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
- win_T *win = (req_scope == kOptScopeWin) ? (win_T *)from : curwin;
+ buf_T *buf = (scope == kOptScopeBuf) ? (buf_T *)from : curbuf;
+ win_T *win = (scope == kOptScopeWin) ? (win_T *)from : curwin;
- return get_vimoption(name, scope, buf, win, arena, err);
+ return get_vimoption(name, opt_flags, buf, win, arena, err);
}
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
index 59e7373f68..5f9d20ee73 100644
--- a/src/nvim/api/private/converter.c
+++ b/src/nvim/api/private/converter.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <lauxlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -7,7 +8,6 @@
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/typval.h"
@@ -15,6 +15,7 @@
#include "nvim/eval/userfunc.h"
#include "nvim/lua/executor.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h
index 26d5ac09a8..6dee86dcf5 100644
--- a/src/nvim/api/private/defs.h
+++ b/src/nvim/api/private/defs.h
@@ -21,6 +21,8 @@
# define Dict(name) KeyDict_##name
# define DictHash(name) KeyDict_##name##_get_field
# define DictKey(name)
+# define LuaRefOf(...) LuaRef
+# define Union(...) Object
# include "api/private/defs.h.inline.generated.h"
#endif
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 8ddaecc58e..c98635f8fd 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -31,19 +31,18 @@
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
-#include "nvim/ui.h"
-#include "nvim/ui_defs.h"
-#include "nvim/version.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/private/api_metadata.generated.h"
-# include "api/private/helpers.c.generated.h"
+# include "api/private/helpers.c.generated.h" // IWYU pragma: keep
#endif
/// Start block that may cause Vimscript exceptions while evaluating another code
///
-/// Used when caller is supposed to be operating when other Vimscript code is being
-/// processed and that “other Vimscript code†must not be affected.
+/// Used just in case caller is supposed to be operating when other Vimscript code
+/// is being processed and that “other Vimscript code†must not be affected.
+///
+/// @warning Avoid calling directly; use TRY_WRAP instead.
///
/// @param[out] tstate Location where try state should be saved.
void try_enter(TryState *const tstate)
@@ -55,74 +54,33 @@ void try_enter(TryState *const tstate)
.current_exception = current_exception,
.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,
};
+ // `msg_list` controls the collection of abort-causing non-exception errors,
+ // which would otherwise be ignored. This pattern is from do_cmdline().
msg_list = &tstate->private_msg_list;
current_exception = NULL;
- trylevel = 1;
got_int = false;
did_throw = false;
need_rethrow = false;
did_emsg = false;
-}
-
-/// End try block, set the error message if any and restore previous state
-///
-/// @warning Return is consistent with most functions (false on error), not with
-/// try_end (true on error).
-///
-/// @param[in] tstate Previous state to restore.
-/// @param[out] err Location where error should be saved.
-///
-/// @return false if error occurred, true otherwise.
-bool try_leave(const TryState *const tstate, Error *const err)
- FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
-{
- const bool ret = !try_end(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 = (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;
-}
-
-/// Start block that may cause vimscript exceptions
-///
-/// Each try_start() call should be mirrored by try_end() call.
-///
-/// To be used as a replacement of `:try … catch … endtry` in C code, in cases
-/// when error flag could not already be set. If there may be pending error
-/// state at the time try_start() is executed which needs to be preserved,
-/// try_enter()/try_leave() pair should be used instead.
-void try_start(void)
-{
trylevel++;
}
-/// End try block, set the error message if any and return true if an error
-/// occurred.
+/// Ends a `try_enter` block; sets error message if any.
///
-/// @param err Pointer to the stack-allocated error object
-/// @return true if an error occurred
-bool try_end(Error *err)
+/// @warning Avoid calling directly; use TRY_WRAP instead.
+///
+/// @param[out] err Pointer to the stack-allocated error object
+void try_leave(const TryState *const tstate, Error *const err)
+ FUNC_ATTR_NONNULL_ALL
{
// Note: all globals manipulated here should be saved/restored in
// try_enter/try_leave.
+ assert(trylevel > 0);
trylevel--;
// Set by emsg(), affects aborting(). See also enter_cleanup().
@@ -165,7 +123,20 @@ bool try_end(Error *err)
discard_current_exception();
}
- return ERROR_SET(err);
+ assert(msg_list == &tstate->private_msg_list);
+ assert(*msg_list == NULL);
+ assert(current_exception == NULL);
+ assert(!got_int);
+ assert(!did_throw);
+ assert(!need_rethrow);
+ assert(!did_emsg);
+ // Restore the exception context.
+ msg_list = (msglist_T **)tstate->msg_list;
+ current_exception = tstate->current_exception;
+ got_int = tstate->got_int;
+ did_throw = tstate->did_throw;
+ need_rethrow = tstate->need_rethrow;
+ did_emsg = tstate->did_emsg;
}
/// Recursively expands a vimscript value in a dict
@@ -805,7 +776,7 @@ char *api_typename(ObjectType t)
UNREACHABLE;
}
-HlMessage parse_hl_msg(Array chunks, Error *err)
+HlMessage parse_hl_msg(Array chunks, bool is_err, Error *err)
{
HlMessage hl_msg = KV_INITIAL_VALUE;
for (size_t i = 0; i < chunks.size; i++) {
@@ -820,7 +791,7 @@ HlMessage parse_hl_msg(Array chunks, Error *err)
String str = copy_string(chunk.items[0].data.string, NULL);
- int hl_id = 0;
+ int hl_id = is_err ? HLF_E : 0;
if (chunk.size == 2) {
hl_id = object_to_hl_id(chunk.items[1], "text highlight", err);
}
diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h
index d06f5c9c65..d581c6bc10 100644
--- a/src/nvim/api/private/helpers.h
+++ b/src/nvim/api/private/helpers.h
@@ -1,7 +1,7 @@
#pragma once
#include <stdbool.h>
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include "klib/kvec.h"
#include "nvim/api/private/defs.h" // IWYU pragma: keep
@@ -147,27 +147,19 @@ typedef struct {
except_T *current_exception;
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;
-// `msg_list` controls the collection of abort-causing non-exception errors,
-// which would otherwise be ignored. This pattern is from do_cmdline().
-//
// TODO(bfredl): prepare error-handling at "top level" (nv_event).
#define TRY_WRAP(err, code) \
do { \
- msglist_T **saved_msg_list = msg_list; \
- msglist_T *private_msg_list; \
- msg_list = &private_msg_list; \
- private_msg_list = NULL; \
- try_start(); \
+ TryState tstate; \
+ try_enter(&tstate); \
code; \
- try_end(err); \
- msg_list = saved_msg_list; /* Restore the exception context. */ \
+ try_leave(&tstate, err); \
} while (0)
// Execute code with cursor position saved and restored and textlock active.
diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c
index 56a3f1cf23..dce47cd99f 100644
--- a/src/nvim/api/tabpage.c
+++ b/src/nvim/api/tabpage.c
@@ -7,11 +7,12 @@
#include "nvim/api/vim.h"
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
-#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
+#include "nvim/types_defs.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/tabpage.c.generated.h"
+# include "api/tabpage.c.generated.h" // IWYU pragma: keep
#endif
/// Gets the windows in a tabpage
@@ -146,11 +147,9 @@ void nvim_tabpage_set_win(Tabpage tabpage, Window win, Error *err)
}
if (tp == curtab) {
- try_start();
- win_goto(wp);
- if (!try_end(err) && curwin != wp) {
- api_set_error(err, kErrorTypeException, "Failed to switch to window %d", win);
- }
+ TRY_WRAP(err, {
+ win_goto(wp);
+ });
} else if (tp->tp_curwin != wp) {
tp->tp_prevwin = tp->tp_curwin;
tp->tp_curwin = wp;
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index b09a9ed253..f6f32a73ed 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -23,7 +23,6 @@
#include "nvim/event/wstream.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
@@ -34,6 +33,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/option.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
@@ -537,7 +537,7 @@ static void prepare_call(RemoteUI *ui, const char *name)
ui_alloc_buf(ui);
}
- // To optimize data transfer(especially for "grid_line"), we bundle adjacent
+ // To optimize data transfer (especially for "grid_line"), we bundle adjacent
// calls to same method together, so only add a new call entry if the last
// method call is different from "name"
diff --git a/src/nvim/api/ui.h b/src/nvim/api/ui.h
index cdccc27ba4..3f996ec219 100644
--- a/src/nvim/api/ui.h
+++ b/src/nvim/api/ui.h
@@ -4,6 +4,7 @@
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/highlight_defs.h" // IWYU pragma: keep
+#include "nvim/macros_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
#include "nvim/ui_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 0ed208fc1a..74718e7ac5 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -136,13 +136,13 @@ void tabline_update(Tabpage current, Array tabs, Buffer current_buffer, Array bu
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_show(Array content, Integer pos, String firstc, String prompt, Integer indent,
- Integer level)
+ Integer level, Integer hl_id)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_pos(Integer pos, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_special_char(String c, Boolean shift, Integer level)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void cmdline_hide(Integer level)
+void cmdline_hide(Integer level, Boolean abort)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_block_show(Array lines)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
@@ -158,7 +158,7 @@ void wildmenu_select(Integer selected)
void wildmenu_hide(void)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void msg_show(String kind, Array content, Boolean replace_last)
+void msg_show(String kind, Array content, Boolean replace_last, Boolean history)
FUNC_API_SINCE(6) FUNC_API_FAST FUNC_API_REMOTE_ONLY;
void msg_clear(void)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 83f9aa573d..c103a56032 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -20,6 +20,7 @@
#include "nvim/api/vim.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/channel.h"
@@ -27,8 +28,8 @@
#include "nvim/context.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
+#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
@@ -44,6 +45,7 @@
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
+#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/log.h"
#include "nvim/lua/executor.h"
@@ -76,7 +78,6 @@
#include "nvim/runtime.h"
#include "nvim/sign_defs.h"
#include "nvim/state.h"
-#include "nvim/state_defs.h"
#include "nvim/statusline.h"
#include "nvim/statusline_defs.h"
#include "nvim/strings.h"
@@ -86,8 +87,6 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-#define LINE_BUFFER_MIN_SIZE 4096
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vim.c.generated.h"
#endif
@@ -518,26 +517,6 @@ Object nvim_exec_lua(String code, Array args, Arena *arena, Error *err)
return nlua_exec(code, args, kRetObject, arena, err);
}
-/// Notify the user with a message
-///
-/// Relays the call to vim.notify . By default forwards your message in the
-/// echo area but can be overridden to trigger desktop notifications.
-///
-/// @param msg Message to display to the user
-/// @param log_level The log level
-/// @param opts Reserved for future use.
-/// @param[out] err Error details, if any
-Object nvim_notify(String msg, Integer log_level, Dict opts, Arena *arena, Error *err)
- FUNC_API_SINCE(7)
-{
- MAXSIZE_TEMP_ARRAY(args, 3);
- ADD_C(args, STRING_OBJ(msg));
- ADD_C(args, INTEGER_OBJ(log_level));
- ADD_C(args, DICT_OBJ(opts));
-
- return NLUA_EXEC_STATIC("return vim.notify(...)", args, kRetObject, arena, err);
-}
-
/// Calculates the number of display cells occupied by `text`.
/// Control characters including [<Tab>] count as one cell.
///
@@ -594,11 +573,10 @@ ArrayOf(String) nvim_get_runtime_file(String name, Boolean all, Arena *arena, Er
kvi_init(cookie.rv);
int flags = DIP_DIRFILE | (all ? DIP_ALL : 0);
- TryState tstate;
- try_enter(&tstate);
- do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ do_in_runtimepath((name.size ? name.data : ""), flags, find_runtime_cb, &cookie);
+ });
return arena_take_arraybuilder(arena, &cookie.rv);
}
@@ -668,16 +646,9 @@ void nvim_set_current_dir(String dir, Error *err)
memcpy(string, dir.data, dir.size);
string[dir.size] = NUL;
- try_start();
-
- if (!changedir_func(string, kCdScopeGlobal)) {
- if (!try_end(err)) {
- api_set_error(err, kErrorTypeException, "Failed to change directory");
- }
- return;
- }
-
- try_end(err);
+ TRY_WRAP(err, {
+ changedir_func(string, kCdScopeGlobal);
+ });
}
/// Gets the current line.
@@ -776,20 +747,24 @@ void nvim_set_vvar(String name, Object value, Error *err)
dict_set_var(&vimvardict, name, value, false, false, NULL, err);
}
-/// Echo a message.
+/// Prints a message given by a list of `[text, hl_group]` "chunks".
///
-/// @param chunks A list of `[text, hl_group]` arrays, each representing a
-/// text chunk with specified highlight group name or ID.
-/// `hl_group` element can be omitted for no highlight.
+/// Example:
+/// ```lua
+/// vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {})
+/// ```
+///
+/// @param chunks List of `[text, hl_group]` pairs, where each is a `text` string highlighted by
+/// the (optional) name or ID `hl_group`.
/// @param history if true, add to |message-history|.
/// @param opts Optional parameters.
-/// - verbose: Message is printed as a result of 'verbose' option.
-/// If Nvim was invoked with -V3log_file, the message will be
-/// redirected to the log_file and suppressed from direct output.
+/// - err: Treat the message like `:echoerr`. Sets `hl_group` to |hl-ErrorMsg| by default.
+/// - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log`
+/// will write the message to the "log" file instead of standard output.
void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
FUNC_API_SINCE(7)
{
- HlMessage hl_msg = parse_hl_msg(chunks, err);
+ HlMessage hl_msg = parse_hl_msg(chunks, opts->err, err);
if (ERROR_SET(err)) {
goto error;
}
@@ -798,7 +773,8 @@ void nvim_echo(Array chunks, Boolean history, Dict(echo_opts) *opts, Error *err)
verbose_enter();
}
- msg_multihl(hl_msg, history ? "echomsg" : "echo", history);
+ char *kind = opts->verbose ? NULL : opts->err ? "echoerr" : history ? "echomsg" : "echo";
+ msg_multihl(hl_msg, kind, history, opts->err);
if (opts->verbose) {
verbose_leave();
@@ -814,37 +790,6 @@ error:
hl_msg_free(hl_msg);
}
-/// Writes a message to the Vim output buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_out_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, false, false);
-}
-
-/// Writes a message to the Vim error buffer. Does not append "\n", the
-/// message is buffered (won't display) until a linefeed is written.
-///
-/// @param str Message
-void nvim_err_write(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, false);
-}
-
-/// Writes a message to the Vim error buffer. Appends "\n", so the buffer is
-/// flushed (and displayed).
-///
-/// @param str Message
-/// @see nvim_err_write()
-void nvim_err_writeln(String str)
- FUNC_API_SINCE(1)
-{
- write_msg(str, true, true);
-}
-
/// Gets the current list of buffer handles
///
/// Includes unlisted (unloaded/deleted) buffers, like `:ls!`.
@@ -892,19 +837,9 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
return;
}
- if (curwin->w_p_wfb) {
- api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
- return;
- }
-
- try_start();
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
- if (!try_end(err) && result == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to buffer %d",
- buffer);
- }
+ TRY_WRAP(err, {
+ do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
+ });
}
/// Gets the current list of window handles.
@@ -951,14 +886,9 @@ void nvim_set_current_win(Window window, Error *err)
return;
}
- try_start();
- goto_tabpage_win(win_find_tabpage(win), win);
- if (!try_end(err) && win != curwin) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to window %d",
- window);
- }
+ TRY_WRAP(err, {
+ goto_tabpage_win(win_find_tabpage(win), win);
+ });
}
/// Creates a new, empty, unnamed buffer.
@@ -973,74 +903,76 @@ void nvim_set_current_win(Window window, Error *err)
Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err)
FUNC_API_SINCE(6)
{
- try_start();
- // Block autocommands for now so they don't mess with the buffer before we
- // finish configuring it.
- block_autocmds();
-
- buf_T *buf = buflist_new(NULL, NULL, 0,
- BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
- if (buf == NULL) {
- unblock_autocmds();
- goto fail;
- }
+ Buffer ret = 0;
- // Open the memline for the buffer. This will avoid spurious autocmds when
- // a later nvim_buf_set_lines call would have needed to "open" the buffer.
- if (ml_open(buf) == FAIL) {
- unblock_autocmds();
- goto fail;
- }
-
- // Set last_changedtick to avoid triggering a TextChanged autocommand right
- // after it was added.
- buf->b_last_changedtick = buf_get_changedtick(buf);
- buf->b_last_changedtick_i = buf_get_changedtick(buf);
- buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+ TRY_WRAP(err, {
+ // Block autocommands for now so they don't mess with the buffer before we
+ // finish configuring it.
+ block_autocmds();
+
+ buf_T *buf = buflist_new(NULL, NULL, 0,
+ BLN_NOOPT | BLN_NEW | (listed ? BLN_LISTED : 0));
+ if (buf == NULL) {
+ unblock_autocmds();
+ goto fail;
+ }
- // Only strictly needed for scratch, but could just as well be consistent
- // and do this now. Buffer is created NOW, not when it later first happens
- // to reach a window or aucmd_prepbuf() ..
- buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
+ // Open the memline for the buffer. This will avoid spurious autocmds when
+ // a later nvim_buf_set_lines call would have needed to "open" the buffer.
+ if (ml_open(buf) == FAIL) {
+ unblock_autocmds();
+ goto fail;
+ }
- if (scratch) {
- set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
- kOptScopeBuf, buf);
- set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
- kOptScopeBuf, buf);
- assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
- buf->b_p_swf = false;
- buf->b_p_ml = false;
- }
+ // Set last_changedtick to avoid triggering a TextChanged autocommand right
+ // after it was added.
+ buf->b_last_changedtick = buf_get_changedtick(buf);
+ buf->b_last_changedtick_i = buf_get_changedtick(buf);
+ buf->b_last_changedtick_pum = buf_get_changedtick(buf);
+
+ // Only strictly needed for scratch, but could just as well be consistent
+ // and do this now. Buffer is created NOW, not when it later first happens
+ // to reach a window or aucmd_prepbuf() ..
+ buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
+
+ if (scratch) {
+ set_option_direct_for(kOptBufhidden, STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ set_option_direct_for(kOptBuftype, STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL, 0,
+ kOptScopeBuf, buf);
+ assert(buf->b_ml.ml_mfp->mf_fd < 0); // ml_open() should not have opened swapfile already
+ buf->b_p_swf = false;
+ buf->b_p_ml = false;
+ }
- unblock_autocmds();
+ unblock_autocmds();
- bufref_T bufref;
- set_bufref(&bufref, buf);
- if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
- && !bufref_valid(&bufref)) {
- goto fail;
- }
- if (listed
- && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
- && !bufref_valid(&bufref)) {
- goto fail;
- }
+ bufref_T bufref;
+ set_bufref(&bufref, buf);
+ if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, false, buf)
+ && !bufref_valid(&bufref)) {
+ goto fail;
+ }
+ if (listed
+ && apply_autocmds(EVENT_BUFADD, NULL, NULL, false, buf)
+ && !bufref_valid(&bufref)) {
+ goto fail;
+ }
- try_end(err);
- return buf->b_fnum;
+ ret = buf->b_fnum;
+ fail:;
+ });
-fail:
- if (!try_end(err)) {
+ if (ret == 0 && !ERROR_SET(err)) {
api_set_error(err, kErrorTypeException, "Failed to create buffer");
}
- return 0;
+ return ret;
}
/// Open a terminal instance in a buffer
///
/// By default (and currently the only option) the terminal will not be
-/// connected to an external process. Instead, input send on the channel
+/// connected to an external process. Instead, input sent on the channel
/// will be echoed directly by the terminal. This is useful to display
/// ANSI terminal sequences returned as part of a rpc message, or similar.
///
@@ -1051,6 +983,19 @@ fail:
/// Then |nvim_chan_send()| can be called immediately to process sequences
/// in a virtual terminal having the intended size.
///
+/// Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you
+/// can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]()
+/// [terminal-scrollback-pager]()
+///
+/// ```lua
+/// vim.api.nvim_create_user_command('TermHl', function()
+/// local b = vim.api.nvim_create_buf(false, true)
+/// local chan = vim.api.nvim_open_term(b, {})
+/// vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n'))
+/// vim.api.nvim_win_set_buf(0, b)
+/// end, { desc = 'Highlights ANSI termcodes in curbuf' })
+/// ```
+///
/// @param buffer the buffer to use (expected to be empty)
/// @param opts Optional parameters.
/// - on_input: Lua callback for input sent, i e keypresses in terminal
@@ -1205,14 +1150,9 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err)
return;
}
- try_start();
- goto_tabpage_tp(tp, true, true);
- if (!try_end(err) && tp != curtab) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to tabpage %d",
- tabpage);
- }
+ TRY_WRAP(err, {
+ goto_tabpage_tp(tp, true, true);
+ });
}
/// Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat the input. UIs call
@@ -1545,20 +1485,17 @@ Array nvim_get_api_info(uint64_t channel_id, Arena *arena)
return rv;
}
-/// Self-identifies the client.
+/// Self-identifies the client. Sets the `client` object returned by |nvim_get_chan_info()|.
///
-/// The client/plugin/application should call this after connecting, to provide
-/// hints about its identity and purpose, for debugging and orchestration.
+/// Clients should call this just after connecting, to provide hints for debugging and
+/// orchestration. (Note: Something is better than nothing! Fields are optional, but at least set
+/// `name`.)
///
-/// Can be called more than once; the caller should merge old info if
-/// appropriate. Example: library first identifies the channel, then a plugin
-/// using that library later identifies itself.
-///
-/// @note "Something is better than nothing". You don't need to include all the
-/// fields.
+/// Can be called more than once; the caller should merge old info if appropriate. Example: library
+/// first identifies the channel, then a plugin using that library later identifies itself.
///
/// @param channel_id
-/// @param name Short name for the connected client
+/// @param name Client short-name. Sets the `client.name` field of |nvim_get_chan_info()|.
/// @param version Dict describing the version, with these
/// (optional) keys:
/// - "major" major version (defaults to 0 if not set, for no release yet)
@@ -1632,6 +1569,8 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String
/// Gets information about a channel.
///
+/// See |nvim_list_uis()| for an example of how to get channel info.
+///
/// @param chan channel_id, or 0 for current channel
/// @returns Channel info dict with these keys:
/// - "id" Channel id.
@@ -1649,8 +1588,8 @@ void nvim_set_client_info(uint64_t channel_id, String name, Dict version, String
/// "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g.
/// for conpty on Windows).
/// - "buffer" (optional) Buffer connected to |terminal| instance.
-/// - "client" (optional) Info about the peer (client on the other end of the RPC channel),
-/// which it provided via |nvim_set_client_info()|.
+/// - "client" (optional) Info about the peer (client on the other end of the channel), as set
+/// by |nvim_set_client_info()|.
///
Dict nvim_get_chan_info(uint64_t channel_id, Integer chan, Arena *arena, Error *err)
FUNC_API_SINCE(4)
@@ -1676,55 +1615,6 @@ Array nvim_list_chans(Arena *arena)
return channel_all_info(arena);
}
-/// Writes a message to vim output or error buffer. The string is split
-/// and flushed after each newline. Incomplete lines are kept for writing
-/// later.
-///
-/// @param message Message to write
-/// @param to_err true: message is an error (uses `emsg` instead of `msg`)
-/// @param writeln Append a trailing newline
-static void write_msg(String message, bool to_err, bool writeln)
-{
- static StringBuilder out_line_buf = KV_INITIAL_VALUE;
- static StringBuilder err_line_buf = KV_INITIAL_VALUE;
- StringBuilder *line_buf = to_err ? &err_line_buf : &out_line_buf;
-
-#define PUSH_CHAR(c) \
- if (kv_max(*line_buf) == 0) { \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } \
- if (c == NL) { \
- kv_push(*line_buf, NUL); \
- if (to_err) { \
- emsg(line_buf->items); \
- } else { \
- msg(line_buf->items, 0); \
- } \
- if (msg_silent == 0) { \
- msg_didout = true; \
- } \
- kv_drop(*line_buf, kv_size(*line_buf)); \
- kv_resize(*line_buf, LINE_BUFFER_MIN_SIZE); \
- } else if (c == NUL) { \
- kv_push(*line_buf, NL); \
- } else { \
- kv_push(*line_buf, c); \
- }
-
- no_wait_return++;
- for (uint32_t i = 0; i < message.size; i++) {
- if (got_int) {
- break;
- }
- PUSH_CHAR(message.data[i]);
- }
- if (writeln) {
- PUSH_CHAR(NL);
- }
- no_wait_return--;
- msg_end();
-}
-
// Functions used for testing purposes
/// Returns object given as argument.
@@ -1796,6 +1686,14 @@ Dict nvim__stats(Arena *arena)
/// Gets a list of dictionaries representing attached UIs.
///
+/// Example: The Nvim builtin |TUI| sets its channel info as described in |startup-tui|. In
+/// particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by
+/// inspecting the client name of each UI:
+///
+/// ```lua
+/// vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name)
+/// ```
+///
/// @return Array of UI dictionaries, each with these keys:
/// - "height" Requested height of the UI
/// - "width" Requested width of the UI
@@ -2086,7 +1984,9 @@ Array nvim_get_mark(String name, Dict(empty) *opts, Arena *arena, Error *err)
/// the "highlights" key in {opts} is true. Each element of the array is a
/// |Dict| with these keys:
/// - start: (number) Byte index (0-based) of first character that uses the highlight.
-/// - group: (string) Name of highlight group.
+/// - group: (string) Name of highlight group. May be removed in the future, use
+/// `groups` instead.
+/// - groups: (array) Names of stacked highlight groups (highest priority last).
Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena, Error *err)
FUNC_API_SINCE(8) FUNC_API_FAST
{
@@ -2138,6 +2038,7 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
});
int stc_hl_id = 0;
+ int scl_hl_id = 0;
statuscol_T statuscol = { 0 };
SignTextAttrs sattrs[SIGN_SHOW_MAX] = { 0 };
@@ -2146,23 +2047,18 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
int cul_id = 0;
int num_id = 0;
linenr_T lnum = statuscol_lnum;
+ foldinfo_T cursorline_fi = { 0 };
decor_redraw_signs(wp, wp->w_buffer, lnum - 1, sattrs, &line_id, &cul_id, &num_id);
statuscol.sattrs = sattrs;
statuscol.foldinfo = fold_info(wp, lnum);
- wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+ win_update_cursorline(wp, &cursorline_fi);
+ statuscol.sign_cul_id = use_cursor_line_highlight(wp, lnum) ? cul_id : 0;
+ scl_hl_id = use_cursor_line_highlight(wp, lnum) ? HLF_CLS : HLF_SC;
- if (wp->w_p_cul) {
- if (statuscol.foldinfo.fi_level != 0 && statuscol.foldinfo.fi_lines > 0) {
- wp->w_cursorline = statuscol.foldinfo.fi_lnum;
- }
- statuscol.use_cul = lnum == wp->w_cursorline && (wp->w_p_culopt_flags & CULOPT_NBR);
- }
-
- statuscol.sign_cul_id = statuscol.use_cul ? cul_id : 0;
if (num_id) {
stc_hl_id = num_id;
- } else if (statuscol.use_cul) {
+ } else if (use_cursor_line_highlight(wp, lnum)) {
stc_hl_id = HLF_CLN;
} else if (wp->w_p_rnu) {
stc_hl_id = (lnum < wp->w_cursor.lnum ? HLF_LNA : HLF_LNB);
@@ -2215,22 +2111,19 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
// If first character doesn't have a defined highlight,
// add the default highlight at the beginning of the highlight list
+ const char *dfltname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
+ opts->use_winbar, stc_hl_id);
if (hltab->start == NULL || (hltab->start - buf) != 0) {
- Dict hl_info = arena_dict(arena, 2);
- const char *grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp,
- opts->use_winbar, stc_hl_id);
-
+ Dict hl_info = arena_dict(arena, 3);
PUT_C(hl_info, "start", INTEGER_OBJ(0));
- PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
-
+ PUT_C(hl_info, "group", CSTR_AS_OBJ(dfltname));
+ Array groups = arena_array(arena, 1);
+ ADD_C(groups, CSTR_AS_OBJ(dfltname));
+ PUT_C(hl_info, "groups", ARRAY_OBJ(groups));
ADD_C(hl_values, DICT_OBJ(hl_info));
}
for (stl_hlrec_t *sp = hltab; sp->start != NULL; sp++) {
- Dict hl_info = arena_dict(arena, 2);
-
- PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
-
const char *grpname;
if (sp->userhl == 0) {
grpname = get_default_stl_hl(opts->use_tabline ? NULL : wp, opts->use_winbar, stc_hl_id);
@@ -2240,7 +2133,18 @@ Dict nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Arena *arena,
snprintf(user_group, sizeof(user_group), "User%d", sp->userhl);
grpname = arena_memdupz(arena, user_group, strlen(user_group));
}
+
+ const char *combine = sp->item == STL_SIGNCOL ? syn_id2name(scl_hl_id)
+ : sp->item == STL_FOLDCOL ? grpname : dfltname;
+ Dict hl_info = arena_dict(arena, 3);
+ PUT_C(hl_info, "start", INTEGER_OBJ(sp->start - buf));
PUT_C(hl_info, "group", CSTR_AS_OBJ(grpname));
+ Array groups = arena_array(arena, 1 + (combine != grpname));
+ if (combine != grpname) {
+ ADD_C(groups, CSTR_AS_OBJ(combine));
+ }
+ ADD_C(groups, CSTR_AS_OBJ(grpname));
+ PUT_C(hl_info, "groups", ARRAY_OBJ(groups));
ADD_C(hl_values, DICT_OBJ(hl_info));
}
PUT_C(result, "highlights", ARRAY_OBJ(hl_values));
@@ -2270,9 +2174,13 @@ void nvim_error_event(uint64_t channel_id, Integer lvl, String data)
/// @return Dict containing these keys:
/// - winid: (number) floating window id
/// - bufnr: (number) buffer id in floating window
-Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena)
+Dict nvim__complete_set(Integer index, Dict(complete_set) *opts, Arena *arena, Error *err)
{
Dict rv = arena_dict(arena, 2);
+ if ((get_cot_flags() & kOptCotFlagPopup) == 0) {
+ api_set_error(err, kErrorTypeException, "completeopt option does not include popup");
+ return rv;
+ }
if (HAS_KEY(opts, complete_set, info)) {
win_T *wp = pum_set_info((int)index, opts->info.data);
if (wp) {
@@ -2389,13 +2297,23 @@ void nvim__redraw(Dict(redraw) *opts, Error *err)
"%s", "Invalid 'range': Expected 2-tuple of Integers", {
return;
});
- linenr_T first = (linenr_T)kv_A(opts->range, 0).data.integer + 1;
- linenr_T last = (linenr_T)kv_A(opts->range, 1).data.integer;
+ int64_t begin_raw = kv_A(opts->range, 0).data.integer;
+ int64_t end_raw = kv_A(opts->range, 1).data.integer;
+
buf_T *rbuf = win ? win->w_buffer : (buf ? buf : curbuf);
- if (last == -1) {
- last = rbuf->b_ml.ml_line_count;
+ linenr_T line_count = rbuf->b_ml.ml_line_count;
+
+ int begin = (int)MIN(begin_raw, line_count);
+ int end;
+ if (end_raw == -1) {
+ end = line_count;
+ } else {
+ end = (int)MIN(MAX(begin, end_raw), line_count);
+ }
+
+ if (begin < end) {
+ redraw_buf_range_later(rbuf, 1 + begin, end);
}
- redraw_buf_range_later(rbuf, first, last);
}
// Redraw later types require update_screen() so call implicitly unless set to false.
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 165cc93fbe..fc7e7e1a06 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
#include <string.h>
#include "klib/kvec.h"
@@ -21,6 +22,7 @@
#include "nvim/garray_defs.h"
#include "nvim/globals.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/runtime.h"
#include "nvim/vim_defs.h"
#include "nvim/viml/parser/expressions.h"
@@ -78,24 +80,24 @@ String exec_impl(uint64_t channel_id, String src, Dict(exec_opts) *opts, Error *
capture_ga = &capture_local;
}
- try_start();
- if (opts->output) {
- msg_silent++;
- msg_col = 0; // prevent leading spaces
- }
+ TRY_WRAP(err, {
+ if (opts->output) {
+ msg_silent++;
+ msg_col = 0; // prevent leading spaces
+ }
- const sctx_T save_current_sctx = api_set_sctx(channel_id);
+ const sctx_T save_current_sctx = api_set_sctx(channel_id);
- do_source_str(src.data, "nvim_exec2()");
- if (opts->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;
- }
+ do_source_str(src.data, "nvim_exec2()");
+ if (opts->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;
- try_end(err);
+ current_sctx = save_current_sctx;
+ });
if (ERROR_SET(err)) {
goto theend;
@@ -125,19 +127,17 @@ theend:
///
/// On execution error: fails with Vimscript error, updates v:errmsg.
///
-/// Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate multiple lines of Vim script
-/// or an Ex command directly, use |nvim_exec2()|. To construct an Ex command using a structured
-/// format and then execute it, use |nvim_cmd()|. To modify an Ex command before evaluating it, use
-/// |nvim_parse_cmd()| in conjunction with |nvim_cmd()|.
+/// Prefer |nvim_cmd()| or |nvim_exec2()| instead. To modify an Ex command in a structured way
+/// before executing it, modify the result of |nvim_parse_cmd()| then pass it to |nvim_cmd()|.
///
/// @param command Ex command string
/// @param[out] err Error details (Vim error), if any
void nvim_command(String command, Error *err)
FUNC_API_SINCE(1)
{
- try_start();
- do_cmdline_cmd(command.data);
- try_end(err);
+ TRY_WRAP(err, {
+ do_cmdline_cmd(command.data);
+ });
}
/// Evaluates a Vimscript |expression|. Dicts and Lists are recursively expanded.
@@ -231,10 +231,9 @@ static Object _call_function(String fn, Array args, dict_T *self, Arena *arena,
funcexe.fe_selfdict = self;
TRY_WRAP(err, {
- // call_func() retval is deceptive, ignore it. Instead we set `msg_list`
- // (see above) to capture abort-causing non-exception errors.
- call_func(fn.data, (int)fn.size, &rettv, (int)args.size,
- vim_args, &funcexe);
+ // call_func() retval is deceptive, ignore it. Instead TRY_WRAP sets `msg_list` to capture
+ // abort-causing non-exception errors.
+ (void)call_func(fn.data, (int)fn.size, &rettv, (int)args.size, vim_args, &funcexe);
});
if (!ERROR_SET(err)) {
@@ -282,20 +281,23 @@ Object nvim_call_dict_function(Object dict, String fn, Array args, Arena *arena,
typval_T rettv;
bool mustfree = false;
switch (dict.type) {
- case kObjectTypeString:
- try_start();
- if (eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE) == FAIL) {
- api_set_error(err, kErrorTypeException,
- "Failed to evaluate dict expression");
- }
- clear_evalarg(&EVALARG_EVALUATE, NULL);
- if (try_end(err)) {
+ case kObjectTypeString: {
+ int eval_ret;
+ TRY_WRAP(err, {
+ eval_ret = eval0(dict.data.string.data, &rettv, NULL, &EVALARG_EVALUATE);
+ clear_evalarg(&EVALARG_EVALUATE, NULL);
+ });
+ if (ERROR_SET(err)) {
return rv;
}
+ if (eval_ret != OK) {
+ abort(); // Should not happen.
+ }
// Evaluation of the string arg created a new dict or increased the
// refcount of a dict. Not necessary for a RPC dict.
mustfree = true;
break;
+ }
case kObjectTypeDict:
object_to_vim(dict, &rettv, err);
break;
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 6f5a9a90c0..1132452faf 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -1,3 +1,4 @@
+#include <assert.h>
#include <stdbool.h>
#include <string.h>
@@ -7,25 +8,22 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/tabpage.h"
#include "nvim/api/win_config.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
-#include "nvim/decoration.h"
#include "nvim/decoration_defs.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
#include "nvim/eval/window.h"
-#include "nvim/extmark_defs.h"
#include "nvim/globals.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
@@ -33,7 +31,6 @@
#include "nvim/syntax.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/ui_compositor.h"
#include "nvim/ui_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -104,10 +101,12 @@
/// @param config Map defining the window configuration. Keys:
/// - relative: Sets the window layout to "floating", placed at (row,col)
/// coordinates relative to:
-/// - "editor" The global editor grid
-/// - "win" Window given by the `win` field, or current window.
-/// - "cursor" Cursor position in current window.
-/// - "mouse" Mouse position
+/// - "cursor" Cursor position in current window.
+/// - "editor" The global editor grid.
+/// - "laststatus" 'laststatus' if present, or last row.
+/// - "mouse" Mouse position.
+/// - "tabline" Tabline if present, or first row.
+/// - "win" Window given by the `win` field, or current window.
/// - win: |window-ID| window to split, or relative window when creating a
/// float (relative="win").
/// - anchor: Decides which corner of the float to place at (row,col):
@@ -702,7 +701,9 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
FUNC_API_SINCE(6)
{
/// Keep in sync with FloatRelative in buffer_defs.h
- static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" };
+ static const char *const float_relative_str[] = {
+ "editor", "win", "cursor", "mouse", "tabline", "laststatus"
+ };
/// Keep in sync with WinSplit in buffer_defs.h
static const char *const win_split_str[] = { "left", "right", "above", "below" };
@@ -808,6 +809,10 @@ static bool parse_float_relative(String relative, FloatRelative *out)
*out = kFloatRelativeCursor;
} else if (striequal(str, "mouse")) {
*out = kFloatRelativeMouse;
+ } else if (striequal(str, "tabline")) {
+ *out = kFloatRelativeTabline;
+ } else if (striequal(str, "laststatus")) {
+ *out = kFloatRelativeLaststatus;
} else {
return false;
}
@@ -890,7 +895,7 @@ static void parse_bordertext(Object bordertext, BorderTextType bordertext_type,
*is_present = true;
}
-static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertext_type,
+static bool parse_bordertext_pos(win_T *wp, String bordertext_pos, BorderTextType bordertext_type,
WinConfig *fconfig, Error *err)
{
AlignTextPos *align;
@@ -904,7 +909,9 @@ static bool parse_bordertext_pos(String bordertext_pos, BorderTextType bordertex
}
if (bordertext_pos.size == 0) {
- *align = kAlignLeft;
+ if (!wp) {
+ *align = kAlignLeft;
+ }
return true;
}
@@ -1245,7 +1252,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
}
// handles unset 'title_pos' same as empty string
- if (!parse_bordertext_pos(config->title_pos, kBorderTextTitle, fconfig, err)) {
+ if (!parse_bordertext_pos(wp, config->title_pos, kBorderTextTitle, fconfig, err)) {
goto fail;
}
} else {
@@ -1272,7 +1279,7 @@ static bool parse_win_config(win_T *wp, Dict(win_config) *config, WinConfig *fco
}
// handles unset 'footer_pos' same as empty string
- if (!parse_bordertext_pos(config->footer_pos, kBorderTextFooter, fconfig, err)) {
+ if (!parse_bordertext_pos(wp, config->footer_pos, kBorderTextFooter, fconfig, err)) {
goto fail;
}
} else {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index 5a4972ef23..d968af421d 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -1,4 +1,3 @@
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
@@ -28,7 +27,7 @@
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "api/window.c.generated.h"
+# include "api/window.c.generated.h" // IWYU pragma: keep
#endif
/// Gets the current buffer in a window
@@ -63,11 +62,6 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
return;
}
- if (win->w_p_wfb) {
- api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
- return;
- }
-
if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return;
@@ -187,14 +181,9 @@ void nvim_win_set_height(Window window, Integer height, Error *err)
return;
}
- if (height > INT_MAX || height < INT_MIN) {
- api_set_error(err, kErrorTypeValidation, "Height value outside range");
- return;
- }
-
- try_start();
- win_setheight_win((int)height, win);
- try_end(err);
+ TRY_WRAP(err, {
+ win_setheight_win((int)height, win);
+ });
}
/// Gets the window width
@@ -229,14 +218,9 @@ void nvim_win_set_width(Window window, Integer width, Error *err)
return;
}
- if (width > INT_MAX || width < INT_MIN) {
- api_set_error(err, kErrorTypeValidation, "Width value outside range");
- return;
- }
-
- try_start();
- win_setwidth_win((int)width, win);
- try_end(err);
+ TRY_WRAP(err, {
+ win_setwidth_win((int)width, win);
+ });
}
/// Gets a window-scoped (w:) variable
@@ -383,19 +367,16 @@ void nvim_win_hide(Window window, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- TryState tstate;
- try_enter(&tstate);
-
- // Never close the autocommand window.
- if (is_aucmd_win(win)) {
- emsg(_(e_autocmd_close));
- } else if (tabpage == curtab) {
- win_close(win, false, false);
- } else {
- win_close_othertab(win, false, tabpage);
- }
-
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ // Never close the autocommand window.
+ if (is_aucmd_win(win)) {
+ emsg(_(e_autocmd_close));
+ } else if (tabpage == curtab) {
+ win_close(win, false, false);
+ } else {
+ win_close_othertab(win, false, tabpage);
+ }
+ });
}
/// Closes the window (like |:close| with a |window-ID|).
@@ -415,10 +396,9 @@ void nvim_win_close(Window window, Boolean force, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- TryState tstate;
- try_enter(&tstate);
- ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
- vim_ignored = try_leave(&tstate, err);
+ TRY_WRAP(err, {
+ ex_win_close(force, win, tabpage == curtab ? NULL : tabpage);
+ });
}
/// Calls a function with window as temporary current window.
@@ -441,15 +421,15 @@ Object nvim_win_call(Window window, LuaRef fun, Error *err)
}
tabpage_T *tabpage = win_find_tabpage(win);
- try_start();
Object res = OBJECT_INIT;
- win_execute_T win_execute_args;
- if (win_execute_before(&win_execute_args, win, tabpage)) {
- Array args = ARRAY_DICT_INIT;
- res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
- }
- win_execute_after(&win_execute_args);
- try_end(err);
+ TRY_WRAP(err, {
+ win_execute_T win_execute_args;
+ if (win_execute_before(&win_execute_args, win, tabpage)) {
+ Array args = ARRAY_DICT_INIT;
+ res = nlua_call_ref(fun, NULL, args, kRetLuaref, NULL, err);
+ }
+ win_execute_after(&win_execute_args);
+ });
return res;
}
@@ -476,6 +456,7 @@ void nvim_win_set_hl_ns(Window window, Integer ns_id, Error *err)
}
win->w_ns_hl = (NS)ns_id;
+ win->w_ns_hl_winhl = -1;
win->w_hl_needs_update = true;
redraw_later(win, UPD_NOT_VALID);
}
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index bb639edc07..361bb8db12 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -31,6 +31,7 @@
#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
+#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/os/input.h"
@@ -1096,6 +1097,10 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
tabpage_T *const new_lu_tp = curtab;
+ // Stop Visual mode, the cursor and "VIsual" may very well be invalid after
+ // switching to another buffer.
+ reset_VIsual_and_resel();
+
// 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.
diff --git a/src/nvim/ascii_defs.h b/src/nvim/ascii_defs.h
index 155a18fb95..86187b553c 100644
--- a/src/nvim/ascii_defs.h
+++ b/src/nvim/ascii_defs.h
@@ -103,6 +103,15 @@ static inline bool ascii_iswhite_or_nul(int c)
return ascii_iswhite(c) || c == NUL;
}
+/// Checks if `c` is a space or tab or newline character or NUL.
+///
+/// @see {ascii_isdigit}
+static inline bool ascii_iswhite_nl_or_nul(int c)
+ FUNC_ATTR_CONST FUNC_ATTR_ALWAYS_INLINE
+{
+ return ascii_iswhite(c) || c == '\n' || c == NUL;
+}
+
/// Check whether character is a decimal digit.
///
/// Library isdigit() function is officially locale-dependent and, for
diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c
index c08ef7a4c1..96da4695de 100644
--- a/src/nvim/autocmd.c
+++ b/src/nvim/autocmd.c
@@ -30,8 +30,8 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/hashtab.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/lua/executor.h"
@@ -42,7 +42,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -75,7 +74,6 @@ static const char e_autocommand_nesting_too_deep[]
// Naming Conventions:
// - general autocmd behavior start with au_
// - AutoCmd start with aucmd_
-// - Autocmd.exec stat with aucmd_exec
// - AutoPat start with aupat_
// - Groups start with augroup_
// - Events start with event_
@@ -255,24 +253,24 @@ static void au_show_for_event(int group, event_T event, const char *pat)
return;
}
- char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
+ char *handler_str = aucmd_handler_to_string(ac);
if (ac->desc != NULL) {
size_t msglen = 100;
char *msg = xmallocz(msglen);
- if (ac->exec.type == CALLABLE_CB) {
- msg_puts_hl(exec_to_string, HLF_8, false);
- snprintf(msg, msglen, " [%s]", ac->desc);
+ if (ac->handler_cmd) {
+ snprintf(msg, msglen, "%s [%s]", handler_str, ac->desc);
} else {
- snprintf(msg, msglen, "%s [%s]", exec_to_string, ac->desc);
+ msg_puts_hl(handler_str, HLF_8, false);
+ snprintf(msg, msglen, " [%s]", ac->desc);
}
msg_outtrans(msg, 0, false);
XFREE_CLEAR(msg);
- } else if (ac->exec.type == CALLABLE_CB) {
- msg_puts_hl(exec_to_string, HLF_8, false);
+ } else if (ac->handler_cmd) {
+ msg_outtrans(handler_str, 0, false);
} else {
- msg_outtrans(exec_to_string, 0, false);
+ msg_puts_hl(handler_str, HLF_8, false);
}
- XFREE_CLEAR(exec_to_string);
+ XFREE_CLEAR(handler_str);
if (p_verbose > 0) {
last_set_msg(ac->script_ctx);
}
@@ -304,7 +302,11 @@ static void aucmd_del(AutoCmd *ac)
xfree(ac->pat);
}
ac->pat = NULL;
- aucmd_exec_free(&ac->exec);
+ if (ac->handler_cmd) {
+ XFREE_CLEAR(ac->handler_cmd);
+ } else {
+ callback_free(&ac->handler_fn);
+ }
XFREE_CLEAR(ac->desc);
au_need_clean = true;
@@ -900,8 +902,8 @@ void do_all_autocmd_events(const char *pat, bool once, int nested, char *cmd, bo
// If *cmd == NUL: show entries.
// If forceit == true: delete entries.
// If group is not AUGROUP_ALL: only use this group.
-int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char *cmd, bool del,
- int group)
+int do_autocmd_event(event_T event, const char *pat, bool once, int nested, const char *cmd,
+ bool del, int group)
FUNC_ATTR_NONNULL_ALL
{
// Cannot be used to show all patterns. See au_show_for_event or au_show_for_all_events
@@ -959,10 +961,8 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char
}
if (is_adding_cmd) {
- AucmdExecutable exec = AUCMD_EXECUTABLE_INIT;
- exec.type = CALLABLE_EX;
- exec.callable.cmd = cmd;
- autocmd_register(0, event, pat, patlen, group, once, nested, NULL, exec);
+ Callback handler_fn = CALLBACK_INIT;
+ autocmd_register(0, event, pat, patlen, group, once, nested, NULL, cmd, &handler_fn);
}
pat = aucmd_next_pattern(pat, (size_t)patlen);
@@ -973,8 +973,13 @@ int do_autocmd_event(event_T event, const char *pat, bool once, int nested, char
return OK;
}
+/// Registers an autocmd. The handler may be a Ex command or callback function, decided by
+/// the `handler_cmd` or `handler_fn` args.
+///
+/// @param handler_cmd Handler Ex command, or NULL if handler is a function (`handler_fn`).
+/// @param handler_fn Handler function, ignored if `handler_cmd` is not NULL.
int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int group, bool once,
- bool nested, char *desc, AucmdExecutable aucmd)
+ bool nested, char *desc, const char *handler_cmd, Callback *handler_fn)
{
// 0 is not a valid group.
assert(group != 0);
@@ -1082,7 +1087,12 @@ int autocmd_register(int64_t id, event_T event, const char *pat, int patlen, int
AutoCmd *ac = kv_pushp(autocmds[(int)event]);
ac->pat = ap;
ac->id = id;
- ac->exec = aucmd_exec_copy(aucmd);
+ if (handler_cmd) {
+ ac->handler_cmd = xstrdup(handler_cmd);
+ } else {
+ ac->handler_cmd = NULL;
+ callback_copy(&ac->handler_fn, handler_fn);
+ }
ac->script_ctx = current_sctx;
ac->script_ctx.sc_lnum += SOURCING_LNUM;
nlua_set_sctx(&ac->script_ctx);
@@ -1666,7 +1676,9 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
} else {
autocmd_fname = fname_io;
}
+ char *afile_orig = NULL; ///< Unexpanded <afile>
if (autocmd_fname != NULL) {
+ afile_orig = xstrdup(autocmd_fname);
// Allocate MAXPATHL for when eval_vars() resolves the fullpath.
autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL);
}
@@ -1798,6 +1810,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// save vector size, to avoid an endless loop when more patterns
// are added when executing autocommands
.ausize = kv_size(autocmds[(int)event]),
+ .afile_orig = afile_orig,
.fname = fname,
.sfname = sfname,
.tail = tail,
@@ -1865,6 +1878,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
autocmd_nested = save_autocmd_nested;
xfree(SOURCING_NAME);
estack_pop();
+ xfree(afile_orig);
xfree(autocmd_fname);
autocmd_fname = save_autocmd_fname;
autocmd_fname_full = save_autocmd_fname_full;
@@ -2022,15 +2036,16 @@ static void aucmd_next(AutoPatCmd *apc)
apc->auidx = SIZE_MAX;
}
-static bool call_autocmd_callback(const AutoCmd *ac, const AutoPatCmd *apc)
+/// Executes an autocmd callback function (as opposed to an Ex command).
+static bool au_callback(const AutoCmd *ac, const AutoPatCmd *apc)
{
- Callback callback = ac->exec.callable.cb;
+ Callback callback = ac->handler_fn;
if (callback.type == kCallbackLua) {
MAXSIZE_TEMP_DICT(data, 7);
PUT_C(data, "id", INTEGER_OBJ(ac->id));
PUT_C(data, "event", CSTR_AS_OBJ(event_nr2name(apc->event)));
+ PUT_C(data, "file", CSTR_AS_OBJ(apc->afile_orig));
PUT_C(data, "match", CSTR_AS_OBJ(autocmd_match));
- PUT_C(data, "file", CSTR_AS_OBJ(autocmd_fname));
PUT_C(data, "buf", INTEGER_OBJ(autocmd_bufnr));
if (apc->data) {
@@ -2089,10 +2104,10 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
if (p_verbose >= 9) {
verbose_enter_scroll();
- char *exec_to_string = aucmd_exec_to_string(ac, ac->exec);
- smsg(0, _("autocommand %s"), exec_to_string);
+ char *handler_str = aucmd_handler_to_string(ac);
+ smsg(0, _("autocommand %s"), handler_str);
msg_puts("\n"); // don't overwrite this either
- XFREE_CLEAR(exec_to_string);
+ XFREE_CLEAR(handler_str);
verbose_leave_scroll();
}
@@ -2103,16 +2118,22 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
apc->script_ctx = current_sctx;
char *retval;
- if (ac->exec.type == CALLABLE_CB) {
- // Can potentially reallocate kvec_t data and invalidate the ac pointer
- if (call_autocmd_callback(ac, apc)) {
- // If an autocommand callback returns true, delete the autocommand
- oneshot = true;
- }
-
- // TODO(tjdevries):
- //
- // Major Hack Alert:
+ if (ac->handler_cmd) {
+ retval = xstrdup(ac->handler_cmd);
+ } else {
+ AutoCmd ac_copy = *ac;
+ // Mark oneshot handler as "removed" now, to prevent recursion by e.g. `:doautocmd`. #25526
+ ac->pat = oneshot ? NULL : ac->pat;
+ // May reallocate `acs` kvec_t data and invalidate the `ac` pointer.
+ bool rv = au_callback(&ac_copy, apc);
+ if (oneshot) {
+ // Restore `pat`. Use `acs` because `ac` may have been invalidated by the callback.
+ kv_A(*acs, apc->auidx).pat = ac_copy.pat;
+ }
+ // If an autocommand callback returns true, delete the autocommand
+ oneshot = oneshot || rv;
+
+ // HACK(tjdevries):
// We just return "not-null" and continue going.
// This would be a good candidate for a refactor. You would need to refactor:
// 1. do_cmdline to accept something besides a string
@@ -2121,8 +2142,6 @@ char *getnextac(int c, void *cookie, int indent, bool do_concat)
// and instead we loop over all the matches and just execute one-by-one.
// However, my expectation would be that could be expensive.
retval = xcalloc(1, 1);
- } else {
- retval = xstrdup(ac->exec.callable.cmd);
}
// Remove one-shot ("once") autocmd in anticipation of its execution.
@@ -2441,60 +2460,14 @@ bool autocmd_delete_id(int64_t id)
return success;
}
-// ===========================================================================
-// AucmdExecutable Functions
-// ===========================================================================
-
-/// Generate a string description for the command/callback of an autocmd
-char *aucmd_exec_to_string(AutoCmd *ac, AucmdExecutable acc)
+/// Gets an (allocated) string representation of an autocmd command/callback.
+char *aucmd_handler_to_string(AutoCmd *ac)
FUNC_ATTR_PURE
{
- switch (acc.type) {
- case CALLABLE_EX:
- return xstrdup(acc.callable.cmd);
- case CALLABLE_CB:
- return callback_to_string(&acc.callable.cb, NULL);
- case CALLABLE_NONE:
- return "This is not possible";
+ if (ac->handler_cmd) {
+ return xstrdup(ac->handler_cmd);
}
-
- abort();
-}
-
-void aucmd_exec_free(AucmdExecutable *acc)
-{
- switch (acc->type) {
- case CALLABLE_EX:
- XFREE_CLEAR(acc->callable.cmd);
- break;
- case CALLABLE_CB:
- callback_free(&acc->callable.cb);
- break;
- case CALLABLE_NONE:
- return;
- }
-
- acc->type = CALLABLE_NONE;
-}
-
-AucmdExecutable aucmd_exec_copy(AucmdExecutable src)
-{
- AucmdExecutable dest = AUCMD_EXECUTABLE_INIT;
-
- switch (src.type) {
- case CALLABLE_EX:
- dest.type = CALLABLE_EX;
- dest.callable.cmd = xstrdup(src.callable.cmd);
- return dest;
- case CALLABLE_CB:
- dest.type = CALLABLE_CB;
- callback_copy(&dest.callable.cb, &src.callable.cb);
- return dest;
- case CALLABLE_NONE:
- return dest;
- }
-
- abort();
+ return callback_to_string(&ac->handler_fn, NULL);
}
bool au_event_is_empty(event_T event)
diff --git a/src/nvim/autocmd_defs.h b/src/nvim/autocmd_defs.h
index 490782b209..970aced506 100644
--- a/src/nvim/autocmd_defs.h
+++ b/src/nvim/autocmd_defs.h
@@ -37,10 +37,11 @@ typedef struct {
} AutoPat;
typedef struct {
- AucmdExecutable exec; ///< Command or callback function
AutoPat *pat; ///< Pattern reference (NULL when autocmd was removed)
int64_t id; ///< ID used for uniquely tracking an autocmd
char *desc; ///< Description for the autocmd
+ char *handler_cmd; ///< Handler Ex command (NULL if handler is a function).
+ Callback handler_fn; ///< Handler callback (ignored if `handler_cmd` is not NULL).
sctx_T script_ctx; ///< Script context where it is defined
bool once; ///< "One shot": removed after execution
bool nested; ///< If autocommands nest here
@@ -52,6 +53,7 @@ struct AutoPatCmd_S {
AutoPat *lastpat; ///< Last matched AutoPat
size_t auidx; ///< Current autocmd index to execute
size_t ausize; ///< Saved AutoCmd vector size
+ char *afile_orig; ///< Unexpanded <afile>
char *fname; ///< Fname to match with
char *sfname; ///< Sfname to match with
char *tail; ///< Tail of fname
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index abcce0dfe8..9e6877cbfa 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -875,6 +875,7 @@ static void free_buffer(buf_T *buf)
aubuflocal_remove(buf);
xfree(buf->additional_data);
xfree(buf->b_prompt_text);
+ kv_destroy(buf->b_wininfo);
callback_free(&buf->b_prompt_callback);
callback_free(&buf->b_prompt_interrupt);
clear_fmark(&buf->b_last_cursor, 0);
@@ -901,13 +902,10 @@ 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);
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ free_wininfo(kv_A(buf->b_wininfo, i), buf);
}
+ kv_size(buf->b_wininfo) = 0;
}
/// Free stuff in the buffer for ":bdel" and when wiping out the buffer.
@@ -1393,7 +1391,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
// If the buffer to be deleted is not the current one, delete it here.
if (buf != curbuf) {
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
// Remove the buffer to be deleted from the jump list.
mark_jumplist_forget_file(curwin, buf_fnum);
}
@@ -1419,7 +1417,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf)) {
buf = au_new_curbuf.br_buf;
} else if (curwin->w_jumplistlen > 0) {
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
// Remove the buffer from the jump list.
mark_jumplist_forget_file(curwin, buf_fnum);
}
@@ -1429,7 +1427,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
if (curwin->w_jumplistlen > 0) {
int jumpidx = curwin->w_jumplistidx;
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
// If the index is the same as the length, the current position was not yet added to the
// jump list. So we can safely go back to the last entry and search from there.
if (jumpidx == curwin->w_jumplistlen) {
@@ -1443,7 +1441,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
}
forward = jumpidx;
- while ((jop_flags & JOP_CLEAN) || jumpidx != curwin->w_jumplistidx) {
+ while ((jop_flags & kOptJopFlagClean) || jumpidx != curwin->w_jumplistidx) {
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
if (buf != NULL) {
@@ -1460,7 +1458,7 @@ static int do_buffer_ext(int action, int start, int dir, int count, int flags)
}
}
if (buf != NULL) { // found a valid buffer: stop searching
- if (jop_flags & JOP_CLEAN) {
+ if (jop_flags & kOptJopFlagClean) {
curwin->w_jumplistidx = jumpidx;
update_jumplist = false;
}
@@ -1926,7 +1924,8 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
}
clear_wininfo(buf);
- buf->b_wininfo = xcalloc(1, sizeof(wininfo_T));
+ WinInfo *curwin_info = xcalloc(1, sizeof(WinInfo));
+ kv_push(buf->b_wininfo, curwin_info);
if (buf == curbuf) {
free_buffer_stuff(buf, kBffInitChangedtick); // delete local vars et al.
@@ -1964,9 +1963,9 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
buf_copy_options(buf, BCO_ALWAYS);
}
- buf->b_wininfo->wi_mark = (fmark_T)INIT_FMARK;
- buf->b_wininfo->wi_mark.mark.lnum = lnum;
- buf->b_wininfo->wi_win = curwin;
+ curwin_info->wi_mark = (fmark_T)INIT_FMARK;
+ curwin_info->wi_mark.mark.lnum = lnum;
+ curwin_info->wi_win = curwin;
hash_init(&buf->b_s.b_keywtab);
hash_init(&buf->b_s.b_keywtab_ic);
@@ -2159,11 +2158,11 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
// current buffer isn't empty: open new tab or window
- if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
+ if (wp == NULL && (swb_flags & (kOptSwbFlagVsplit | kOptSwbFlagSplit | kOptSwbFlagNewtab))
&& !buf_is_empty(curbuf)) {
- if (swb_flags & SWB_NEWTAB) {
+ if (swb_flags & kOptSwbFlagNewtab) {
tabpage_new();
- } else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
+ } else if (win_split(0, (swb_flags & kOptSwbFlagVsplit) ? WSP_VERT : 0)
== FAIL) {
return FAIL;
}
@@ -2183,7 +2182,7 @@ int buflist_getfile(int n, linenr_T lnum, int options, int forceit)
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = true;
}
- if (jop_flags & JOP_VIEW && restore_view) {
+ if (jop_flags & kOptJopFlagView && restore_view) {
mark_view_restore(fm);
}
return OK;
@@ -2631,30 +2630,26 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
bool copy_options)
FUNC_ATTR_NONNULL_ARG(1)
{
- wininfo_T *wip;
+ WinInfo *wip;
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ size_t i;
+ for (i = 0; i < kv_size(buf->b_wininfo); i++) {
+ wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == win) {
break;
}
}
- if (wip == NULL) {
+
+ if (i == kv_size(buf->b_wininfo)) {
// allocate a new entry
- wip = xcalloc(1, sizeof(wininfo_T));
+ wip = xcalloc(1, sizeof(WinInfo));
wip->wi_win = win;
if (lnum == 0) { // set lnum even when it's 0
lnum = 1;
}
} else {
// remove the entry from the list
- if (wip->wi_prev) {
- wip->wi_prev->wi_next = wip->wi_next;
- } else {
- buf->b_wininfo = wip->wi_next;
- }
- if (wip->wi_next) {
- wip->wi_next->wi_prev = wip->wi_prev;
- }
+ kv_shift(buf->b_wininfo, i, 1);
if (copy_options && wip->wi_optset) {
clear_winopt(&wip->wi_opt);
deleteFoldRecurse(buf, &wip->wi_folds);
@@ -2679,17 +2674,15 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T
}
// insert the entry in front of the list
- wip->wi_next = buf->b_wininfo;
- buf->b_wininfo = wip;
- wip->wi_prev = NULL;
- if (wip->wi_next) {
- wip->wi_next->wi_prev = wip;
- }
+ kv_pushp(buf->b_wininfo);
+ memmove(&kv_A(buf->b_wininfo, 1), &kv_A(buf->b_wininfo, 0),
+ (kv_size(buf->b_wininfo) - 1) * sizeof(kv_A(buf->b_wininfo, 0)));
+ kv_A(buf->b_wininfo, 0) = wip;
}
/// Check that "wip" has 'diff' set and the diff is only for another tab page.
/// That's because a diff is local to a tab page.
-static bool wininfo_other_tab_diff(wininfo_T *wip)
+static bool wininfo_other_tab_diff(WinInfo *wip)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{
if (!wip->wi_opt.wo_diff) {
@@ -2713,42 +2706,38 @@ static bool wininfo_other_tab_diff(wininfo_T *wip)
/// @param skip_diff_buffer when true, avoid windows with 'diff' set that is in another tab page.
///
/// @return NULL when there isn't any info.
-static wininfo_T *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer)
+static WinInfo *find_wininfo(buf_T *buf, bool need_options, bool skip_diff_buffer)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
- wininfo_T *wip;
-
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == curwin
&& (!skip_diff_buffer || !wininfo_other_tab_diff(wip))
&& (!need_options || wip->wi_optset)) {
- break;
+ return wip;
}
}
- if (wip != NULL) {
- return wip;
- }
-
// If no wininfo for curwin, use the first in the list (that doesn't have
// 'diff' set and is in another tab page).
// If "need_options" is true skip entries that don't have options set,
// unless the window is editing "buf", so we can copy from the window
// itself.
if (skip_diff_buffer) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (!wininfo_other_tab_diff(wip)
&& (!need_options
|| wip->wi_optset
|| (wip->wi_win != NULL
&& wip->wi_win->w_buffer == buf))) {
- break;
+ return wip;
}
}
- } else {
- wip = buf->b_wininfo;
+ } else if (kv_size(buf->b_wininfo)) {
+ return kv_A(buf->b_wininfo, 0);
}
- return wip;
+ return NULL;
}
/// Reset the local window options to the values last used in this window.
@@ -2760,7 +2749,7 @@ void get_winopts(buf_T *buf)
clear_winopt(&curwin->w_onebuf_opt);
clearFolding(curwin);
- wininfo_T *const wip = find_wininfo(buf, true, true);
+ WinInfo *const wip = find_wininfo(buf, true, true);
if (wip != NULL && wip->wi_win != curwin && wip->wi_win != NULL
&& wip->wi_win->w_buffer == buf) {
win_T *wp = wip->wi_win;
@@ -2800,7 +2789,7 @@ fmark_T *buflist_findfmark(buf_T *buf)
{
static fmark_T no_position = { { 1, 0, 0 }, 0, 0, { 0 }, NULL };
- wininfo_T *const wip = find_wininfo(buf, false, false);
+ WinInfo *const wip = find_wininfo(buf, false, false);
return (wip == NULL) ? &no_position : &(wip->wi_mark);
}
@@ -2819,6 +2808,7 @@ void buflist_list(exarg_T *eap)
garray_T buflist;
buf_T **buflist_data = NULL;
+ msg_ext_set_kind("list_cmd");
if (vim_strchr(eap->arg, 't')) {
ga_init(&buflist, sizeof(buf_T *), 50);
for (buf = firstbuf; buf != NULL; buf = buf->b_next) {
@@ -3267,8 +3257,8 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate)
n);
validate_virtcol(curwin);
size_t len = strlen(buffer);
- col_print(buffer + len, IOSIZE - len,
- (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
+ (void)col_print(buffer + len, IOSIZE - len,
+ (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
}
append_arg_number(curwin, buffer, IOSIZE);
@@ -3296,13 +3286,13 @@ void fileinfo(int fullname, int shorthelp, bool dont_truncate)
xfree(buffer);
}
-void col_print(char *buf, size_t buflen, int col, int vcol)
+int col_print(char *buf, size_t buflen, int col, int vcol)
{
if (col == vcol) {
- vim_snprintf(buf, buflen, "%d", col);
- } else {
- vim_snprintf(buf, buflen, "%d-%d", col, vcol);
+ return vim_snprintf(buf, buflen, "%d", col);
}
+
+ return vim_snprintf(buf, buflen, "%d-%d", col, vcol);
}
static char *lasttitle = NULL;
@@ -3341,96 +3331,11 @@ void maketitle(void)
title_str = p_titlestring;
}
} else {
- // Format: "fname + (path) (1 of 2) - VIM".
-
-#define SPACE_FOR_FNAME (sizeof(buf) - 100)
-#define SPACE_FOR_DIR (sizeof(buf) - 20)
-#define SPACE_FOR_ARGNR (sizeof(buf) - 10) // At least room for " - Nvim".
- char *buf_p = buf;
- if (curbuf->b_fname == NULL) {
- const size_t size = xstrlcpy(buf_p, _("[No Name]"),
- SPACE_FOR_FNAME + 1);
- buf_p += MIN(size, SPACE_FOR_FNAME);
- } else {
- buf_p += transstr_buf(path_tail(curbuf->b_fname), -1, buf_p, SPACE_FOR_FNAME + 1, true);
- }
-
- switch (bufIsChanged(curbuf)
- | (curbuf->b_p_ro << 1)
- | (!MODIFIABLE(curbuf) << 2)) {
- case 0:
- break;
- case 1:
- buf_p = strappend(buf_p, " +"); break;
- case 2:
- buf_p = strappend(buf_p, " ="); break;
- case 3:
- buf_p = strappend(buf_p, " =+"); break;
- case 4:
- case 6:
- buf_p = strappend(buf_p, " -"); break;
- case 5:
- case 7:
- buf_p = strappend(buf_p, " -+"); break;
- default:
- abort();
- }
-
- if (curbuf->b_fname != NULL) {
- // Get path of file, replace home dir with ~.
- *buf_p++ = ' ';
- *buf_p++ = '(';
- home_replace(curbuf, curbuf->b_ffname, buf_p,
- (SPACE_FOR_DIR - (size_t)(buf_p - buf)), true);
-#ifdef BACKSLASH_IN_FILENAME
- // Avoid "c:/name" to be reduced to "c".
- if (isalpha((uint8_t)(*buf_p)) && *(buf_p + 1) == ':') {
- buf_p += 2;
- }
-#endif
- // Remove the file name.
- char *p = path_tail_with_sep(buf_p);
- if (p == buf_p) {
- // Must be a help buffer.
- xstrlcpy(buf_p, _("help"), SPACE_FOR_DIR - (size_t)(buf_p - buf));
- } else {
- *p = NUL;
- }
-
- // Translate unprintable chars and concatenate. Keep some
- // room for the server name. When there is no room (very long
- // file name) use (...).
- if ((size_t)(buf_p - buf) < SPACE_FOR_DIR) {
- char *const tbuf = transstr(buf_p, true);
- const size_t free_space = SPACE_FOR_DIR - (size_t)(buf_p - buf) + 1;
- const size_t dir_len = xstrlcpy(buf_p, tbuf, free_space);
- buf_p += MIN(dir_len, free_space - 1);
- xfree(tbuf);
- } else {
- const size_t free_space = SPACE_FOR_ARGNR - (size_t)(buf_p - buf) + 1;
- const size_t dots_len = xstrlcpy(buf_p, "...", free_space);
- buf_p += MIN(dots_len, free_space - 1);
- }
- *buf_p++ = ')';
- *buf_p = NUL;
- } else {
- *buf_p = NUL;
- }
-
- append_arg_number(curwin, buf_p, (int)(SPACE_FOR_ARGNR - (size_t)(buf_p - buf)));
-
- xstrlcat(buf_p, " - Nvim", (sizeof(buf) - (size_t)(buf_p - buf)));
-
- if (maxlen > 0) {
- // Make it shorter by removing a bit in the middle.
- if (vim_strsize(buf) > maxlen) {
- trunc_string(buf, buf, maxlen, sizeof(buf));
- }
- }
+ // Format: "fname + (path) (1 of 2) - Nvim".
+ char *default_titlestring = "%t%( %M%)%( (%{expand(\"%:~:h\")})%)%a - Nvim";
+ build_stl_str_hl(curwin, buf, sizeof(buf), default_titlestring,
+ kOptTitlestring, 0, 0, maxlen, NULL, NULL, NULL, NULL);
title_str = buf;
-#undef SPACE_FOR_FNAME
-#undef SPACE_FOR_DIR
-#undef SPACE_FOR_ARGNR
}
}
bool mustset = value_change(title_str, &lasttitle);
@@ -3511,15 +3416,16 @@ void free_titles(void)
/// Get relative cursor position in window into "buf[buflen]", in the localized
/// percentage form like %99, 99%; using "Top", "Bot" or "All" when appropriate.
-void get_rel_pos(win_T *wp, char *buf, int buflen)
+int get_rel_pos(win_T *wp, char *buf, int buflen)
{
// Need at least 3 chars for writing.
if (buflen < 3) {
- return;
+ return 0;
}
linenr_T above; // number of lines above window
linenr_T below; // number of lines below window
+ int len;
above = wp->w_topline - 1;
above += win_get_fill(wp, wp->w_topline) - wp->w_topfill;
@@ -3530,25 +3436,24 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
}
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
if (below <= 0) {
- xstrlcpy(buf, (above == 0 ? _("All") : _("Bot")), (size_t)buflen);
+ len = vim_snprintf(buf, (size_t)buflen, "%s", (above == 0) ? _("All") : _("Bot"));
} else if (above <= 0) {
- xstrlcpy(buf, _("Top"), (size_t)buflen);
+ len = vim_snprintf(buf, (size_t)buflen, "%s", _("Top"));
} else {
int perc = (above > 1000000
? (above / ((above + below) / 100))
: (above * 100 / (above + below)));
-
- char *p = buf;
- size_t l = (size_t)buflen;
- if (perc < 10) {
- // prepend one space
- buf[0] = ' ';
- p++;
- l--;
- }
// localized percentage value
- vim_snprintf(p, l, _("%d%%"), perc);
+ len = vim_snprintf(buf, (size_t)buflen, _("%s%d%%"), (perc < 10) ? " " : "", perc);
}
+ if (len < 0) {
+ buf[0] = NUL;
+ len = 0;
+ } else if (len > buflen - 1) {
+ len = buflen - 1;
+ }
+
+ return len;
}
/// Append (2 of 8) to "buf[buflen]", if editing more than one file.
@@ -3722,7 +3627,7 @@ void ex_buffer_all(exarg_T *eap)
// Open the buffer in this window.
swap_exists_action = SEA_DIALOG;
- set_curbuf(buf, DOBUF_GOTO, !(jop_flags & JOP_CLEAN));
+ set_curbuf(buf, DOBUF_GOTO, !(jop_flags & kOptJopFlagClean));
if (!bufref_valid(&bufref)) {
// Autocommands deleted the buffer.
swap_exists_action = SEA_NONE;
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 8cd1ffc979..3b84a4420f 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -70,7 +70,7 @@ typedef struct {
// Mask to check for flags that prevent normal writing
#define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR)
-typedef struct wininfo_S wininfo_T;
+typedef struct wininfo_S WinInfo;
typedef struct frame_S frame_T;
typedef uint64_t disptick_T; // display tick type
@@ -85,7 +85,7 @@ typedef struct {
// Structure that contains all options that are local to a window.
// Used twice in a window: for the current buffer and for all buffers.
-// Also used in wininfo_T.
+// Also used in WinInfo.
typedef struct {
int wo_arab;
#define w_p_arab w_onebuf_opt.wo_arab // 'arabic'
@@ -219,8 +219,6 @@ typedef struct {
// The window-info is kept in a list at b_wininfo. It is kept in
// most-recently-used order.
struct wininfo_S {
- wininfo_T *wi_next; // next entry or NULL for last entry
- wininfo_T *wi_prev; // previous entry or NULL for first entry
win_T *wi_win; // pointer to window that did set wi_mark
fmark_T wi_mark; // last cursor mark in the file
bool wi_optset; // true when wi_opt has useful values
@@ -316,8 +314,6 @@ typedef struct {
char *b_p_spf; // 'spellfile'
char *b_p_spl; // 'spelllang'
char *b_p_spo; // 'spelloptions'
-#define SPO_CAMEL 0x1
-#define SPO_NPBUFFER 0x2
unsigned b_p_spo_flags; // 'spelloptions' flags
int b_cjk; // all CJK letters as OK
uint8_t b_syn_chartab[32]; // syntax iskeyword option
@@ -413,7 +409,7 @@ struct file_buffer {
// change
linenr_T b_mod_xlines; // number of extra buffer lines inserted;
// negative when lines were deleted
- wininfo_T *b_wininfo; // list of last used info for each window
+ kvec_t(WinInfo *) b_wininfo; // list of last used info for each window
disptick_T b_mod_tick_syn; // last display tick syntax was updated
disptick_T b_mod_tick_decor; // last display tick decoration providers
// where invoked
@@ -905,6 +901,8 @@ typedef enum {
kFloatRelativeWindow = 1,
kFloatRelativeCursor = 2,
kFloatRelativeMouse = 3,
+ kFloatRelativeTabline = 4,
+ kFloatRelativeLaststatus = 5,
} FloatRelative;
/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c)
@@ -1036,7 +1034,7 @@ struct window_S {
synblock_T *w_s; ///< for :ownsyntax
int w_ns_hl;
- int w_ns_hl_winhl;
+ int w_ns_hl_winhl; ///< when set to -1, 'winhighlight' shouldn't be used
int w_ns_hl_active;
int *w_ns_hl_attr;
diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c
index e725678937..ab07d67ef3 100644
--- a/src/nvim/buffer_updates.c
+++ b/src/nvim/buffer_updates.c
@@ -16,12 +16,13 @@
#include "nvim/lua/executor.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "buffer_updates.c.generated.h"
+# include "buffer_updates.c.generated.h" // IWYU pragma: keep
#endif
// Register a channel. Return True if the channel was added, or already added.
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 5f830b4219..1afa10df63 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -27,7 +27,6 @@
#include "nvim/fileio.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/iconv_defs.h"
#include "nvim/input.h"
@@ -351,7 +350,7 @@ static int check_mtime(buf_T *buf, FileInfo *file_info)
msg_silent = 0; // Must give this prompt.
// Don't use emsg() here, don't want to flush the buffers.
msg(_("WARNING: The file has been changed since reading it!!!"), HLF_E);
- if (ask_yesno(_("Do you really want to write to it"), true) == 'n') {
+ if (ask_yesno(_("Do you really want to write to it")) == 'n') {
return FAIL;
}
msg_scroll = false; // Always overwrite the file message now.
@@ -725,9 +724,9 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
FileInfo file_info;
const bool no_prepend_dot = false;
- if ((bkc & BKC_YES) || append) { // "yes"
+ if ((bkc & kOptBkcFlagYes) || append) { // "yes"
*backup_copyp = true;
- } else if ((bkc & BKC_AUTO)) { // "auto"
+ } else if ((bkc & kOptBkcFlagAuto)) { // "auto"
// Don't rename the file when:
// - it's a hard link
// - it's a symbolic link
@@ -773,19 +772,19 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
}
// Break symlinks and/or hardlinks if we've been asked to.
- if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK)) {
+ if ((bkc & kOptBkcFlagBreaksymlink) || (bkc & kOptBkcFlagBreakhardlink)) {
#ifdef UNIX
bool file_info_link_ok = os_fileinfo_link(fname, &file_info);
// Symlinks.
- if ((bkc & BKC_BREAKSYMLINK)
+ if ((bkc & kOptBkcFlagBreaksymlink)
&& file_info_link_ok
&& !os_fileinfo_id_equal(&file_info, file_info_old)) {
*backup_copyp = false;
}
// Hardlinks.
- if ((bkc & BKC_BREAKHARDLINK)
+ if ((bkc & kOptBkcFlagBreakhardlink)
&& os_fileinfo_hardlinks(file_info_old) > 1
&& (!file_info_link_ok
|| os_fileinfo_id_equal(&file_info, file_info_old))) {
@@ -1148,6 +1147,7 @@ int buf_write(buf_T *buf, char *fname, char *sfname, linenr_T start, linenr_T en
msg_scroll = true; // don't overwrite previous file message
}
if (!filtering) {
+ msg_ext_set_kind("bufwrite");
// show that we are busy
#ifndef UNIX
filemess(buf, sfname, "");
@@ -1763,6 +1763,7 @@ restore_backup:
if (msg_add_fileformat(fileformat)) {
insert_space = true;
}
+ msg_ext_set_kind("bufwrite");
msg_add_lines(insert_space, lnum, nchars); // add line/char count
if (!shortmess(SHM_WRITE)) {
if (append) {
diff --git a/src/nvim/change.c b/src/nvim/change.c
index f3a8e0b208..ecd6012679 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -25,7 +25,6 @@
#include "nvim/fold.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -914,7 +913,7 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
// fixpos is true, we don't want to end up positioned at the NUL,
// unless "restart_edit" is set or 'virtualedit' contains "onemore".
if (col > 0 && fixpos && restart_edit == 0
- && (get_ve_flags(curwin) & VE_ONEMORE) == 0) {
+ && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
curwin->w_cursor.col--;
curwin->w_cursor.coladd = 0;
curwin->w_cursor.col -= utf_head_off(oldp, oldp + curwin->w_cursor.col);
diff --git a/src/nvim/channel.c b/src/nvim/channel.c
index 021fdd4b79..912d515f84 100644
--- a/src/nvim/channel.c
+++ b/src/nvim/channel.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <fcntl.h>
#include <inttypes.h>
#include <lauxlib.h>
#include <stddef.h>
@@ -22,7 +23,6 @@
#include "nvim/event/proc.h"
#include "nvim/event/rstream.h"
#include "nvim/event/socket.h"
-#include "nvim/event/stream.h"
#include "nvim/event/wstream.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
@@ -32,6 +32,7 @@
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 1afd590b0e..f72420a00f 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -95,7 +95,7 @@ int buf_init_chartab(buf_T *buf, bool global)
int c = 0;
while (c < ' ') {
- g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
+ g_chartab[c++] = (dy_flags & kOptDyFlagUhex) ? 4 : 2;
}
while (c <= '~') {
@@ -109,7 +109,7 @@ int buf_init_chartab(buf_T *buf, bool global)
g_chartab[c++] = (CT_PRINT_CHAR | CT_FNAME_CHAR) + 1;
} else {
// the rest is unprintable by default
- g_chartab[c++] = (dy_flags & DY_UHEX) ? 4 : 2;
+ g_chartab[c++] = (dy_flags & kOptDyFlagUhex) ? 4 : 2;
}
}
}
@@ -237,7 +237,7 @@ static int parse_isopt(const char *var, buf_T *buf, bool only_check)
if (c < ' ' || c > '~') {
if (tilde) {
g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK)
- + ((dy_flags & DY_UHEX) ? 4 : 2));
+ + ((dy_flags & kOptDyFlagUhex) ? 4 : 2));
g_chartab[c] &= (uint8_t) ~CT_PRINT_CHAR;
} else {
g_chartab[c] = (uint8_t)((g_chartab[c] & ~CT_CELL_MASK) + 1);
@@ -614,7 +614,7 @@ void transchar_nonprint(const buf_T *buf, char *charbuf, int c)
}
assert(c <= 0xff);
- if (dy_flags & DY_UHEX || c > 0x7f) {
+ if (dy_flags & kOptDyFlagUhex || c > 0x7f) {
// 'display' has "uhex"
transchar_hex(charbuf, c);
} else {
diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c
index 700d554821..3a9eeb73c8 100644
--- a/src/nvim/cmdexpand.c
+++ b/src/nvim/cmdexpand.c
@@ -42,7 +42,6 @@
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
-#include "nvim/log.h"
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/mapping.h"
@@ -100,7 +99,7 @@ static int compl_selected;
static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
- return (wop_flags & WOP_FUZZY)
+ return (wop_flags & kOptWopFlagFuzzy)
&& xp->xp_context != EXPAND_BOOL_SETTINGS
&& xp->xp_context != EXPAND_COLORS
&& xp->xp_context != EXPAND_COMPILER
@@ -133,7 +132,7 @@ static bool cmdline_fuzzy_completion_supported(const expand_T *const xp)
bool cmdline_fuzzy_complete(const char *const fuzzystr)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
{
- return (wop_flags & WOP_FUZZY) && *fuzzystr != NUL;
+ return (wop_flags & kOptWopFlagFuzzy) && *fuzzystr != NUL;
}
/// Sort function for the completion matches.
@@ -806,7 +805,7 @@ static char *find_longest_match(expand_T *xp, int options)
}
if (i < xp->xp_numfiles) {
if (!(options & WILD_NO_BEEP)) {
- vim_beep(BO_WILD);
+ vim_beep(kOptBoFlagWildmode);
}
break;
}
@@ -1069,7 +1068,7 @@ int showmatches(expand_T *xp, bool wildmenu)
bool compl_use_pum = (ui_has(kUICmdline)
? ui_has(kUIPopupmenu)
- : wildmenu && (wop_flags & WOP_PUM))
+ : wildmenu && (wop_flags & kOptWopFlagPum))
|| ui_has(kUIWildmenu);
if (compl_use_pum) {
@@ -1084,6 +1083,7 @@ int showmatches(expand_T *xp, bool wildmenu)
ui_flush();
cmdline_row = msg_row;
msg_didany = false; // lines_left will be set again
+ msg_ext_set_kind("wildlist");
msg_start(); // prepare for paging
}
@@ -1938,7 +1938,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
case CMD_tjump:
case CMD_stjump:
case CMD_ptjump:
- if (wop_flags & WOP_TAGFILE) {
+ if (wop_flags & kOptWopFlagTagfile) {
xp->xp_context = EXPAND_TAGS_LISTFILES;
} else {
xp->xp_context = EXPAND_TAGS;
@@ -2000,6 +2000,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, expa
FALLTHROUGH;
case CMD_buffer:
case CMD_sbuffer:
+ case CMD_pbuffer:
case CMD_checktime:
xp->xp_context = EXPAND_BUFFERS;
xp->xp_pattern = (char *)arg;
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 5993eefd67..d2285cab24 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -13,6 +13,7 @@
#include "nvim/cmdhist.h"
#include "nvim/errors.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
diff --git a/src/nvim/cmdhist.h b/src/nvim/cmdhist.h
index c933982593..f45345372b 100644
--- a/src/nvim/cmdhist.h
+++ b/src/nvim/cmdhist.h
@@ -1,7 +1,9 @@
#pragma once
+#include <stddef.h> // IWYU pragma: keep
+
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/os/time_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/context.c b/src/nvim/context.c
index 461b10a9d5..9d8fdb7e74 100644
--- a/src/nvim/context.c
+++ b/src/nvim/context.c
@@ -20,6 +20,7 @@
#include "nvim/hashtab.h"
#include "nvim/keycodes.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/shada.h"
diff --git a/src/nvim/context.h b/src/nvim/context.h
index 4375030fbc..5ae2a078b0 100644
--- a/src/nvim/context.h
+++ b/src/nvim/context.h
@@ -1,6 +1,6 @@
#pragma once
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 35afca2fe9..580ed856e4 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -11,6 +11,7 @@
#include "nvim/drawscreen.h"
#include "nvim/fold.h"
#include "nvim/globals.h"
+#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
@@ -105,7 +106,7 @@ static int coladvance2(win_T *wp, pos_T *pos, bool addspaces, bool finetune, col
|| (State & MODE_TERMINAL)
|| restart_edit != NUL
|| (VIsual_active && *p_sel != 'o')
- || ((get_ve_flags(wp) & VE_ONEMORE) && wcol < MAXCOL);
+ || ((get_ve_flags(wp) & kOptVeFlagOnemore) && wcol < MAXCOL);
char *line = ml_get_buf(wp->w_buffer, pos->lnum);
int linelen = ml_get_buf_len(wp->w_buffer, pos->lnum);
@@ -341,11 +342,13 @@ void check_cursor_col(win_T *win)
} else if (win->w_cursor.col >= len) {
// Allow cursor past end-of-line when:
// - in Insert mode or restarting Insert mode
+ // - in Terminal mode
// - in Visual mode and 'selection' isn't "old"
// - 'virtualedit' is set
if ((State & MODE_INSERT) || restart_edit
+ || (State & MODE_TERMINAL)
|| (VIsual_active && *p_sel != 'o')
- || (cur_ve_flags & VE_ONEMORE)
+ || (cur_ve_flags & kOptVeFlagOnemore)
|| virtual_active(win)) {
win->w_cursor.col = len;
} else {
@@ -362,7 +365,7 @@ void check_cursor_col(win_T *win)
// line.
if (oldcol == MAXCOL) {
win->w_cursor.coladd = 0;
- } else if (cur_ve_flags == VE_ALL) {
+ } else if (cur_ve_flags == kOptVeFlagAll) {
if (oldcoladd > win->w_cursor.col) {
win->w_cursor.coladd = oldcoladd - win->w_cursor.col;
diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c
index 1f11367618..a058394b9f 100644
--- a/src/nvim/cursor_shape.c
+++ b/src/nvim/cursor_shape.c
@@ -45,6 +45,7 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = {
{ "more", 0, 0, 0, 0, 0, 0, 0, 0, "m", SHAPE_MOUSE },
{ "more_lastline", 0, 0, 0, 0, 0, 0, 0, 0, "ml", SHAPE_MOUSE },
{ "showmatch", 0, 0, 0, 100, 100, 100, 0, 0, "sm", SHAPE_CURSOR },
+ { "terminal", 0, 0, 0, 0, 0, 0, 0, 0, "t", SHAPE_CURSOR },
};
/// Converts cursor_shapes into an Array of Dictionaries
@@ -321,6 +322,8 @@ int cursor_get_mode_idx(void)
{
if (State == MODE_SHOWMATCH) {
return SHAPE_IDX_SM;
+ } else if (State == MODE_TERMINAL) {
+ return SHAPE_IDX_TERM;
} else if (State & VREPLACE_FLAG) {
return SHAPE_IDX_R;
} else if (State & REPLACE_FLAG) {
diff --git a/src/nvim/cursor_shape.h b/src/nvim/cursor_shape.h
index 21967a81f4..6d9e7de2e5 100644
--- a/src/nvim/cursor_shape.h
+++ b/src/nvim/cursor_shape.h
@@ -23,7 +23,8 @@ typedef enum {
SHAPE_IDX_MORE = 14, ///< Hit-return or More
SHAPE_IDX_MOREL = 15, ///< Hit-return or More in last line
SHAPE_IDX_SM = 16, ///< showing matching paren
- SHAPE_IDX_COUNT = 17,
+ SHAPE_IDX_TERM = 17, ///< Terminal mode
+ SHAPE_IDX_COUNT = 18,
} ModeShape;
typedef enum {
diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c
index b71ff23f57..e60d04fdfd 100644
--- a/src/nvim/debugger.c
+++ b/src/nvim/debugger.c
@@ -110,13 +110,11 @@ void do_debug(char *cmd)
}
if (debug_oldval != NULL) {
smsg(0, _("Oldval = \"%s\""), debug_oldval);
- xfree(debug_oldval);
- debug_oldval = NULL;
+ XFREE_CLEAR(debug_oldval);
}
if (debug_newval != NULL) {
smsg(0, _("Newval = \"%s\""), debug_newval);
- xfree(debug_newval);
- debug_newval = NULL;
+ XFREE_CLEAR(debug_newval);
}
char *sname = estack_sfile(ESTACK_NONE);
if (sname != NULL) {
@@ -153,8 +151,7 @@ void do_debug(char *cmd)
debug_break_level = -1;
xfree(cmdline);
- cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL,
- CALLBACK_NONE);
+ cmdline = getcmdline_prompt('>', NULL, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE, false, NULL);
debug_break_level = n;
if (typeahead_saved) {
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index 3633940b14..149504f424 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -17,12 +17,11 @@
#include "nvim/fold.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/marktree.h"
#include "nvim/memory.h"
-#include "nvim/move.h"
+#include "nvim/memory_defs.h"
#include "nvim/option_vars.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
@@ -304,17 +303,30 @@ static void decor_free_inner(DecorVirtText *vt, uint32_t first_idx)
}
}
+/// Check if we are in a callback while drawing, which might invalidate the marktree iterator.
+///
+/// This should be called whenever a structural modification has been done to a
+/// marktree in a public API function (i e any change which adds or deletes marks).
+void decor_state_invalidate(buf_T *buf)
+{
+ if (decor_state.win && decor_state.win->w_buffer == buf) {
+ decor_state.itr_valid = false;
+ }
+}
+
void decor_check_to_be_deleted(void)
{
assert(!decor_state.running_decor_provider);
decor_free_inner(to_free_virt, to_free_sh);
to_free_virt = NULL;
to_free_sh = DECOR_ID_INVALID;
+ decor_state.win = NULL;
}
void decor_state_free(DecorState *state)
{
- kv_destroy(state->active);
+ kv_destroy(state->slots);
+ kv_destroy(state->ranges_i);
}
void clear_virttext(VirtText *text)
@@ -399,14 +411,30 @@ bool decor_redraw_reset(win_T *wp, DecorState *state)
{
state->row = -1;
state->win = wp;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange item = kv_A(state->active, i);
- if (item.owned && item.kind == kDecorKindVirtText) {
- clear_virttext(&item.data.vt->data.virt_text);
- xfree(item.data.vt);
+
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ int const beg_pos[] = { 0, state->future_begin };
+ int const end_pos[] = { state->current_end, (int)kv_size(state->ranges_i) };
+
+ for (int pos_i = 0; pos_i < 2; pos_i++) {
+ for (int i = beg_pos[pos_i]; i < end_pos[pos_i]; i++) {
+ DecorRange *const r = &slots[indices[i]].range;
+ if (r->owned && r->kind == kDecorKindVirtText) {
+ clear_virttext(&r->data.vt->data.virt_text);
+ xfree(r->data.vt);
+ }
}
}
- kv_size(state->active) = 0;
+
+ kv_size(state->slots) = 0;
+ kv_size(state->ranges_i) = 0;
+ state->free_slot_i = -1;
+ state->current_end = 0;
+ state->future_begin = 0;
+ state->new_range_ordering = 0;
+
return wp->w_buffer->b_marktree->n_keys;
}
@@ -431,6 +459,8 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
{
buf_T *buf = wp->w_buffer;
state->top_row = top_row;
+ state->itr_valid = true;
+
if (!marktree_itr_get_overlap(buf->b_marktree, top_row, 0, state->itr)) {
return false;
}
@@ -452,14 +482,37 @@ bool decor_redraw_start(win_T *wp, int top_row, DecorState *state)
bool decor_redraw_line(win_T *wp, int row, DecorState *state)
{
+ int count = (int)kv_size(state->ranges_i);
+ int const cur_end = state->current_end;
+ int fut_beg = state->future_begin;
+
+ // Move future ranges to start right after current ranges.
+ // Otherwise future ranges will grow forward indefinitely.
+ if (fut_beg == count) {
+ fut_beg = count = cur_end;
+ } else if (fut_beg != cur_end) {
+ int *const indices = state->ranges_i.items;
+ memmove(indices + cur_end, indices + fut_beg, (size_t)(count - fut_beg) * sizeof(indices[0]));
+
+ count = cur_end + (count - fut_beg);
+ fut_beg = cur_end;
+ }
+
+ kv_size(state->ranges_i) = (size_t)count;
+ state->future_begin = fut_beg;
+
if (state->row == -1) {
decor_redraw_start(wp, row, state);
+ } else if (!state->itr_valid) {
+ marktree_itr_get(wp->w_buffer->b_marktree, row, 0, state->itr);
+ state->itr_valid = true;
}
+
state->row = row;
state->col_until = -1;
state->eol_col = -1;
- if (kv_size(state->active)) {
+ if (cur_end != 0 || fut_beg != count) {
return true;
}
@@ -489,18 +542,51 @@ static void decor_range_add_from_inline(DecorState *state, int start_row, int st
}
}
-static void decor_range_insert(DecorState *state, DecorRange range)
+static void decor_range_insert(DecorState *state, DecorRange *range)
{
- kv_pushp(state->active);
- size_t index;
- for (index = kv_size(state->active) - 1; index > 0; index--) {
- DecorRange item = kv_A(state->active, index - 1);
- if (item.priority <= range.priority) {
- break;
+ range->ordering = state->new_range_ordering++;
+
+ int index;
+ // Get space for a new `DecorRange` from the freelist or allocate.
+ if (state->free_slot_i >= 0) {
+ index = state->free_slot_i;
+ DecorRangeSlot *slot = &kv_A(state->slots, index);
+ state->free_slot_i = slot->next_free_i;
+ slot->range = *range;
+ } else {
+ index = (int)kv_size(state->slots);
+ kv_pushp(state->slots)->range = *range;
+ }
+
+ int const row = range->start_row;
+ int const col = range->start_col;
+
+ int const count = (int)kv_size(state->ranges_i);
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ int begin = state->future_begin;
+ int end = count;
+ while (begin < end) {
+ int const mid = begin + ((end - begin) >> 1);
+ DecorRange *const mr = &slots[indices[mid]].range;
+
+ int const mrow = mr->start_row;
+ int const mcol = mr->start_col;
+ if (mrow < row || (mrow == row && mcol <= col)) {
+ begin = mid + 1;
+ if (mrow == row && mcol == col) {
+ break;
+ }
+ } else {
+ end = mid;
}
- kv_A(state->active, index) = kv_A(state->active, index - 1);
}
- kv_A(state->active, index) = range;
+
+ kv_pushp(state->ranges_i);
+ int *const item = &kv_A(state->ranges_i, begin);
+ memmove(item + 1, item, (size_t)(count - begin) * sizeof(*item));
+ *item = index;
}
void decor_range_add_virt(DecorState *state, int start_row, int start_col, int end_row, int end_col,
@@ -516,7 +602,7 @@ void decor_range_add_virt(DecorState *state, int start_row, int start_col, int e
.priority = vt->priority,
.draw_col = -10,
};
- decor_range_insert(state, range);
+ decor_range_insert(state, &range);
}
void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end_row, int end_col,
@@ -541,7 +627,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end
if (sh->hl_id) {
range.attr_id = syn_id2attr(sh->hl_id);
}
- decor_range_insert(state, range);
+ decor_range_insert(state, &range);
}
if (sh->flags & (kSHUIWatched)) {
@@ -549,7 +635,7 @@ void decor_range_add_sh(DecorState *state, int start_row, int start_col, int end
range.data.ui.ns_id = ns;
range.data.ui.mark_id = mark_id;
range.data.ui.pos = (sh->flags & kSHUIWatchedOverlay) ? kVPosOverlay : kVPosEndOfLine;
- decor_range_insert(state, range);
+ decor_range_insert(state, &range);
}
}
@@ -569,29 +655,32 @@ void decor_init_draw_col(int win_col, bool hidden, DecorRange *item)
void decor_recheck_draw_col(int win_col, bool hidden, DecorState *state)
{
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
- if (item->draw_col == -3) {
- decor_init_draw_col(win_col, hidden, item);
+ int const end = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ for (int i = 0; i < end; i++) {
+ DecorRange *const r = &slots[indices[i]].range;
+ if (r->draw_col == -3) {
+ decor_init_draw_col(win_col, hidden, r);
}
}
}
-int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
+int decor_redraw_col_impl(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
{
- buf_T *buf = wp->w_buffer;
- if (col <= state->col_until) {
- return state->current;
- }
- state->col_until = MAXCOL;
+ buf_T *const buf = wp->w_buffer;
+ int const row = state->row;
+ int col_until = MAXCOL;
+
while (true) {
// TODO(bfredl): check duplicate entry in "intersection"
// branch
MTKey mark = marktree_itr_current(state->itr);
- if (mark.pos.row < 0 || mark.pos.row > state->row) {
+ if (mark.pos.row < 0 || mark.pos.row > row) {
break;
- } else if (mark.pos.row == state->row && mark.pos.col > col) {
- state->col_until = mark.pos.col - 1;
+ } else if (mark.pos.row == row && mark.pos.col > col) {
+ col_until = mark.pos.col - 1;
break;
}
@@ -607,73 +696,132 @@ next_mark:
marktree_itr_next(buf->b_marktree, state->itr);
}
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ int count = (int)kv_size(state->ranges_i);
+ int cur_end = state->current_end;
+ int fut_beg = state->future_begin;
+
+ // Promote future ranges before the cursor to active.
+ for (; fut_beg < count; fut_beg++) {
+ int const index = indices[fut_beg];
+ DecorRange *const r = &slots[index].range;
+ if (r->start_row > row || (r->start_row == row && r->start_col > col)) {
+ break;
+ }
+ int const ordering = r->ordering;
+ DecorPriority const priority = r->priority;
+
+ int begin = 0;
+ int end = cur_end;
+ while (begin < end) {
+ int mid = begin + ((end - begin) >> 1);
+ int mi = indices[mid];
+ DecorRange *mr = &slots[mi].range;
+ if (mr->priority < priority || (mr->priority == priority && mr->ordering < ordering)) {
+ begin = mid + 1;
+ } else {
+ end = mid;
+ }
+ }
+
+ int *const item = indices + begin;
+ memmove(item + 1, item, (size_t)(cur_end - begin) * sizeof(*item));
+ *item = index;
+ cur_end++;
+ }
+
+ if (fut_beg < count) {
+ DecorRange *r = &slots[indices[fut_beg]].range;
+ if (r->start_row == row) {
+ col_until = MIN(col_until, r->start_col - 1);
+ }
+ }
+
+ int new_cur_end = 0;
+
int attr = 0;
- size_t j = 0;
int conceal = 0;
schar_T conceal_char = 0;
int conceal_attr = 0;
TriState spell = kNone;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange item = kv_A(state->active, i);
- bool active = false, keep = true;
- if (item.end_row < state->row
- || (item.end_row == state->row && item.end_col <= col)) {
- if (!(item.start_row >= state->row && decor_virt_pos(&item))) {
- keep = false;
- }
+ for (int i = 0; i < cur_end; i++) {
+ int const index = indices[i];
+ DecorRangeSlot *const slot = slots + index;
+ DecorRange *const r = &slot->range;
+
+ bool keep;
+ if (r->end_row < row || (r->end_row == row && r->end_col <= col)) {
+ keep = r->start_row >= row && decor_virt_pos(r);
} else {
- if (item.start_row < state->row
- || (item.start_row == state->row && item.start_col <= col)) {
- active = true;
- if (item.end_row == state->row && item.end_col > col) {
- state->col_until = MIN(state->col_until, item.end_col - 1);
- }
- } else {
- if (item.start_row == state->row) {
- state->col_until = MIN(state->col_until, item.start_col - 1);
- }
+ keep = true;
+
+ if (r->end_row == row && r->end_col > col) {
+ col_until = MIN(col_until, r->end_col - 1);
}
- }
- if (active && item.attr_id > 0) {
- attr = hl_combine_attr(attr, item.attr_id);
- }
- if (active && item.kind == kDecorKindHighlight && (item.data.sh.flags & kSHConceal)) {
- conceal = 1;
- if (item.start_row == state->row && item.start_col == col) {
- DecorSignHighlight *sh = &item.data.sh;
- conceal = 2;
- conceal_char = sh->text[0];
- state->col_until = MIN(state->col_until, item.start_col);
- conceal_attr = item.attr_id;
+
+ if (r->attr_id > 0) {
+ attr = hl_combine_attr(attr, r->attr_id);
}
- }
- if (active && item.kind == kDecorKindHighlight) {
- if (item.data.sh.flags & kSHSpellOn) {
- spell = kTrue;
- } else if (item.data.sh.flags & kSHSpellOff) {
- spell = kFalse;
+
+ if (r->kind == kDecorKindHighlight && (r->data.sh.flags & kSHConceal)) {
+ conceal = 1;
+ if (r->start_row == row && r->start_col == col) {
+ DecorSignHighlight *sh = &r->data.sh;
+ conceal = 2;
+ conceal_char = sh->text[0];
+ col_until = MIN(col_until, r->start_col);
+ conceal_attr = r->attr_id;
+ }
}
- if (item.data.sh.url != NULL) {
- attr = hl_add_url(attr, item.data.sh.url);
+
+ if (r->kind == kDecorKindHighlight) {
+ if (r->data.sh.flags & kSHSpellOn) {
+ spell = kTrue;
+ } else if (r->data.sh.flags & kSHSpellOff) {
+ spell = kFalse;
+ }
+ if (r->data.sh.url != NULL) {
+ attr = hl_add_url(attr, r->data.sh.url);
+ }
}
}
- if (item.start_row == state->row && item.start_col <= col
- && decor_virt_pos(&item) && item.draw_col == -10) {
- decor_init_draw_col(win_col, hidden, &item);
+
+ if (r->start_row == row && r->start_col <= col
+ && decor_virt_pos(r) && r->draw_col == -10) {
+ decor_init_draw_col(win_col, hidden, r);
}
+
if (keep) {
- kv_A(state->active, j++) = item;
- } else if (item.owned) {
- if (item.kind == kDecorKindVirtText) {
- clear_virttext(&item.data.vt->data.virt_text);
- xfree(item.data.vt);
- } else if (item.kind == kDecorKindHighlight) {
- xfree((void *)item.data.sh.url);
+ indices[new_cur_end++] = index;
+ } else {
+ if (r->owned) {
+ if (r->kind == kDecorKindVirtText) {
+ clear_virttext(&r->data.vt->data.virt_text);
+ xfree(r->data.vt);
+ } else if (r->kind == kDecorKindHighlight) {
+ xfree((void *)r->data.sh.url);
+ }
}
+
+ int *fi = &state->free_slot_i;
+ slot->next_free_i = *fi;
+ *fi = index;
}
}
- kv_size(state->active) = j;
+ cur_end = new_cur_end;
+
+ if (fut_beg == count) {
+ fut_beg = count = cur_end;
+ }
+
+ kv_size(state->ranges_i) = (size_t)count;
+ state->future_begin = fut_beg;
+ state->current_end = cur_end;
+ state->col_until = col_until;
+
state->current = attr;
state->conceal = conceal;
state->conceal_char = conceal_char;
@@ -708,9 +856,9 @@ static const uint32_t sign_filter[4] = {[kMTMetaSignText] = kMTFilterSelect,
/// Return the sign attributes on the currently refreshed row.
///
/// @param[out] sattrs Output array for sign text and texthl id
-/// @param[out] line_attr Highest priority linehl id
-/// @param[out] cul_attr Highest priority culhl id
-/// @param[out] num_attr Highest priority numhl id
+/// @param[out] line_id Highest priority linehl id
+/// @param[out] cul_id Highest priority culhl id
+/// @param[out] num_id Highest priority numhl id
void decor_redraw_signs(win_T *wp, buf_T *buf, int row, SignTextAttrs sattrs[], int *line_id,
int *cul_id, int *num_id)
{
@@ -870,16 +1018,18 @@ bool decor_redraw_eol(win_T *wp, DecorState *state, int *eol_attr, int eol_col)
{
decor_redraw_col(wp, MAXCOL, MAXCOL, false, state);
state->eol_col = eol_col;
+
+ int const count = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
bool has_virt_pos = false;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange item = kv_A(state->active, i);
- if (item.start_row == state->row && decor_virt_pos(&item)) {
- has_virt_pos = true;
- }
+ for (int i = 0; i < count; i++) {
+ DecorRange *r = &slots[indices[i]].range;
+ has_virt_pos |= r->start_row == state->row && decor_virt_pos(r);
- if (item.kind == kDecorKindHighlight
- && (item.data.sh.flags & kSHHlEol) && item.start_row <= state->row) {
- *eol_attr = hl_combine_attr(*eol_attr, item.attr_id);
+ if (r->kind == kDecorKindHighlight && (r->data.sh.flags & kSHHlEol)) {
+ *eol_attr = hl_combine_attr(*eol_attr, r->attr_id);
}
}
return has_virt_pos;
diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h
index 1b595fb86f..bdbb1795cb 100644
--- a/src/nvim/decoration.h
+++ b/src/nvim/decoration.h
@@ -15,8 +15,8 @@
// actual Decor* data is in decoration_defs.h
/// Keep in sync with VirtTextPos in decoration_defs.h
-EXTERN const char *const virt_text_pos_str[]
-INIT( = { "eol", "overlay", "win_col", "right_align", "inline" });
+EXTERN const char *const virt_text_pos_str[] INIT( = { "eol", "eol_right_align", "inline",
+ "overlay", "right_align", "win_col" });
/// Keep in sync with HlMode in decoration_defs.h
EXTERN const char *const hl_mode_str[] INIT( = { "", "replace", "combine", "blend" });
@@ -27,13 +27,19 @@ typedef enum {
kDecorKindVirtText,
kDecorKindVirtLines,
kDecorKindUIWatched,
-} DecorRangeKind;
+} DecorRangeKindEnum;
+
+typedef uint8_t DecorRangeKind;
typedef struct {
int start_row;
int start_col;
int end_row;
int end_col;
+ int ordering; ///< range insertion order
+ DecorPriority priority;
+ bool owned; ///< ephemeral decoration, free memory immediately
+ DecorRangeKind kind;
// next pointers MUST NOT be used, these are separate ranges
// vt->next could be pointing to freelist memory at this point
union {
@@ -46,9 +52,6 @@ typedef struct {
} ui;
} data;
int attr_id; ///< cached lookup of inl.hl_id if it was a highlight
- bool owned; ///< ephemeral decoration, free memory immediately
- DecorPriority priority;
- DecorRangeKind kind;
/// Screen column to draw the virtual text.
/// When -1, it should be drawn on the current screen line after deciding where.
/// When -3, it may be drawn at a position yet to be assigned.
@@ -57,9 +60,28 @@ typedef struct {
int draw_col;
} DecorRange;
+/// DecorRange can be removed from `DecorState` list in any order,
+/// so we track available slots using a freelist (with `next_free_i`).
+/// The list head is in `DecorState.free_slot_i`.
+typedef union {
+ DecorRange range;
+ int next_free_i;
+} DecorRangeSlot;
+
typedef struct {
MarkTreeIter itr[1];
- kvec_t(DecorRange) active;
+ kvec_t(DecorRangeSlot) slots;
+ kvec_t(int) ranges_i;
+ /// Indices in [0; current_end) of `ranges_i` point to ranges that start
+ /// before current position. Sorted by priority and order of insertion.
+ int current_end;
+ /// Indices in [future_begin, kv_size(ranges_i)) of `ranges_i` point to
+ /// ranges that start after current position. Sorted by starting position.
+ int future_begin;
+ /// Head of DecorRangeSlot freelist. -1 if none are freed.
+ int free_slot_i;
+ /// Index for keeping track of range insertion order.
+ int new_range_ordering;
win_T *win;
int top_row;
int row;
@@ -74,6 +96,7 @@ typedef struct {
TriState spell;
bool running_decor_provider;
+ bool itr_valid;
} DecorState;
EXTERN DecorState decor_state INIT( = { 0 });
@@ -83,4 +106,14 @@ EXTERN kvec_t(DecorSignHighlight) decor_items INIT( = KV_INITIAL_VALUE);
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "decoration.h.generated.h"
+# include "decoration.h.inline.generated.h"
#endif
+
+static inline int decor_redraw_col(win_T *wp, int col, int win_col, bool hidden, DecorState *state)
+ FUNC_ATTR_ALWAYS_INLINE
+{
+ if (col <= state->col_until) {
+ return state->current;
+ }
+ return decor_redraw_col_impl(wp, col, win_col, hidden, state);
+}
diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h
index 58ba93a7ba..36ad6df7a0 100644
--- a/src/nvim/decoration_defs.h
+++ b/src/nvim/decoration_defs.h
@@ -19,10 +19,11 @@ typedef kvec_t(VirtTextChunk) VirtText;
/// Keep in sync with virt_text_pos_str[] in decoration.h
typedef enum {
kVPosEndOfLine,
+ kVPosEndOfLineRightAlign,
+ kVPosInline,
kVPosOverlay,
- kVPosWinCol,
kVPosRightAlign,
- kVPosInline,
+ kVPosWinCol,
} VirtTextPos;
typedef kvec_t(struct virt_line { VirtText line; bool left_col; }) VirtLines;
diff --git a/src/nvim/decoration_provider.c b/src/nvim/decoration_provider.c
index 74f444d8e8..7c99fbf889 100644
--- a/src/nvim/decoration_provider.c
+++ b/src/nvim/decoration_provider.c
@@ -55,14 +55,13 @@ static bool decor_provider_invoke(int provider_idx, const char *name, LuaRef ref
// We get the provider here via an index in case the above call to nlua_call_ref causes
// decor_providers to be reallocated.
DecorProvider *provider = &kv_A(decor_providers, provider_idx);
-
if (!ERROR_SET(&err)
&& api_object_to_bool(ret, "provider %s retval", default_true, &err)) {
provider->error_count = 0;
return true;
}
- if (ERROR_SET(&err)) {
+ if (ERROR_SET(&err) && provider->error_count < DP_MAX_ERROR) {
decor_provider_error(provider, name, err.msg);
provider->error_count++;
@@ -121,7 +120,8 @@ void decor_providers_invoke_win(win_T *wp)
{
// this might change in the future
// then we would need decor_state.running_decor_provider just like "on_line" below
- assert(kv_size(decor_state.active) == 0);
+ assert(decor_state.current_end == 0
+ && decor_state.future_begin == (int)kv_size(decor_state.ranges_i));
if (kv_size(decor_providers) > 0) {
validate_botline(wp);
@@ -155,7 +155,7 @@ void decor_providers_invoke_win(win_T *wp)
/// @param row Row to invoke line callback for
/// @param[out] has_decor Set when at least one provider invokes a line callback
/// @param[out] err Provider error
-void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
+void decor_providers_invoke_line(win_T *wp, int row)
{
decor_state.running_decor_provider = true;
for (size_t i = 0; i < kv_size(decor_providers); i++) {
@@ -165,9 +165,7 @@ void decor_providers_invoke_line(win_T *wp, int row, bool *has_decor)
ADD_C(args, WINDOW_OBJ(wp->handle));
ADD_C(args, BUFFER_OBJ(wp->w_buffer->handle));
ADD_C(args, INTEGER_OBJ(row));
- if (decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) {
- *has_decor = true;
- } else {
+ if (!decor_provider_invoke((int)i, "line", p->redraw_line, args, true)) {
// return 'false' or error: skip rest of this window
kv_A(decor_providers, i).state = kDecorProviderWinDisabled;
}
diff --git a/src/nvim/diff.c b/src/nvim/diff.c
index f1dd08f0e6..68441f7adc 100644
--- a/src/nvim/diff.c
+++ b/src/nvim/diff.c
@@ -44,6 +44,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
@@ -1621,6 +1622,11 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
} else {
// second overlap of new block with existing block
dp->df_count[idx_new] += (linenr_T)hunk->count_new;
+ if ((dp->df_lnum[idx_new] + dp->df_count[idx_new] - 1)
+ > curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count) {
+ dp->df_count[idx_new] = curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count
+ - dp->df_lnum[idx_new] + 1;
+ }
}
// Adjust the size of the block to include all the lines to the
@@ -1631,6 +1637,11 @@ static void process_hunk(diff_T **dpp, diff_T **dprevp, int idx_orig, int idx_ne
if (off < 0) {
// new change ends in existing block, adjust the end
dp->df_count[idx_new] += -off;
+ if ((dp->df_lnum[idx_new] + dp->df_count[idx_new] - 1)
+ > curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count) {
+ dp->df_count[idx_new] = curtab->tp_diffbuf[idx_new]->b_ml.ml_line_count
+ - dp->df_lnum[idx_new] + 1;
+ }
off = 0;
}
@@ -1809,7 +1820,8 @@ static void find_top_diff_block(diff_T **thistopdiff, diff_T **nextblockblock, i
topdiffchange = 0;
}
- // check if the fromwin topline is matched by the current diff. if so, set it to the top of the diff block
+ // check if the fromwin topline is matched by the current diff. if so,
+ // set it to the top of the diff block
if (topline >= topdiff->df_lnum[fromidx] && topline <=
(topdiff->df_lnum[fromidx] + topdiff->df_count[fromidx])) {
// this line is inside the current diff block, so we will save the
@@ -1854,8 +1866,10 @@ static void count_filler_lines_and_topline(int *curlinenum_to, int *linesfiller,
}
} else {
(*linesfiller) = 0;
- ch_virtual_lines = get_max_diff_length(curdif);
- isfiller = (curdif->df_count[toidx] ? false : true);
+ if (curdif) {
+ ch_virtual_lines = get_max_diff_length(curdif);
+ isfiller = (curdif->df_count[toidx] ? false : true);
+ }
if (isfiller) {
while (curdif && curdif->df_next && curdif->df_lnum[toidx] ==
curdif->df_next->df_lnum[toidx]
@@ -2010,10 +2024,15 @@ static void run_linematch_algorithm(diff_T *dp)
size_t ndiffs = 0;
for (int i = 0; i < DB_COUNT; i++) {
if (curtab->tp_diffbuf[i] != NULL) {
- // write the contents of the entire buffer to
- // diffbufs_mm[diffbuffers_count]
- diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs],
- dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1);
+ if (dp->df_count[i] > 0) {
+ // write the contents of the entire buffer to
+ // diffbufs_mm[diffbuffers_count]
+ diff_write_buffer(curtab->tp_diffbuf[i], &diffbufs_mm[ndiffs],
+ dp->df_lnum[i], dp->df_lnum[i] + dp->df_count[i] - 1);
+ } else {
+ diffbufs_mm[ndiffs].size = 0;
+ diffbufs_mm[ndiffs].ptr = NULL;
+ }
diffbufs[ndiffs] = &diffbufs_mm[ndiffs];
@@ -2049,6 +2068,12 @@ static void run_linematch_algorithm(diff_T *dp)
/// Returns > 0 for inserting that many filler lines above it (never happens
/// when 'diffopt' doesn't contain "filler").
/// This should only be used for windows where 'diff' is set.
+/// When diffopt contains linematch, a changed/added/deleted line
+/// may also have filler lines above it. In such a case, the possibilities
+/// are no longer mutually exclusive. The number of filler lines is
+/// returned from diff_check, and the integer 'linestatus' passed by
+/// pointer is set to -1 to indicate a changed line, and -2 to indicate an
+/// added line
///
/// @param wp
/// @param lnum
@@ -2102,7 +2127,8 @@ int diff_check_with_linestatus(win_T *wp, linenr_T lnum, int *linestatus)
// Useful for scrollbind calculations which need to count all the filler lines
// above the screen.
if (lnum >= wp->w_topline && lnum < wp->w_botline
- && !dp->is_linematched && diff_linematch(dp)) {
+ && !dp->is_linematched && diff_linematch(dp)
+ && diff_check_sanity(curtab, dp)) {
run_linematch_algorithm(dp);
}
@@ -2417,7 +2443,7 @@ int diffopt_changed(void)
char *p = p_dip;
while (*p != NUL) {
- // Note: Keep this in sync with p_dip_values
+ // Note: Keep this in sync with opt_dip_values.
if (strncmp(p, "filler", 6) == 0) {
p += 6;
diff_flags_new |= DIFF_FILLER;
@@ -2464,7 +2490,7 @@ int diffopt_changed(void)
p += 8;
diff_flags_new |= DIFF_INTERNAL;
} else if (strncmp(p, "algorithm:", 10) == 0) {
- // Note: Keep this in sync with p_dip_algorithm_values.
+ // Note: Keep this in sync with opt_dip_algorithm_values.
p += 10;
if (strncmp(p, "myers", 5) == 0) {
p += 5;
@@ -2745,7 +2771,7 @@ bool diff_infold(win_T *wp, linenr_T lnum)
void nv_diffgetput(bool put, size_t count)
{
if (bt_prompt(curbuf)) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
return;
}
diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c
index ea0d1ba708..f32123e686 100644
--- a/src/nvim/digraph.c
+++ b/src/nvim/digraph.c
@@ -22,7 +22,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/mapping.h"
@@ -1024,6 +1023,7 @@ static digr_T digraphdefault[] =
{ '?', '=', 0x2245 },
{ '?', '2', 0x2248 },
{ '=', '?', 0x224c },
+ { '.', '=', 0x2250 },
{ 'H', 'I', 0x2253 },
{ '!', '=', 0x2260 },
{ '=', '3', 0x2261 },
@@ -1715,6 +1715,7 @@ void listdigraphs(bool use_headers)
{
result_T previous = 0;
+ msg_ext_set_kind("list_cmd");
msg_putchar('\n');
const digr_T *dp = digraphdefault;
@@ -1954,16 +1955,16 @@ void f_digraph_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "digraph_getlist()" function
void f_digraph_getlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
+ if (tv_check_for_opt_bool_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
bool flag_list_all;
if (argvars[0].v_type == VAR_UNKNOWN) {
flag_list_all = false;
} else {
- bool error = false;
- varnumber_T flag = tv_get_number_chk(&argvars[0], &error);
- if (error) {
- return;
- }
+ varnumber_T flag = tv_get_bool(&argvars[0]);
flag_list_all = flag != 0;
}
@@ -2183,22 +2184,22 @@ static void keymap_unload(void)
/// @param fmt format string containing one %s item
/// @param buf buffer for the result
/// @param len length of buffer
-bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
+int get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
{
char *p;
if (wp->w_buffer->b_p_iminsert != B_IMODE_LMAP) {
- return false;
+ return 0;
}
buf_T *old_curbuf = curbuf;
win_T *old_curwin = curwin;
+ char to_evaluate[] = "b:keymap_name";
curbuf = wp->w_buffer;
curwin = wp;
- STRCPY(buf, "b:keymap_name"); // must be writable
emsg_skip++;
- char *s = p = eval_to_string(buf, false, false);
+ char *s = p = eval_to_string(to_evaluate, false, false);
emsg_skip--;
curbuf = old_curbuf;
curwin = old_curwin;
@@ -2209,9 +2210,12 @@ bool get_keymap_str(win_T *wp, char *fmt, char *buf, int len)
p = "lang";
}
}
- if (vim_snprintf(buf, (size_t)len, fmt, p) > len - 1) {
+ int plen = vim_snprintf(buf, (size_t)len, fmt, p);
+ xfree(s);
+ if (plen < 0 || plen > len - 1) {
buf[0] = NUL;
+ plen = 0;
}
- xfree(s);
- return buf[0] != NUL;
+
+ return plen;
}
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 79f3298eb4..37a42917b0 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -31,6 +31,7 @@
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
+#include "nvim/insexpand.h"
#include "nvim/mark_defs.h"
#include "nvim/marktree_defs.h"
#include "nvim/match.h"
@@ -80,6 +81,8 @@ typedef struct {
int cul_attr; ///< set when 'cursorline' active
int line_attr; ///< attribute for the whole line
int line_attr_lowprio; ///< low-priority attribute for the line
+ int sign_num_attr; ///< line number attribute (sign numhl)
+ int sign_cul_attr; ///< cursorline sign attribute (sign culhl)
int fromcol; ///< start of inverting
int tocol; ///< end of inverting
@@ -251,12 +254,20 @@ static int line_putchar(buf_T *buf, const char **pp, schar_T *dest, int maxcells
static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int win_row)
{
- DecorState *state = &decor_state;
- const int max_col = wp->w_grid.cols;
+ DecorState *const state = &decor_state;
+ int const max_col = wp->w_grid.cols;
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);
+ bool const do_eol = state->eol_col > -1;
+
+ int const end = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ /// Total width of all virtual text with "eol_right_align" alignment
+ int totalWidthOfEolRightAlignedVirtText = 0;
+
+ for (int i = 0; i < end; i++) {
+ DecorRange *item = &slots[indices[i]].range;
if (!(item->start_row == state->row && decor_virt_pos(item))) {
continue;
}
@@ -269,7 +280,44 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
if (decor_virt_pos(item) && item->draw_col == -1) {
bool updated = true;
VirtTextPos pos = decor_virt_pos_kind(item);
- if (pos == kVPosRightAlign) {
+
+ if (do_eol && pos == kVPosEndOfLineRightAlign) {
+ int eolOffset = 0;
+ if (totalWidthOfEolRightAlignedVirtText == 0) {
+ // Look ahead to the remaining decor items
+ for (int j = i; j < end; j++) {
+ /// A future decor to be handled in this function's call
+ DecorRange *lookaheadItem = &slots[indices[j]].range;
+
+ if (lookaheadItem->start_row != state->row
+ || !decor_virt_pos(lookaheadItem)
+ || lookaheadItem->draw_col != -1) {
+ continue;
+ }
+
+ /// The Virtual Text of the decor item we're looking ahead to
+ DecorVirtText *lookaheadVt = NULL;
+ if (item->kind == kDecorKindVirtText) {
+ assert(item->data.vt);
+ lookaheadVt = item->data.vt;
+ }
+
+ if (decor_virt_pos_kind(lookaheadItem) == kVPosEndOfLineRightAlign) {
+ // An extra space is added for single character spacing in EOL alignment
+ totalWidthOfEolRightAlignedVirtText += (lookaheadVt->width + 1);
+ }
+ }
+
+ // Remove one space from the total width since there's no single space after the last entry
+ totalWidthOfEolRightAlignedVirtText--;
+
+ if (totalWidthOfEolRightAlignedVirtText <= (right_pos - state->eol_col)) {
+ eolOffset = right_pos - totalWidthOfEolRightAlignedVirtText - state->eol_col;
+ }
+ }
+
+ item->draw_col = state->eol_col + eolOffset;
+ } else if (pos == kVPosRightAlign) {
right_pos -= vt->width;
item->draw_col = right_pos;
} else if (pos == kVPosEndOfLine && do_eol) {
@@ -296,7 +344,7 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int
int vcol = item->draw_col - col_off;
int col = draw_virt_text_item(buf, item->draw_col, vt->data.virt_text,
vt->hl_mode, max_col, vcol);
- if (vt->pos == kVPosEndOfLine && do_eol) {
+ if (do_eol && ((vt->pos == kVPosEndOfLine) || (vt->pos == kVPosEndOfLineRightAlign))) {
state->eol_col = col + 1;
}
*end_col = MAX(*end_col, col);
@@ -360,14 +408,14 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
// TODO(bfredl): integrate with grid.c linebuf code? madness?
static void draw_col_buf(win_T *wp, winlinevars_T *wlv, const char *text, size_t len, int attr,
- bool vcol)
+ const colnr_T *fold_vcol, bool inc_vcol)
{
const char *ptr = text;
while (ptr < text + len && wlv->off < wp->w_grid.cols) {
int cells = line_putchar(wp->w_buffer, &ptr, &linebuf_char[wlv->off],
wp->w_grid.cols - wlv->off, wlv->off);
int myattr = attr;
- if (vcol) {
+ if (inc_vcol) {
advance_color_col(wlv, wlv->vcol);
if (wlv->color_cols && wlv->vcol == *wlv->color_cols) {
myattr = hl_combine_attr(win_hl_attr(wp, HLF_MC), myattr);
@@ -375,7 +423,7 @@ static void draw_col_buf(win_T *wp, winlinevars_T *wlv, const char *text, size_t
}
for (int c = 0; c < cells; c++) {
linebuf_attr[wlv->off] = myattr;
- linebuf_vcol[wlv->off] = vcol ? wlv->vcol++ : -1;
+ linebuf_vcol[wlv->off] = inc_vcol ? wlv->vcol++ : fold_vcol ? *(fold_vcol++) : -1;
wlv->off++;
}
}
@@ -391,11 +439,11 @@ static void draw_col_fill(winlinevars_T *wlv, schar_T fillchar, int width, int a
}
/// Return true if CursorLineSign highlight is to be used.
-static bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)
+bool use_cursor_line_highlight(win_T *wp, linenr_T lnum)
{
return wp->w_p_cul
&& lnum == wp->w_cursorline
- && (wp->w_p_culopt_flags & CULOPT_NBR);
+ && (wp->w_p_culopt_flags & kOptCuloptFlagNumber);
}
/// Setup for drawing the 'foldcolumn', if there is one.
@@ -404,7 +452,7 @@ static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv)
int fdc = compute_foldcolumn(wp, 0);
if (fdc > 0) {
int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLF : HLF_FC);
- fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, &wlv->off, NULL);
+ fill_foldcolumn(wp, wlv->foldinfo, wlv->lnum, attr, fdc, &wlv->off, NULL, NULL);
}
}
@@ -413,8 +461,9 @@ static void draw_foldcolumn(win_T *wp, winlinevars_T *wlv)
/// @param fdc Current width of the foldcolumn
/// @param[out] wlv_off Pointer to linebuf offset, incremented for default column
/// @param[out] out_buffer Char array to fill, only used for 'statuscolumn'
+/// @param[out] out_vcol vcol array to fill, only used for 'statuscolumn'
void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, int fdc, int *wlv_off,
- schar_T *out_buffer)
+ colnr_T *out_vcol, schar_T *out_buffer)
{
bool closed = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
int level = foldinfo.fi_level;
@@ -440,10 +489,12 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
symbol = schar_from_ascii('>');
}
+ int vcol = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3;
if (out_buffer) {
+ out_vcol[i] = vcol;
out_buffer[i] = symbol;
} else {
- linebuf_vcol[*wlv_off] = i >= level ? -1 : (i == closedcol - 1 && closed) ? -2 : -3;
+ linebuf_vcol[*wlv_off] = vcol;
linebuf_attr[*wlv_off] = attr;
linebuf_char[(*wlv_off)++] = symbol;
}
@@ -454,14 +505,15 @@ void fill_foldcolumn(win_T *wp, foldinfo_T foldinfo, linenr_T lnum, int attr, in
/// 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. If there is no
/// sign, draw blank cells instead.
-static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, int sign_cul_attr)
+static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx)
{
SignTextAttrs sattr = wlv->sattrs[sign_idx];
+ int scl_attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
if (sattr.text[0] && wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
- int attr = (use_cursor_line_highlight(wp, wlv->lnum) && sign_cul_attr)
- ? sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0;
int fill = nrcol ? number_width(wp) + 1 : SIGN_WIDTH;
+ int attr = wlv->sign_cul_attr ? wlv->sign_cul_attr : sattr.hl_id ? syn_id2attr(sattr.hl_id) : 0;
+ attr = hl_combine_attr(scl_attr, attr);
draw_col_fill(wlv, schar_from_ascii(' '), fill, attr);
int sign_pos = wlv->off - SIGN_WIDTH - (int)nrcol;
assert(sign_pos >= 0);
@@ -469,8 +521,7 @@ static void draw_sign(bool nrcol, win_T *wp, winlinevars_T *wlv, int sign_idx, i
linebuf_char[sign_pos + 1] = sattr.text[1];
} else {
assert(!nrcol); // handled in draw_lnum_col()
- int attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
- draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, attr);
+ draw_col_fill(wlv, schar_from_ascii(' '), SIGN_WIDTH, scl_attr);
}
}
@@ -507,10 +558,10 @@ static bool use_cursor_line_nr(win_T *wp, winlinevars_T *wlv)
{
return wp->w_p_cul
&& wlv->lnum == wp->w_cursorline
- && (wp->w_p_culopt_flags & CULOPT_NBR)
+ && (wp->w_p_culopt_flags & kOptCuloptFlagNumber)
&& (wlv->row == wlv->startrow + wlv->filler_lines
|| (wlv->row > wlv->startrow + wlv->filler_lines
- && (wp->w_p_culopt_flags & CULOPT_LINE)));
+ && (wp->w_p_culopt_flags & kOptCuloptFlagLine)));
}
static int get_line_number_attr(win_T *wp, winlinevars_T *wlv)
@@ -537,7 +588,7 @@ static int get_line_number_attr(win_T *wp, winlinevars_T *wlv)
/// Display the absolute or relative line number. After the first row fill with
/// blanks when the 'n' flag isn't in 'cpo'.
-static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int sign_cul_attr)
+static void draw_lnum_col(win_T *wp, winlinevars_T *wlv)
{
bool has_cpo_n = vim_strchr(p_cpo, CPO_NUMCOL) != NULL;
@@ -550,12 +601,12 @@ static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int
// then display the sign instead of the line number.
if (wp->w_minscwidth == SCL_NUM && wlv->sattrs[0].text[0]
&& wlv->row == wlv->startrow + wlv->filler_lines && wlv->filler_todo <= 0) {
- draw_sign(true, wp, wlv, 0, sign_cul_attr);
+ draw_sign(true, wp, wlv, 0);
} else {
// Draw the line number (empty space after wrapping).
int width = number_width(wp) + 1;
- int attr = (sign_num_attr > 0 && wlv->filler_todo <= 0)
- ? sign_num_attr : get_line_number_attr(wp, wlv);
+ int attr = hl_combine_attr(get_line_number_attr(wp, wlv),
+ wlv->filler_todo <= 0 ? wlv->sign_num_attr : 0);
if (wlv->row == wlv->startrow + wlv->filler_lines
&& (wp->w_skipcol == 0 || wlv->row > 0 || (wp->w_p_nu && wp->w_p_rnu))) {
char buf[32];
@@ -569,7 +620,7 @@ static void draw_lnum_col(win_T *wp, winlinevars_T *wlv, int sign_num_attr, int
char *num = skipwhite(buf);
rl_mirror_ascii(num, skiptowhite(num));
}
- draw_col_buf(wp, wlv, buf, (size_t)width, attr, false);
+ draw_col_buf(wp, wlv, buf, (size_t)width, attr, NULL, false);
} else {
draw_col_fill(wlv, schar_from_ascii(' '), width, attr);
}
@@ -624,22 +675,27 @@ static void draw_statuscol(win_T *wp, winlinevars_T *wlv, linenr_T lnum, int vir
char *p = buf;
char transbuf[MAXPATHL];
- int attr = stcp->num_attr;
+ colnr_T *fold_vcol = NULL;
size_t len = strlen(buf);
+ int scl_attr = win_hl_attr(wp, use_cursor_line_highlight(wp, wlv->lnum) ? HLF_CLS : HLF_SC);
+ int num_attr = hl_combine_attr(get_line_number_attr(wp, wlv),
+ wlv->filler_todo <= 0 ? wlv->sign_num_attr : 0);
+ int cur_attr = num_attr;
// Draw each segment with the specified highlighting.
for (stl_hlrec_t *sp = stcp->hlrec; sp->start != NULL; sp++) {
ptrdiff_t textlen = sp->start - p;
// Make all characters printable.
size_t translen = transstr_buf(p, textlen, transbuf, MAXPATHL, true);
- draw_col_buf(wp, wlv, transbuf, translen, attr, false);
+ draw_col_buf(wp, wlv, transbuf, translen, cur_attr, fold_vcol, false);
+ int attr = sp->item == STL_SIGNCOL ? scl_attr : sp->item == STL_FOLDCOL ? 0 : num_attr;
+ cur_attr = hl_combine_attr(attr, sp->userhl < 0 ? syn_id2attr(-sp->userhl) : 0);
+ fold_vcol = sp->item == STL_FOLDCOL ? stcp->fold_vcol : NULL;
p = sp->start;
- int hl = sp->userhl;
- attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr;
}
size_t translen = transstr_buf(p, buf + len - p, transbuf, MAXPATHL, true);
- draw_col_buf(wp, wlv, transbuf, translen, attr, false);
- draw_col_fill(wlv, schar_from_ascii(' '), stcp->width - width, stcp->num_attr);
+ draw_col_buf(wp, wlv, transbuf, translen, cur_attr, fold_vcol, false);
+ draw_col_fill(wlv, schar_from_ascii(' '), stcp->width - width, cur_attr);
}
static void handle_breakindent(win_T *wp, winlinevars_T *wlv)
@@ -711,7 +767,7 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv)
// Combine 'showbreak' with 'cursorline', prioritizing 'showbreak'.
int attr = hl_combine_attr(wlv->cul_attr, win_hl_attr(wp, HLF_AT));
colnr_T vcol_before = wlv->vcol;
- draw_col_buf(wp, wlv, sbr, strlen(sbr), attr, true);
+ draw_col_buf(wp, wlv, sbr, strlen(sbr), attr, NULL, true);
wlv->vcol_sbr = wlv->vcol;
// Correct start of highlighted area for 'showbreak'.
@@ -756,17 +812,28 @@ static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v)
if (wlv->virt_inline_i < kv_size(wlv->virt_inline)) {
return true;
}
- DecorState *state = &decor_state;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
- if (item->start_row != state->row
- || item->kind != kDecorKindVirtText
- || item->data.vt->pos != kVPosInline
- || item->data.vt->width == 0) {
- continue;
- }
- if (item->draw_col >= -1 && item->start_col >= v) {
- return true;
+
+ int const count = (int)kv_size(decor_state.ranges_i);
+ int const cur_end = decor_state.current_end;
+ int const fut_beg = decor_state.future_begin;
+ int *const indices = decor_state.ranges_i.items;
+ DecorRangeSlot *const slots = decor_state.slots.items;
+
+ int const beg_pos[] = { 0, fut_beg };
+ int const end_pos[] = { cur_end, count };
+
+ for (int pos_i = 0; pos_i < 2; pos_i++) {
+ for (int i = beg_pos[pos_i]; i < end_pos[pos_i]; i++) {
+ DecorRange *item = &slots[indices[i]].range;
+ if (item->start_row != decor_state.row
+ || item->kind != kDecorKindVirtText
+ || item->data.vt->pos != kVPosInline
+ || item->data.vt->width == 0) {
+ continue;
+ }
+ if (item->draw_col >= -1 && item->start_col >= v) {
+ return true;
+ }
}
}
return false;
@@ -780,8 +847,12 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
wlv->virt_inline = VIRTTEXT_EMPTY;
wlv->virt_inline_i = 0;
DecorState *state = &decor_state;
- for (size_t i = 0; i < kv_size(state->active); i++) {
- DecorRange *item = &kv_A(state->active, i);
+ int const end = state->current_end;
+ int *const indices = state->ranges_i.items;
+ DecorRangeSlot *const slots = state->slots.items;
+
+ for (int i = 0; i < end; i++) {
+ DecorRange *item = &slots[indices[i]].range;
if (item->draw_col == -3) {
// No more inline virtual text before this non-inline virtual text item,
// so its position can be decided now.
@@ -913,6 +984,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
colnr_T vcol_prev = -1; // "wlv.vcol" of previous character
ScreenGrid *grid = &wp->w_grid; // grid specific to the window
+ const bool in_curline = wp == curwin && lnum == curwin->w_cursor.lnum;
const bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0;
const bool has_foldtext = has_fold && *wp->w_p_fdt != NUL;
@@ -932,7 +1004,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
bool area_highlighting = false; // Visual or incsearch highlighting in this line
int vi_attr = 0; // attributes for Visual and incsearch highlighting
int area_attr = 0; // attributes desired by highlighting
- int search_attr = 0; // attributes desired by 'hlsearch'
+ int search_attr = 0; // attributes desired by 'hlsearch' or ComplMatchIns
int vcol_save_attr = 0; // saved attr for 'cursorcolumn'
int decor_attr = 0; // attributes desired by syntax and extmarks
bool has_syntax = false; // this buffer has syntax highl.
@@ -946,7 +1018,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
int spell_attr = 0; // attributes desired by spelling
int word_end = 0; // last byte with same spell_attr
int cur_checked_col = 0; // checked column for current line
- bool extra_check = 0; // has syntax or linebreak
+ bool extra_check = false; // has extra highlighting
int multi_attr = 0; // attributes desired by multibyte
int mb_l = 1; // multi-byte byte length
int mb_c = 0; // decoded multi-byte character
@@ -1028,12 +1100,12 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
}
- has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
-
if (!end_fill) {
- decor_providers_invoke_line(wp, lnum - 1, &has_decor);
+ decor_providers_invoke_line(wp, lnum - 1);
}
+ has_decor = decor_redraw_line(wp, lnum - 1, &decor_state);
+
if (has_decor) {
extra_check = true;
}
@@ -1096,7 +1168,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
// Check if the char under the cursor should be inverted (highlighted).
- if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin
+ if (!highlight_match && in_curline
&& cursor_is_block_during_visual(*p_sel == 'e')) {
noinvcur = true;
}
@@ -1165,11 +1237,11 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
wlv.filler_todo = wlv.filler_lines;
// Cursor line highlighting for 'cursorline' in the current window.
- if (wp->w_p_cul && wp->w_p_culopt_flags != CULOPT_NBR && lnum == wp->w_cursorline
+ if (wp->w_p_cul && wp->w_p_culopt_flags != kOptCuloptFlagNumber && lnum == wp->w_cursorline
// Do not show the cursor line in the text when Visual mode is active,
// because it's not clear what is selected then.
&& !(wp == curwin && VIsual_active)) {
- cul_screenline = (is_wrapped && (wp->w_p_culopt_flags & CULOPT_SCRLINE));
+ cul_screenline = (is_wrapped && (wp->w_p_culopt_flags & kOptCuloptFlagScreenline));
if (!cul_screenline) {
apply_cursorline_highlight(wp, &wlv);
} else {
@@ -1178,11 +1250,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
area_highlighting = true;
}
- int line_attr = 0;
- int sign_cul_attr = 0;
- int sign_num_attr = 0;
+ int sign_line_attr = 0;
// TODO(bfredl, vigoux): line_attr should not take priority over decoration!
- decor_redraw_signs(wp, buf, wlv.lnum - 1, wlv.sattrs, &line_attr, &sign_cul_attr, &sign_num_attr);
+ decor_redraw_signs(wp, buf, wlv.lnum - 1, wlv.sattrs,
+ &sign_line_attr, &wlv.sign_cul_attr, &wlv.sign_num_attr);
statuscol_T statuscol = { 0 };
if (*wp->w_p_stc != NUL) {
@@ -1191,19 +1262,15 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
statuscol.sattrs = wlv.sattrs;
statuscol.foldinfo = foldinfo;
statuscol.width = win_col_off(wp) - (wp == cmdwin_win);
- statuscol.use_cul = use_cursor_line_highlight(wp, lnum);
- statuscol.sign_cul_id = statuscol.use_cul ? sign_cul_attr : 0;
- statuscol.num_attr = sign_num_attr > 0 ? syn_id2attr(sign_num_attr) : 0;
- } else {
- if (sign_cul_attr > 0) {
- sign_cul_attr = syn_id2attr(sign_cul_attr);
- }
- if (sign_num_attr > 0) {
- sign_num_attr = syn_id2attr(sign_num_attr);
- }
+ statuscol.sign_cul_id = use_cursor_line_highlight(wp, lnum) ? wlv.sign_cul_attr : 0;
+ } else if (wlv.sign_cul_attr > 0) {
+ wlv.sign_cul_attr = use_cursor_line_highlight(wp, lnum) ? syn_id2attr(wlv.sign_cul_attr) : 0;
+ }
+ if (wlv.sign_num_attr > 0) {
+ wlv.sign_num_attr = syn_id2attr(wlv.sign_num_attr);
}
- if (line_attr > 0) {
- wlv.line_attr = syn_id2attr(line_attr);
+ if (sign_line_attr > 0) {
+ wlv.line_attr = syn_id2attr(sign_line_attr);
}
// Highlight the current line in the quickfix window.
@@ -1472,6 +1539,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
ptr = line + v; // "line" may have been updated
}
+ if ((State & MODE_INSERT) && in_curline && ins_compl_win_active(wp)) {
+ area_highlighting = true;
+ }
+
win_line_start(wp, &wlv);
bool draw_cols = true;
int leftcols_width = 0;
@@ -1522,9 +1593,6 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// skip columns
} else if (statuscol.draw) {
// Draw 'statuscolumn' if it is set.
- if (sign_num_attr == 0) {
- statuscol.num_attr = get_line_number_attr(wp, &wlv);
- }
const int v = (int)(ptr - line);
draw_statuscol(wp, &wlv, lnum, wlv.row - startrow - wlv.filler_lines, col_rows, &statuscol);
if (wp->w_redr_statuscol) {
@@ -1541,10 +1609,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// wp->w_scwidth is zero if signcol=number is used
for (int sign_idx = 0; sign_idx < wp->w_scwidth; sign_idx++) {
- draw_sign(false, wp, &wlv, sign_idx, sign_cul_attr);
+ draw_sign(false, wp, &wlv, sign_idx);
}
- draw_lnum_col(wp, &wlv, sign_num_attr, sign_cul_attr);
+ draw_lnum_col(wp, &wlv);
}
win_col_offset = wlv.off;
@@ -1608,8 +1676,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
}
// When still displaying '$' of change command, stop at cursor.
- if (dollar_vcol >= 0 && wp == curwin
- && lnum == wp->w_cursor.lnum && wlv.vcol >= wp->w_virtcol) {
+ if (dollar_vcol >= 0 && in_curline && wlv.vcol >= wp->w_virtcol) {
draw_virt_text(wp, buf, win_col_offset, &wlv.col, wlv.row);
// don't clear anything after wlv.col
wlv_put_linebuf(wp, &wlv, wlv.col, false, bg_attr, 0);
@@ -1718,6 +1785,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (*ptr == NUL) {
has_match_conc = 0;
}
+
+ // Check if ComplMatchIns highlight is needed.
+ if ((State & MODE_INSERT) && in_curline && ins_compl_win_active(wp)) {
+ int ins_match_attr = ins_compl_col_range_attr((int)(ptr - line));
+ if (ins_match_attr > 0) {
+ search_attr = hl_combine_attr(search_attr, ins_match_attr);
+ }
+ }
}
if (wlv.diff_hlf != (hlf_T)0) {
@@ -1949,7 +2024,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
decor_attr = 0;
if (extra_check) {
- const bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
+ const bool no_plain_buffer = (wp->w_s->b_p_spo_flags & kOptSpoFlagNoplainbuffer) != 0;
bool can_spell = !no_plain_buffer;
// Get extmark and syntax attributes, unless still at the start of the line
@@ -2324,7 +2399,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
if (wlv.n_extra == 0) {
wlv.n_extra = byte2cells(mb_c) - 1;
}
- if ((dy_flags & DY_UHEX) && wp->w_p_rl) {
+ if ((dy_flags & kOptDyFlagUhex) && wp->w_p_rl) {
rl_mirror_ascii(wlv.p_extra, NULL); // reverse "<12>"
}
wlv.sc_extra = NUL;
@@ -2425,8 +2500,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// With 'virtualedit' we may never reach cursor position, but we still
// need to correct the cursor column, so do that at end of line.
if (!did_wcol && wlv.filler_todo <= 0
- && wp == curwin && lnum == wp->w_cursor.lnum
- && conceal_cursor_line(wp)
+ && in_curline && conceal_cursor_line(wp)
&& (wlv.vcol + wlv.skip_cells >= wp->w_virtcol || mb_schar == NUL)) {
wp->w_wcol = wlv.col - wlv.boguscols;
if (wlv.vcol + wlv.skip_cells < wp->w_virtcol) {
@@ -2619,7 +2693,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s
// 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) {
+ if (in_curline) {
curwin->w_cline_row = startrow;
curwin->w_cline_height = wlv.row - startrow;
curwin->w_cline_folded = has_fold;
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index e90a0d945f..4d7f80bf76 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -68,7 +68,6 @@
#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
-#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/decoration_defs.h"
#include "nvim/decoration_provider.h"
@@ -178,14 +177,11 @@ bool default_grid_alloc(void)
resizing = true;
// 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.
+ // when Rows and Columns have been set.
if ((default_grid.chars != NULL
&& Rows == default_grid.rows
&& Columns == default_grid.cols)
- || Rows == 0
- || Columns == 0
- || (!full_screen && default_grid.chars == NULL)) {
+ || Rows == 0 || Columns == 0) {
resizing = false;
return false;
}
@@ -290,9 +286,23 @@ void screen_resize(int width, int height)
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;
+ if (!ui_has(kUIMessages)) {
+ // clamp 'cmdheight'
+ int max_p_ch = Rows - min_rows(curtab) + 1;
+ if (p_ch > 0 && p_ch > max_p_ch) {
+ p_ch = MAX(max_p_ch, 1);
+ curtab->tp_ch_used = p_ch;
+ }
+ // clamp 'cmdheight' for other tab pages
+ FOR_ALL_TABS(tp) {
+ if (tp == curtab) {
+ continue; // already set above
+ }
+ int max_tp_ch = Rows - min_rows(tp) + 1;
+ if (tp->tp_ch_used > 0 && tp->tp_ch_used > max_tp_ch) {
+ tp->tp_ch_used = MAX(max_tp_ch, 1);
+ }
+ }
}
height = Rows;
width = Columns;
@@ -396,7 +406,7 @@ void check_screensize(void)
{
// Limit Rows and Columns to avoid an overflow in Rows * Columns.
// need room for one window and command line
- Rows = MIN(MAX(Rows, min_rows()), 1000);
+ Rows = MIN(MAX(Rows, min_rows_for_all_tabpages()), 1000);
Columns = MIN(MAX(Columns, MIN_COLUMNS), 10000);
}
@@ -665,6 +675,10 @@ int update_screen(void)
updating_screen = false;
+ if (need_maketitle) {
+ maketitle();
+ }
+
// Clear or redraw the command line. Done last, because scrolling may
// mess up the command line.
if (clear_cmdline || redraw_cmdline || redraw_mode) {
@@ -678,8 +692,11 @@ int update_screen(void)
decor_providers_invoke_end();
- // either cmdline is cleared, not drawn or mode is last drawn
- cmdline_was_last_drawn = false;
+ // Either cmdline is cleared, not drawn or mode is last drawn.
+ // This does not (necessarily) overwrite an external cmdline.
+ if (!ui_has(kUICmdline)) {
+ cmdline_was_last_drawn = false;
+ }
return OK;
}
@@ -842,6 +859,19 @@ void setcursor_mayforce(win_T *wp, bool force)
}
}
+/// Mark the title and icon for redraw if either of them uses statusline format.
+///
+/// @return whether either title or icon uses statusline format.
+bool redraw_custom_title_later(void)
+{
+ if ((p_icon && (stl_syntax & STL_IN_ICON))
+ || (p_title && (stl_syntax & STL_IN_TITLE))) {
+ need_maketitle = true;
+ return true;
+ }
+ return false;
+}
+
/// Show current cursor info in ruler and various other places
///
/// @param always if false, only show ruler if position has changed.
@@ -875,10 +905,7 @@ void show_cursor_info_later(bool force)
curwin->w_redr_status = true;
}
- if ((p_icon && (stl_syntax & STL_IN_ICON))
- || (p_title && (stl_syntax & STL_IN_TITLE))) {
- need_maketitle = true;
- }
+ redraw_custom_title_later();
}
curwin->w_stl_cursor = curwin->w_cursor;
@@ -1018,7 +1045,7 @@ int showmode(void)
if (State & MODE_LANGMAP) {
if (curwin->w_p_arab) {
msg_puts_hl(_(" Arabic"), hl_id, false);
- } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL)) {
+ } else if (get_keymap_str(curwin, " (%s)", NameBuff, MAXPATHL) > 0) {
msg_puts_hl(NameBuff, hl_id, false);
}
}
@@ -1092,9 +1119,13 @@ int showmode(void)
win_T *ruler_win = curwin->w_status_height == 0 ? curwin : lastwin_nofloating();
if (redrawing() && ruler_win->w_status_height == 0 && global_stl_height() == 0
&& !(p_ch == 0 && !ui_has(kUIMessages))) {
- grid_line_start(&msg_grid_adj, Rows - 1);
+ if (!ui_has(kUIMessages)) {
+ grid_line_start(&msg_grid_adj, Rows - 1);
+ }
win_redr_ruler(ruler_win);
- grid_line_flush();
+ if (!ui_has(kUIMessages)) {
+ grid_line_flush();
+ }
}
redraw_cmdline = false;
@@ -1498,10 +1529,12 @@ static void win_update(win_T *wp)
decor_providers_invoke_win(wp);
- if (win_redraw_signcols(wp)) {
- wp->w_lines_valid = 0;
- wp->w_redr_type = UPD_NOT_VALID;
- changed_line_abv_curs_win(wp);
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (win->w_buffer == wp->w_buffer && win_redraw_signcols(win)) {
+ win->w_lines_valid = 0;
+ changed_line_abv_curs_win(win);
+ redraw_later(win, UPD_NOT_VALID);
+ }
}
init_search_hl(wp, &screen_search_hl);
@@ -1882,7 +1915,7 @@ static void win_update(win_T *wp)
unsigned save_ve_flags = curwin->w_ve_flags;
if (curwin->w_p_lbr) {
- curwin->w_ve_flags = VE_ALL;
+ curwin->w_ve_flags = kOptVeFlagAll;
}
getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc);
@@ -1891,7 +1924,7 @@ static void win_update(win_T *wp)
// Highlight to the end of the line, unless 'virtualedit' has
// "block".
if (curwin->w_curswant == MAXCOL) {
- if (get_ve_flags(curwin) & VE_BLOCK) {
+ if (get_ve_flags(curwin) & kOptVeFlagBlock) {
pos_T pos;
int cursor_above = curwin->w_cursor.lnum < VIsual.lnum;
@@ -1903,7 +1936,7 @@ static void win_update(win_T *wp)
pos.lnum += cursor_above ? 1 : -1) {
colnr_T t;
- pos.col = (colnr_T)strlen(ml_get_buf(wp->w_buffer, pos.lnum));
+ pos.col = ml_get_buf_len(wp->w_buffer, pos.lnum);
getvvcol(wp, &pos, NULL, NULL, &t);
toc = MAX(toc, t);
}
@@ -1999,14 +2032,7 @@ static void win_update(win_T *wp)
}
foldinfo_T cursorline_fi = { 0 };
- wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
- if (wp->w_p_cul) {
- // Make sure that the cursorline on a closed fold is redrawn
- cursorline_fi = fold_info(wp, wp->w_cursor.lnum);
- if (cursorline_fi.fi_level != 0 && cursorline_fi.fi_lines > 0) {
- wp->w_cursorline = cursorline_fi.fi_lnum;
- }
- }
+ win_update_cursorline(wp, &cursorline_fi);
win_check_ns_hl(wp);
@@ -2068,7 +2094,7 @@ static void win_update(win_T *wp)
// match in fixed position might need redraw
// if lines were inserted or deleted
|| (wp->w_match_head != NULL
- && buf->b_mod_xlines != 0)))))
+ && buf->b_mod_set && buf->b_mod_xlines != 0)))))
|| lnum == wp->w_cursorline
|| lnum == wp->w_last_cursorline) {
if (lnum == mod_top) {
@@ -2227,7 +2253,7 @@ static void win_update(win_T *wp)
&& wp->w_lines[idx].wl_valid
&& wp->w_lines[idx].wl_lnum == lnum
&& lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && !(dy_flags & (kOptDyFlagLastline | kOptDyFlagTruncate))
&& 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,
@@ -2287,7 +2313,8 @@ static void win_update(win_T *wp)
// - 'number' is set and below inserted/deleted lines, or
// - 'relativenumber' is set and cursor moved vertically,
// the text doesn't need to be redrawn, but the number column does.
- if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot && buf->b_mod_xlines != 0)
+ if ((wp->w_p_nu && mod_top != 0 && lnum >= mod_bot
+ && buf->b_mod_set && buf->b_mod_xlines != 0)
|| (wp->w_p_rnu && wp->w_last_cursor_lnum_rnu != wp->w_cursor.lnum)) {
foldinfo_T info = wp->w_p_cul && lnum == wp->w_cursor.lnum
? cursorline_fi : fold_info(wp, lnum);
@@ -2354,7 +2381,7 @@ redr_statuscol:
// 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"
+ } else if (dy_flags & kOptDyFlagTruncate) { // 'display' has "truncate"
// Last line isn't finished: Display "@@@" in the last screen line.
grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
grid_line_fill(0, MIN(wp->w_grid.cols, 3), wp->w_p_fcs_chars.lastline, at_attr);
@@ -2362,7 +2389,7 @@ redr_statuscol:
grid_line_flush();
set_empty_rows(wp, srow);
wp->w_botline = lnum;
- } else if (dy_flags & DY_LASTLINE) { // 'display' has "lastline"
+ } else if (dy_flags & kOptDyFlagLastline) { // 'display' has "lastline"
// Last line isn't finished: Display "@@@" at the end.
// If this would split a doublewidth char in two, we need to display "@@@@" instead
grid_line_start(&wp->w_grid, wp->w_grid.rows - 1);
@@ -2546,9 +2573,9 @@ 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;
- int wwidth = wp->w_grid.cols;
+ int n = wp->w_grid.cols - (col + wmw);
- return MIN(fdc, wwidth - (col + wmw));
+ return MIN(fdc, n);
}
/// Return the width of the 'number' and 'relativenumber' column.
@@ -2752,6 +2779,10 @@ void redraw_statuslines(void)
if (redraw_tabline) {
draw_tabline();
}
+
+ if (need_maketitle) {
+ maketitle();
+ }
}
/// Redraw all status lines at the bottom of frame "frp".
@@ -2827,3 +2858,18 @@ bool win_cursorline_standout(const win_T *wp)
{
return wp->w_p_cul || (wp->w_p_cole > 0 && !conceal_cursor_line(wp));
}
+
+/// Update w_cursorline, taking care to set it to the to the start of a closed fold.
+///
+/// @param[out] foldinfo foldinfo for the cursor line
+void win_update_cursorline(win_T *wp, foldinfo_T *foldinfo)
+{
+ wp->w_cursorline = win_cursorline_standout(wp) ? wp->w_cursor.lnum : 0;
+ if (wp->w_p_cul) {
+ // Make sure that the cursorline on a closed fold is redrawn
+ *foldinfo = fold_info(wp, wp->w_cursor.lnum);
+ if (foldinfo->fi_level != 0 && foldinfo->fi_lines > 0) {
+ wp->w_cursorline = foldinfo->fi_lnum;
+ }
+ }
+}
diff --git a/src/nvim/drawscreen.h b/src/nvim/drawscreen.h
index 36ba8099fd..58eca9ac07 100644
--- a/src/nvim/drawscreen.h
+++ b/src/nvim/drawscreen.h
@@ -4,6 +4,7 @@
#include "nvim/buffer_defs.h"
#include "nvim/macros_defs.h"
+#include "nvim/pos_defs.h"
/// flags for update_screen()
/// The higher the value, the higher the priority
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index f06dc124f0..9e17c93f3f 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -4,9 +4,11 @@
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
+#include <stddef.h>
#include <string.h>
#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -31,6 +33,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -46,6 +49,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -440,7 +444,7 @@ static int insert_check(VimState *state)
msg_scroll = false;
// Open fold at the cursor line, according to 'foldopen'.
- if (fdo_flags & FDO_INSERT) {
+ if (fdo_flags & kOptFdoFlagInsert) {
foldOpenCursor();
}
@@ -604,8 +608,11 @@ static int insert_execute(VimState *state, int key)
|| (ins_compl_enter_selects()
&& (s->c == CAR || s->c == K_KENTER || s->c == NL)))
&& stop_arrow() == OK) {
- ins_compl_delete();
- ins_compl_insert(false);
+ ins_compl_delete(false);
+ ins_compl_insert(false, false);
+ } else if (ascii_iswhite_nl_or_nul(s->c) && ins_compl_preinsert_effect()) {
+ // Delete preinserted text when typing special chars
+ ins_compl_delete(false);
}
}
}
@@ -751,7 +758,7 @@ static int insert_handle_key(InsertState *s)
ins_ctrl_o();
// don't move the cursor left when 'virtualedit' has "onemore".
- if (get_ve_flags(curwin) & VE_ONEMORE) {
+ if (get_ve_flags(curwin) & kOptVeFlagOnemore) {
ins_at_eol = false;
s->nomove = true;
}
@@ -2518,7 +2525,7 @@ int oneright(void)
// move "l" bytes right, but don't end up on the NUL, unless 'virtualedit'
// contains "onemore".
- if (ptr[l] == NUL && (get_ve_flags(curwin) & VE_ONEMORE) == 0) {
+ if (ptr[l] == NUL && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
return FAIL;
}
curwin->w_cursor.col += l;
@@ -2600,7 +2607,7 @@ void cursor_up_inner(win_T *wp, linenr_T n)
// If we entered a fold, move to the beginning, unless in
// Insert mode or when 'foldopen' contains "all": it will open
// in a moment.
- if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & FDO_ALL))) {
+ if (n > 0 || !((State & MODE_INSERT) || (fdo_flags & kOptFdoFlagAll))) {
hasFolding(wp, lnum, &lnum, NULL);
}
}
@@ -3223,7 +3230,7 @@ static void ins_reg(void)
check_cursor(curwin);
}
if (regname == NUL || !valid_yank_reg(regname, false)) {
- vim_beep(BO_REG);
+ vim_beep(kOptBoFlagRegister);
need_redraw = true; // remove the '"'
} else {
if (literally == Ctrl_O || literally == Ctrl_P) {
@@ -3235,7 +3242,7 @@ static void ins_reg(void)
do_put(regname, NULL, BACKWARD, 1,
(literally == Ctrl_P ? PUT_FIXINDENT : 0) | PUT_CURSEND);
} else if (insert_reg(regname, literally) == FAIL) {
- vim_beep(BO_REG);
+ vim_beep(kOptBoFlagRegister);
need_redraw = true; // remove the '"'
} else if (stop_insert_mode) {
// When the '=' register was used and a function was invoked that
@@ -3314,7 +3321,7 @@ static void ins_ctrl_g(void)
// Unknown CTRL-G command, reserved for future expansion.
default:
- vim_beep(BO_CTRLG);
+ vim_beep(kOptBoFlagCtrlg);
}
}
@@ -3412,7 +3419,7 @@ static bool ins_esc(int *count, int cmdchar, bool nomove)
&& (curwin->w_cursor.col != 0 || curwin->w_cursor.coladd > 0)
&& (restart_edit == NUL || (gchar_cursor() == NUL && !VIsual_active))
&& !revins_on) {
- if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == VE_ALL) {
+ if (curwin->w_cursor.coladd > 0 || get_ve_flags(curwin) == kOptVeFlagAll) {
oneleft();
if (restart_edit != NUL) {
curwin->w_cursor.coladd++;
@@ -3598,7 +3605,7 @@ static void ins_del(void)
const int temp = curwin->w_cursor.col;
if (!can_bs(BS_EOL) // only if "eol" included
|| do_join(2, false, true, false, false) == FAIL) {
- vim_beep(BO_BS);
+ vim_beep(kOptBoFlagBackspace);
} else {
curwin->w_cursor.col = temp;
// Adjust orig_line_count in case more lines have been deleted than
@@ -3610,7 +3617,7 @@ static void ins_del(void)
}
}
} else if (del_char(false) == FAIL) { // delete char under cursor
- vim_beep(BO_BS);
+ vim_beep(kOptBoFlagBackspace);
}
did_ai = false;
did_si = false;
@@ -3649,7 +3656,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
|| (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0
&& curwin->w_cursor.col <= ai_col)
|| (!can_bs(BS_EOL) && curwin->w_cursor.col == 0)))) {
- vim_beep(BO_BS);
+ vim_beep(kOptBoFlagBackspace);
return false;
}
@@ -3962,7 +3969,7 @@ static void ins_left(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -3985,14 +3992,14 @@ static void ins_left(void)
coladvance(curwin, MAXCOL);
curwin->w_set_curswant = true; // so we stay at the end
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
static void ins_home(int c)
{
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4008,7 +4015,7 @@ static void ins_home(int c)
static void ins_end(int c)
{
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4025,7 +4032,7 @@ static void ins_end(int c)
static void ins_s_left(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4037,7 +4044,7 @@ static void ins_s_left(void)
bck_word(1, false, false);
curwin->w_set_curswant = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
@@ -4046,7 +4053,7 @@ static void ins_s_left(void)
static void ins_right(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4075,7 +4082,7 @@ static void ins_right(void)
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
@@ -4083,7 +4090,7 @@ static void ins_right(void)
static void ins_s_right(void)
{
const bool end_change = dont_sync_undo == kFalse; // end undoable change
- if ((fdo_flags & FDO_HOR) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped) {
foldOpenCursor();
}
undisplay_dollar();
@@ -4096,7 +4103,7 @@ static void ins_s_right(void)
fwd_word(1, false, 0);
curwin->w_set_curswant = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
dont_sync_undo = kFalse;
}
@@ -4120,7 +4127,7 @@ static void ins_up(bool startcol)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4142,7 +4149,7 @@ static void ins_pageup(void)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4165,7 +4172,7 @@ static void ins_down(bool startcol)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4187,7 +4194,7 @@ static void ins_pagedown(void)
start_arrow(&tpos);
can_cindent = true;
} else {
- vim_beep(BO_CRSR);
+ vim_beep(kOptBoFlagCursor);
}
}
@@ -4535,7 +4542,7 @@ static int ins_digraph(void)
int ins_copychar(linenr_T lnum)
{
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) {
- vim_beep(BO_COPY);
+ vim_beep(kOptBoFlagCopy);
return NUL;
}
@@ -4558,7 +4565,7 @@ int ins_copychar(linenr_T lnum)
int c = ci.chr.value < 0 ? (uint8_t)(*ci.ptr) : ci.chr.value;
if (c == NUL) {
- vim_beep(BO_COPY);
+ vim_beep(kOptBoFlagCopy);
}
return c;
}
diff --git a/src/nvim/errors.h b/src/nvim/errors.h
index df94945a3d..baea005f15 100644
--- a/src/nvim/errors.h
+++ b/src/nvim/errors.h
@@ -129,6 +129,7 @@ EXTERN const char e_missingparen[] INIT(= N_("E107: Missing parentheses: %s"));
EXTERN const char e_empty_buffer[] INIT(= N_("E749: Empty buffer"));
EXTERN const char e_nobufnr[] INIT(= N_("E86: Buffer %" PRId64 " does not exist"));
+EXTERN const char e_unknown_function_str[] INIT(= N_("E117: Unknown function: %s"));
EXTERN const char e_str_not_inside_function[] INIT(= N_("E193: %s not inside a function"));
EXTERN const char e_invalpat[] INIT(= N_("E682: Invalid search pattern or delimiter"));
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index faacf3c65a..97d1a3c75d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -270,6 +270,7 @@ static struct vimvar {
VV(VV_COLLATE, "collate", VAR_STRING, VV_RO),
VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO),
VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO),
+ VV(VV_STACKTRACE, "stacktrace", VAR_LIST, VV_RO),
// Neovim
VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO),
VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO),
@@ -4112,10 +4113,10 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua
{
const bool working = (**arg == '+'); // has("+option")
OptIndex opt_idx;
- int scope;
+ int opt_flags;
// Isolate the option name and find its value.
- char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &scope);
+ char *const option_end = (char *)find_option_var_end(arg, &opt_idx, &opt_flags);
if (option_end == NULL) {
if (rettv != NULL) {
@@ -4143,7 +4144,7 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua
ret = FAIL;
} else if (rettv != NULL) {
- OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, scope);
+ OptVal value = is_tty_opt ? get_tty_option(*arg) : get_option_value(opt_idx, opt_flags);
assert(value.type != kOptValTypeNil);
*rettv = optval_as_tv(value, true);
@@ -7965,8 +7966,7 @@ void ex_execute(exarg_T *eap)
} else if (eap->cmdidx == CMD_echoerr) {
// We don't want to abort following commands, restore did_emsg.
int save_did_emsg = did_emsg;
- msg_ext_set_kind("echoerr");
- emsg_multiline(ga.ga_data, true);
+ emsg_multiline(ga.ga_data, "echoerr", HLF_E, true);
if (!force_abort) {
did_emsg = save_did_emsg;
}
@@ -7986,24 +7986,25 @@ void ex_execute(exarg_T *eap)
/// Skip over the name of an option variable: "&option", "&g:option" or "&l:option".
///
-/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning.
-/// @param[out] opt_idxp Set to option index in options[] table.
-/// @param[out] scope Set to option scope.
+/// @param[in,out] arg Points to the "&" or '+' when called, to "option" when returning.
+/// @param[out] opt_idxp Set to option index in options[] table.
+/// @param[out] opt_flags Option flags.
///
/// @return NULL when no option name found. Otherwise pointer to the char after the option name.
-const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp, int *const scope)
+const char *find_option_var_end(const char **const arg, OptIndex *const opt_idxp,
+ int *const opt_flags)
{
const char *p = *arg;
p++;
if (*p == 'g' && p[1] == ':') {
- *scope = OPT_GLOBAL;
+ *opt_flags = OPT_GLOBAL;
p += 2;
} else if (*p == 'l' && p[1] == ':') {
- *scope = OPT_LOCAL;
+ *opt_flags = OPT_LOCAL;
p += 2;
} else {
- *scope = 0;
+ *opt_flags = 0;
}
const char *end = find_option_end(p, opt_idxp);
@@ -8490,7 +8491,7 @@ char *do_string_sub(char *str, size_t len, char *pat, char *sub, typval_T *expr,
return ret;
}
-/// common code for getting job callbacks for jobstart, termopen and rpcstart
+/// Common code for getting job callbacks for `jobstart`.
///
/// @return true/false on success/failure.
bool common_job_callbacks(dict_T *vopts, CallbackReader *on_stdout, CallbackReader *on_stderr,
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index bb9b00abc7..8b4aa8101a 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -167,6 +167,7 @@ typedef enum {
VV_COLLATE,
VV_EXITING,
VV_MAXCOL,
+ VV_STACKTRACE,
// Nvim
VV_STDERR,
VV_MSGPACK_TYPES,
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index a418b34909..9d787c68ea 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -17,6 +17,7 @@
--- @field deprecated? true
--- @field returns? string|false
--- @field returns_desc? string
+--- @field generics? string[] Used to write `---@generic` annotations over a function.
--- @field signature? string
--- @field desc? string
--- @field params [string, string, string][]
@@ -1383,16 +1384,22 @@ M.funcs = {
See |complete_info_mode| for the values.
pum_visible |TRUE| if popup menu is visible.
See |pumvisible()|.
- items List of completion matches. Each item is a
- dictionary containing the entries "word",
+ items List of all completion candidates. Each item
+ is a dictionary containing the entries "word",
"abbr", "menu", "kind", "info" and "user_data".
See |complete-items|.
+ matches Same as "items", but only returns items that
+ are matching current query. If both "matches"
+ and "items" are in "what", the returned list
+ will still be named "items", but each item
+ will have an additional "match" field.
selected Selected item index. First index is zero.
Index is -1 if no item is selected (showing
typed text only, or the last completion after
no item is selected when using the <Up> or
<Down> keys)
- inserted Inserted string. [NOT IMPLEMENTED YET]
+ completed Return a dictionary containing the entries of
+ the currently selected index item.
preview_winid Info floating preview window id.
preview_bufnr Info floating preview buffer id.
@@ -1521,9 +1528,10 @@ M.funcs = {
A |Dictionary| is copied in a similar way as a |List|.
Also see |deepcopy()|.
]=],
+ generics = { 'T' },
name = 'copy',
- params = { { 'expr', 'any' } },
- returns = 'any',
+ params = { { 'expr', 'T' } },
+ returns = 'T',
signature = 'copy({expr})',
},
cos = {
@@ -1639,6 +1647,7 @@ M.funcs = {
]=],
name = 'ctxset',
params = { { 'context', 'table' }, { 'index', 'integer' } },
+ returns = 'integer',
signature = 'ctxset({context} [, {index}])',
},
ctxsize = {
@@ -1738,8 +1747,10 @@ M.funcs = {
Also see |copy()|.
]=],
+ generics = { 'T' },
name = 'deepcopy',
- params = { { 'expr', 'any' }, { 'noref', 'boolean' } },
+ params = { { 'expr', 'T' }, { 'noref', 'boolean' } },
+ returns = 'T',
signature = 'deepcopy({expr} [, {noref}])',
},
delete = {
@@ -1869,6 +1880,7 @@ M.funcs = {
fast = true,
name = 'did_filetype',
params = {},
+ returns = 'integer',
signature = 'did_filetype()',
},
diff_filler = {
@@ -1886,6 +1898,7 @@ M.funcs = {
]=],
name = 'diff_filler',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'diff_filler({lnum})',
},
diff_hlID = {
@@ -1930,6 +1943,7 @@ M.funcs = {
]=],
name = 'digraph_get',
params = { { 'chars', 'string' } },
+ returns = 'string',
signature = 'digraph_get({chars})',
},
digraph_getlist = {
@@ -1952,6 +1966,7 @@ M.funcs = {
]=],
name = 'digraph_getlist',
params = { { 'listall', 'boolean' } },
+ returns = 'string[][]',
signature = 'digraph_getlist([{listall}])',
},
digraph_set = {
@@ -2016,6 +2031,7 @@ M.funcs = {
]=],
name = 'empty',
params = { { 'expr', 'any' } },
+ returns = 'integer',
signature = 'empty({expr})',
},
environ = {
@@ -2048,6 +2064,7 @@ M.funcs = {
fast = true,
name = 'escape',
params = { { 'string', 'string' }, { 'chars', 'string' } },
+ returns = 'string',
signature = 'escape({string}, {chars})',
},
eval = {
@@ -3018,6 +3035,7 @@ M.funcs = {
]=],
name = 'foreach',
params = { { 'expr1', 'string|table' }, { 'expr2', 'string|function' } },
+ returns = 'string|table',
signature = 'foreach({expr1}, {expr2})',
},
foreground = {
@@ -3372,6 +3390,7 @@ M.funcs = {
]=],
name = 'getbufline',
params = { { 'buf', 'integer|string' }, { 'lnum', 'integer' }, { 'end', 'integer' } },
+ returns = 'string[]',
signature = 'getbufline({buf}, {lnum} [, {end}])',
},
getbufoneline = {
@@ -3452,15 +3471,17 @@ M.funcs = {
signature = 'getchangelist([{buf}])',
},
getchar = {
- args = { 0, 1 },
+ args = { 0, 2 },
desc = [=[
Get a single character from the user or input stream.
- If {expr} is omitted, wait until a character is available.
+ If {expr} is omitted or is -1, wait until a character is
+ available.
If {expr} is 0, only get a character when one is available.
Return zero otherwise.
If {expr} is 1, only check if a character is available, it is
not consumed. Return zero if no character available.
- If you prefer always getting a string use |getcharstr()|.
+ If you prefer always getting a string use |getcharstr()|, or
+ specify |FALSE| as "number" in {opts}.
Without {expr} and when {expr} is 0 a whole character or
special key is returned. If it is a single character, the
@@ -3470,7 +3491,8 @@ M.funcs = {
starting with 0x80 (decimal: 128). This is the same value as
the String "\<Key>", e.g., "\<Left>". The returned value is
also a String when a modifier (shift, control, alt) was used
- that is not included in the character.
+ that is not included in the character. |keytrans()| can also
+ be used to convert a returned String into a readable form.
When {expr} is 0 and Esc is typed, there will be a short delay
while Vim waits to see if this is the start of an escape
@@ -3482,6 +3504,32 @@ M.funcs = {
Use getcharmod() to obtain any additional modifiers.
+ The optional argument {opts} is a Dict and supports the
+ following items:
+
+ cursor A String specifying cursor behavior
+ when waiting for a character.
+ "hide": hide the cursor.
+ "keep": keep current cursor unchanged.
+ "msg": move cursor to message area.
+ (default: automagically decide
+ between "keep" and "msg")
+
+ number If |TRUE|, return a Number when getting
+ a single character.
+ If |FALSE|, the return value is always
+ converted to a String, and an empty
+ String (instead of 0) is returned when
+ no character is available.
+ (default: |TRUE|)
+
+ simplify If |TRUE|, include modifiers in the
+ character if possible. E.g., return
+ the same value for CTRL-I and <Tab>.
+ If |FALSE|, don't include modifiers in
+ the character.
+ (default: |TRUE|)
+
When the user clicks a mouse button, the mouse event will be
returned. The position can then be found in |v:mouse_col|,
|v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|.
@@ -3519,9 +3567,9 @@ M.funcs = {
<
]=],
name = 'getchar',
- params = { { 'expr', '0|1' } },
- returns = 'integer',
- signature = 'getchar([{expr}])',
+ params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
+ returns = 'integer|string',
+ signature = 'getchar([{expr} [, {opts}]])',
},
getcharmod = {
desc = [=[
@@ -3594,21 +3642,13 @@ M.funcs = {
signature = 'getcharsearch()',
},
getcharstr = {
- args = { 0, 1 },
+ args = { 0, 2 },
desc = [=[
- Get a single character from the user or input stream as a
- string.
- If {expr} is omitted, wait until a character is available.
- If {expr} is 0 or false, only get a character when one is
- available. Return an empty string otherwise.
- If {expr} is 1 or true, only check if a character is
- available, it is not consumed. Return an empty string
- if no character is available.
- Otherwise this works like |getchar()|, except that a number
- result is converted to a string.
+ The same as |getchar()|, except that this always returns a
+ String, and "number" isn't allowed in {opts}.
]=],
name = 'getcharstr',
- params = { { 'expr', '0|1' } },
+ params = { { 'expr', '-1|0|1' }, { 'opts', 'table' } },
returns = 'string',
signature = 'getcharstr([{expr}])',
},
@@ -3700,6 +3740,7 @@ M.funcs = {
]=],
name = 'getcmdscreenpos',
params = {},
+ returns = 'integer',
signature = 'getcmdscreenpos()',
},
getcmdtype = {
@@ -4651,6 +4692,25 @@ M.funcs = {
returns = 'vim.fn.getscriptinfo.ret[]',
signature = 'getscriptinfo([{opts}])',
},
+ getstacktrace = {
+ args = 0,
+ desc = [=[
+ Returns the current stack trace of Vim scripts.
+ Stack trace is a |List|, of which each item is a |Dictionary|
+ with the following items:
+ funcref The funcref if the stack is at a function,
+ otherwise this item is omitted.
+ event The string of the event description if the
+ stack is at an autocmd event, otherwise this
+ item is omitted.
+ lnum The line number in the script on the stack.
+ filepath The file path of the script on the stack.
+ ]=],
+ name = 'getstacktrace',
+ params = {},
+ returns = 'table[]',
+ signature = 'getstacktrace()',
+ },
gettabinfo = {
args = { 0, 1 },
base = 1,
@@ -4781,6 +4841,7 @@ M.funcs = {
]=],
name = 'gettext',
params = { { 'text', 'string' } },
+ returns = 'string',
signature = 'gettext({text})',
},
getwininfo = {
@@ -4800,6 +4861,8 @@ M.funcs = {
botline last complete displayed buffer line
bufnr number of buffer in the window
height window height (excluding winbar)
+ leftcol first column displayed; only used when
+ 'wrap' is off
loclist 1 if showing a location list
quickfix 1 if quickfix or location list window
terminal 1 if a terminal window
@@ -4963,6 +5026,7 @@ M.funcs = {
]=],
name = 'glob2regpat',
params = { { 'string', 'string' } },
+ returns = 'string',
signature = 'glob2regpat({string})',
},
globpath = {
@@ -5370,6 +5434,7 @@ M.funcs = {
fast = true,
name = 'iconv',
params = { { 'string', 'string' }, { 'from', 'string' }, { 'to', 'string' } },
+ returns = 'string',
signature = 'iconv({string}, {from}, {to})',
},
id = {
@@ -5393,6 +5458,7 @@ M.funcs = {
]=],
name = 'id',
params = { { 'expr', 'any' } },
+ returns = 'string',
signature = 'id({expr})',
},
indent = {
@@ -5445,6 +5511,7 @@ M.funcs = {
]=],
name = 'index',
params = { { 'object', 'any' }, { 'expr', 'any' }, { 'start', 'integer' }, { 'ic', 'boolean' } },
+ returns = 'integer',
signature = 'index({object}, {expr} [, {start} [, {ic}]])',
},
indexof = {
@@ -5492,6 +5559,7 @@ M.funcs = {
]=],
name = 'indexof',
params = { { 'object', 'any' }, { 'expr', 'any' }, { 'opts', 'table' } },
+ returns = 'integer',
signature = 'indexof({object}, {expr} [, {opts}])',
},
input = {
@@ -5500,6 +5568,7 @@ M.funcs = {
desc = '',
name = 'input',
params = { { 'prompt', 'string' }, { 'text', 'string' }, { 'completion', 'string' } },
+ returns = 'string',
signature = 'input({prompt} [, {text} [, {completion}]])',
},
input__1 = {
@@ -5619,6 +5688,7 @@ M.funcs = {
]=],
name = 'input',
params = { { 'opts', 'table' } },
+ returns = 'string',
signature = 'input({opts})',
},
inputdialog = {
@@ -5665,6 +5735,7 @@ M.funcs = {
]=],
name = 'inputrestore',
params = {},
+ returns = 'integer',
signature = 'inputrestore()',
},
inputsave = {
@@ -5678,6 +5749,7 @@ M.funcs = {
]=],
name = 'inputsave',
params = {},
+ returns = 'integer',
signature = 'inputsave()',
},
inputsecret = {
@@ -5697,6 +5769,7 @@ M.funcs = {
]=],
name = 'inputsecret',
params = { { 'prompt', 'string' }, { 'text', 'string' } },
+ returns = 'string',
signature = 'inputsecret({prompt} [, {text}])',
},
insert = {
@@ -5754,7 +5827,8 @@ M.funcs = {
<
]=],
name = 'invert',
- params = { { 'expr', 'number' } },
+ params = { { 'expr', 'integer' } },
+ returns = 'integer',
signature = 'invert({expr})',
},
isabsolutepath = {
@@ -5870,7 +5944,7 @@ M.funcs = {
the index.
]=],
name = 'items',
- params = { { 'dict', 'any' } },
+ params = { { 'dict', 'table' } },
signature = 'items({dict})',
},
jobclose = {
@@ -5917,7 +5991,7 @@ M.funcs = {
jobstart = {
args = { 1, 2 },
desc = [=[
- Note: Prefer |vim.system()| in Lua (unless using the `pty` option).
+ Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`).
Spawns {cmd} as a job.
If {cmd} is a List it runs directly (no 'shell').
@@ -5925,8 +5999,11 @@ M.funcs = {
call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
<(See |shell-unquoting| for details.)
- Example: >vim
- call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}})
+ Example: start a job and handle its output: >vim
+ call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}})
+ <
+ Example: start a job in a |terminal| connected to the current buffer: >vim
+ call jobstart(['nvim', '-h'], {'term':v:true})
<
Returns |job-id| on success, 0 on invalid arguments (or job
table is full), -1 if {cmd}[0] or 'shell' is not executable.
@@ -5991,6 +6068,10 @@ M.funcs = {
stdin: (string) Either "pipe" (default) to connect the
job's stdin to a channel or "null" to disconnect
stdin.
+ term: (boolean) Spawns {cmd} in a new pseudo-terminal session
+ connected to the current (unmodified) buffer. Implies "pty".
+ Default "height" and "width" are set to the current window
+ dimensions. |jobstart()|. Defaults $TERM to "xterm-256color".
width: (number) Width of the `pty` terminal.
{opts} is passed as |self| dictionary to the callback; the
@@ -6004,6 +6085,7 @@ M.funcs = {
]=],
name = 'jobstart',
params = { { 'cmd', 'string|string[]' }, { 'opts', 'table' } },
+ returns = 'integer',
signature = 'jobstart({cmd} [, {opts}])',
},
jobstop = {
@@ -6020,6 +6102,7 @@ M.funcs = {
]=],
name = 'jobstop',
params = { { 'id', 'integer' } },
+ returns = 'integer',
signature = 'jobstop({id})',
},
jobwait = {
@@ -6047,6 +6130,7 @@ M.funcs = {
]=],
name = 'jobwait',
params = { { 'jobs', 'integer[]' }, { 'timeout', 'integer' } },
+ returns = 'integer[]',
signature = 'jobwait({jobs} [, {timeout}])',
},
join = {
@@ -6066,6 +6150,7 @@ M.funcs = {
]=],
name = 'join',
params = { { 'list', 'any[]' }, { 'sep', 'string' } },
+ returns = 'string',
signature = 'join({list} [, {sep}])',
},
json_decode = {
@@ -6109,6 +6194,7 @@ M.funcs = {
]=],
name = 'json_encode',
params = { { 'expr', 'any' } },
+ returns = 'string',
signature = 'json_encode({expr})',
},
keys = {
@@ -6121,6 +6207,7 @@ M.funcs = {
]=],
name = 'keys',
params = { { 'dict', 'table' } },
+ returns = 'string[]',
signature = 'keys({dict})',
},
keytrans = {
@@ -6136,6 +6223,7 @@ M.funcs = {
]=],
name = 'keytrans',
params = { { 'string', 'string' } },
+ returns = 'string',
signature = 'keytrans({string})',
},
last_buffer_nr = {
@@ -6162,7 +6250,8 @@ M.funcs = {
]=],
name = 'len',
- params = { { 'expr', 'any' } },
+ params = { { 'expr', 'any[]' } },
+ returns = 'integer',
signature = 'len({expr})',
tags = { 'E701' },
},
@@ -6297,6 +6386,7 @@ M.funcs = {
]=],
name = 'lispindent',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'lispindent({lnum})',
},
list2blob = {
@@ -6315,6 +6405,7 @@ M.funcs = {
]=],
name = 'list2blob',
params = { { 'list', 'any[]' } },
+ returns = 'string',
signature = 'list2blob({list})',
},
list2str = {
@@ -6339,6 +6430,7 @@ M.funcs = {
]=],
name = 'list2str',
params = { { 'list', 'any[]' }, { 'utf8', 'boolean' } },
+ returns = 'string',
signature = 'list2str({list} [, {utf8}])',
},
localtime = {
@@ -6348,6 +6440,7 @@ M.funcs = {
]=],
name = 'localtime',
params = {},
+ returns = 'integer',
signature = 'localtime()',
},
log = {
@@ -6368,6 +6461,7 @@ M.funcs = {
float_func = 'log',
name = 'log',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'log({expr})',
},
log10 = {
@@ -6387,6 +6481,7 @@ M.funcs = {
float_func = 'log10',
name = 'log10',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'log10({expr})',
},
luaeval = {
@@ -7271,6 +7366,7 @@ M.funcs = {
]=],
name = 'max',
params = { { 'expr', 'any' } },
+ returns = 'number',
signature = 'max({expr})',
},
menu_get = {
@@ -7419,6 +7515,7 @@ M.funcs = {
]=],
name = 'min',
params = { { 'expr', 'any' } },
+ returns = 'number',
signature = 'min({expr})',
},
mkdir = {
@@ -7467,6 +7564,7 @@ M.funcs = {
]=],
name = 'mkdir',
params = { { 'name', 'string' }, { 'flags', 'string' }, { 'prot', 'string' } },
+ returns = 'integer',
signature = 'mkdir({name} [, {flags} [, {prot}]])',
tags = { 'E739' },
},
@@ -7646,6 +7744,7 @@ M.funcs = {
]=],
name = 'nextnonblank',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'nextnonblank({lnum})',
},
nr2char = {
@@ -7669,6 +7768,7 @@ M.funcs = {
]=],
name = 'nr2char',
params = { { 'expr', 'integer' }, { 'utf8', 'boolean' } },
+ returns = 'string',
signature = 'nr2char({expr} [, {utf8}])',
},
nvim_api__ = {
@@ -7730,6 +7830,7 @@ M.funcs = {
]=],
name = 'pathshorten',
params = { { 'path', 'string' }, { 'len', 'integer' } },
+ returns = 'string',
signature = 'pathshorten({path} [, {len}])',
},
perleval = {
@@ -7773,6 +7874,7 @@ M.funcs = {
]=],
name = 'pow',
params = { { 'x', 'number' }, { 'y', 'number' } },
+ returns = 'number',
signature = 'pow({x}, {y})',
},
prevnonblank = {
@@ -7790,6 +7892,7 @@ M.funcs = {
]=],
name = 'prevnonblank',
params = { { 'lnum', 'integer' } },
+ returns = 'integer',
signature = 'prevnonblank({lnum})',
},
printf = {
@@ -8475,7 +8578,13 @@ M.funcs = {
<
]=],
name = 'reduce',
- params = { { 'object', 'any' }, { 'func', 'function' }, { 'initial', 'any' } },
+ generics = { 'T' },
+ params = {
+ { 'object', 'any' },
+ { 'func', 'fun(accumulator: T, current: any): any' },
+ { 'initial', 'any' },
+ },
+ returns = 'T',
signature = 'reduce({object}, {func} [, {initial}])',
},
reg_executing = {
@@ -8679,6 +8788,7 @@ M.funcs = {
]=],
name = 'rename',
params = { { 'from', 'string' }, { 'to', 'string' } },
+ returns = 'integer',
signature = 'rename({from}, {to})',
},
['repeat'] = {
@@ -8721,6 +8831,7 @@ M.funcs = {
fast = true,
name = 'resolve',
params = { { 'filename', 'string' } },
+ returns = 'string',
signature = 'resolve({filename})',
},
reverse = {
@@ -8738,7 +8849,9 @@ M.funcs = {
<
]=],
name = 'reverse',
- params = { { 'object', 'any' } },
+ generics = { 'T' },
+ params = { { 'object', 'T[]' } },
+ returns = 'T[]',
signature = 'reverse({object})',
},
round = {
@@ -8762,6 +8875,7 @@ M.funcs = {
float_func = 'round',
name = 'round',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'round({expr})',
},
rpcnotify = {
@@ -8774,7 +8888,8 @@ M.funcs = {
<
]=],
name = 'rpcnotify',
- params = { { 'channel', 'integer' }, { 'event', 'string' }, { 'args', 'any' } },
+ params = { { 'channel', 'integer' }, { 'event', 'string' }, { '...', 'any' } },
+ returns = 'integer',
signature = 'rpcnotify({channel}, {event} [, {args}...])',
},
rpcrequest = {
@@ -8787,7 +8902,7 @@ M.funcs = {
<
]=],
name = 'rpcrequest',
- params = { { 'channel', 'integer' }, { 'method', 'string' }, { 'args', 'any' } },
+ params = { { 'channel', 'integer' }, { 'method', 'string' }, { '...', 'any' } },
signature = 'rpcrequest({channel}, {method} [, {args}...])',
},
rpcstart = {
@@ -8846,6 +8961,7 @@ M.funcs = {
]=],
name = 'screenattr',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer',
signature = 'screenattr({row}, {col})',
},
screenchar = {
@@ -8864,6 +8980,7 @@ M.funcs = {
]=],
name = 'screenchar',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer',
signature = 'screenchar({row}, {col})',
},
screenchars = {
@@ -8879,6 +8996,7 @@ M.funcs = {
]=],
name = 'screenchars',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer[]',
signature = 'screenchars({row}, {col})',
},
screencol = {
@@ -8899,6 +9017,7 @@ M.funcs = {
]=],
name = 'screencol',
params = {},
+ returns = 'integer[]',
signature = 'screencol()',
},
screenpos = {
@@ -8944,6 +9063,7 @@ M.funcs = {
]=],
name = 'screenrow',
params = {},
+ returns = 'integer',
signature = 'screenrow()',
},
screenstring = {
@@ -8960,6 +9080,7 @@ M.funcs = {
]=],
name = 'screenstring',
params = { { 'row', 'integer' }, { 'col', 'integer' } },
+ returns = 'string',
signature = 'screenstring({row}, {col})',
},
search = {
@@ -9079,6 +9200,7 @@ M.funcs = {
{ 'timeout', 'integer' },
{ 'skip', 'string|function' },
},
+ returns = 'integer',
signature = 'search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]])',
},
searchcount = {
@@ -9401,6 +9523,7 @@ M.funcs = {
]=],
name = 'serverlist',
params = {},
+ returns = 'string[]',
signature = 'serverlist()',
},
serverstart = {
@@ -9441,6 +9564,7 @@ M.funcs = {
]=],
name = 'serverstart',
params = { { 'address', 'string' } },
+ returns = 'string',
signature = 'serverstart([{address}])',
},
serverstop = {
@@ -9453,6 +9577,7 @@ M.funcs = {
]=],
name = 'serverstop',
params = { { 'address', 'string' } },
+ returns = 'integer',
signature = 'serverstop({address})',
},
setbufline = {
@@ -9486,6 +9611,7 @@ M.funcs = {
]=],
name = 'setbufline',
params = { { 'buf', 'integer|string' }, { 'lnum', 'integer' }, { 'text', 'string|string[]' } },
+ returns = 'integer',
signature = 'setbufline({buf}, {lnum}, {text})',
},
setbufvar = {
@@ -9611,6 +9737,7 @@ M.funcs = {
]=],
name = 'setcmdline',
params = { { 'str', 'string' }, { 'pos', 'integer' } },
+ returns = 'integer',
signature = 'setcmdline({str} [, {pos}])',
},
setcmdpos = {
@@ -9961,6 +10088,7 @@ M.funcs = {
{ 'action', 'string' },
{ 'what', 'vim.fn.setqflist.what' },
},
+ returns = 'integer',
signature = 'setqflist({list} [, {action} [, {what}]])',
},
setreg = {
@@ -10137,6 +10265,7 @@ M.funcs = {
]=],
name = 'sha256',
params = { { 'string', 'string' } },
+ returns = 'string',
signature = 'sha256({string})',
},
shellescape = {
@@ -10177,6 +10306,7 @@ M.funcs = {
]=],
name = 'shellescape',
params = { { 'string', 'string' }, { 'special', 'boolean' } },
+ returns = 'string',
signature = 'shellescape({string} [, {special}])',
},
shiftwidth = {
@@ -10691,6 +10821,7 @@ M.funcs = {
]=],
name = 'simplify',
params = { { 'filename', 'string' } },
+ returns = 'string',
signature = 'simplify({filename})',
},
sin = {
@@ -10710,6 +10841,7 @@ M.funcs = {
float_func = 'sin',
name = 'sin',
params = { { 'expr', 'number' } },
+ returns = 'number',
signature = 'sin({expr})',
},
sinh = {
@@ -10859,7 +10991,9 @@ M.funcs = {
<
]=],
name = 'sort',
- params = { { 'list', 'any' }, { 'how', 'string|function' }, { 'dict', 'any' } },
+ generics = { 'T' },
+ params = { { 'list', 'T[]' }, { 'how', 'string|function' }, { 'dict', 'any' } },
+ returns = 'T[]',
signature = 'sort({list} [, {how} [, {dict}]])',
},
soundfold = {
@@ -10876,6 +11010,7 @@ M.funcs = {
]=],
name = 'soundfold',
params = { { 'word', 'string' } },
+ returns = 'string',
signature = 'soundfold({word})',
},
spellbadword = {
@@ -10937,6 +11072,7 @@ M.funcs = {
]=],
name = 'spellsuggest',
params = { { 'word', 'string' }, { 'max', 'integer' }, { 'capital', 'boolean' } },
+ returns = 'string[]',
signature = 'spellsuggest({word} [, {max} [, {capital}]])',
},
split = {
@@ -10970,6 +11106,7 @@ M.funcs = {
]=],
name = 'split',
params = { { 'string', 'string' }, { 'pattern', 'string' }, { 'keepempty', 'boolean' } },
+ returns = 'string[]',
signature = 'split({string} [, {pattern} [, {keepempty}]])',
},
sqrt = {
@@ -11153,7 +11290,7 @@ M.funcs = {
and exists only for backwards-compatibility.
With UTF-8 composing characters are handled properly: >vim
echo str2list("aÌ") " returns [97, 769]
-
+ <
]=],
name = 'str2list',
params = { { 'string', 'string' }, { 'utf8', 'boolean' } },
@@ -12188,24 +12325,14 @@ M.funcs = {
signature = 'tempname()',
},
termopen = {
+ deprecated = true,
args = { 1, 2 },
desc = [=[
- Spawns {cmd} in a new pseudo-terminal session connected
- to the current (unmodified) buffer. Parameters and behavior
- are the same as |jobstart()| except "pty", "width", "height",
- and "TERM" are ignored: "height" and "width" are taken from
- the current window. Note that termopen() implies a "pty" arg
- to jobstart(), and thus has the implications documented at
- |jobstart()|.
-
- Returns the same values as jobstart().
-
- Terminal environment is initialized as in |jobstart-env|,
- except $TERM is set to "xterm-256color". Full behavior is
- described in |terminal|.
+ Use |jobstart()| with `{term: v:true}` instead.
]=],
name = 'termopen',
params = { { 'cmd', 'string|string[]' }, { 'opts', 'table' } },
+ returns = 'integer',
signature = 'termopen({cmd} [, {opts}])',
},
test_garbagecollect_now = {
@@ -12715,6 +12842,7 @@ M.funcs = {
]=],
name = 'virtcol2col',
params = { { 'winid', 'integer' }, { 'lnum', 'integer' }, { 'col', 'integer' } },
+ returns = 'integer',
signature = 'virtcol2col({winid}, {lnum}, {col})',
},
visualmode = {
@@ -12739,6 +12867,7 @@ M.funcs = {
]=],
name = 'visualmode',
params = { { 'expr', 'boolean' } },
+ returns = 'string',
signature = 'visualmode([{expr}])',
},
wait = {
@@ -12896,6 +13025,7 @@ M.funcs = {
]=],
name = 'win_id2win',
params = { { 'expr', 'integer' } },
+ returns = 'integer',
signature = 'win_id2win({expr})',
},
win_move_separator = {
@@ -13089,6 +13219,7 @@ M.funcs = {
]=],
name = 'winlayout',
params = { { 'tabnr', 'integer' } },
+ returns = 'any[]',
signature = 'winlayout([{tabnr}])',
},
winline = {
@@ -13141,6 +13272,7 @@ M.funcs = {
]=],
name = 'winnr',
params = { { 'arg', 'string|integer' } },
+ returns = 'integer',
signature = 'winnr([{arg}])',
},
winrestcmd = {
@@ -13157,6 +13289,7 @@ M.funcs = {
]=],
name = 'winrestcmd',
params = {},
+ returns = 'string',
signature = 'winrestcmd()',
},
winrestview = {
@@ -13236,6 +13369,7 @@ M.funcs = {
]=],
name = 'winwidth',
params = { { 'nr', 'integer' } },
+ returns = 'integer',
signature = 'winwidth({nr})',
},
wordcount = {
@@ -13331,7 +13465,8 @@ M.funcs = {
<
]=],
name = 'xor',
- params = { { 'expr', 'number' }, { 'expr', 'number' } },
+ params = { { 'expr', 'integer' }, { 'expr', 'integer' } },
+ returns = 'integer',
signature = 'xor({expr}, {expr})',
},
}
diff --git a/src/nvim/eval/buffer.c b/src/nvim/eval/buffer.c
index 73bfd6db2a..41ed17598b 100644
--- a/src/nvim/eval/buffer.c
+++ b/src/nvim/eval/buffer.c
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -66,12 +67,11 @@ buf_T *find_buffer(typval_T *avar)
/// If there is a window for "curbuf", make it the current window.
static void find_win_for_curbuf(void)
{
- wininfo_T *wip;
-
// The b_wininfo list should have the windows that recently contained the
// buffer, going over this is faster than going over all the windows.
// Do check the buffer is still there.
- FOR_ALL_BUF_WININFO(curbuf, wip) {
+ for (size_t i = 0; i < kv_size(curbuf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(curbuf->b_wininfo, i);
if (wip->wi_win != NULL && wip->wi_win->w_buffer == curbuf) {
curwin = wip->wi_win;
break;
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index afc2efddf6..cfcd415219 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -6,6 +6,8 @@
#include <string.h>
#include "klib/kvec.h"
+#include "mpack/conv.h"
+#include "mpack/mpack_core.h"
#include "mpack/object.h"
#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
@@ -21,7 +23,6 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
/// Helper structure for container_struct
diff --git a/src/nvim/eval/decode.h b/src/nvim/eval/decode.h
index 485cc65561..af5fd3979c 100644
--- a/src/nvim/eval/decode.h
+++ b/src/nvim/eval/decode.h
@@ -2,7 +2,7 @@
#include <stddef.h> // IWYU pragma: keep
-#include "mpack/object.h"
+#include "mpack/object.h" // IWYU pragma: keep
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/eval/deprecated.c b/src/nvim/eval/deprecated.c
new file mode 100644
index 0000000000..0fc16b605d
--- /dev/null
+++ b/src/nvim/eval/deprecated.c
@@ -0,0 +1,158 @@
+#include <stdbool.h> // for true
+
+#include "nvim/channel.h"
+#include "nvim/errors.h"
+#include "nvim/eval.h"
+#include "nvim/eval/deprecated.h"
+#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
+#include "nvim/ex_cmds.h"
+#include "nvim/gettext_defs.h" // for _
+#include "nvim/globals.h"
+#include "nvim/macros_defs.h" // for S_LEN
+#include "nvim/message.h" // for semsg
+#include "nvim/types_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/deprecated.c.generated.h" // IWYU pragma: keep
+#endif
+
+/// "rpcstart()" function (DEPRECATED)
+void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_STRING
+ || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) {
+ // Wrong argument types
+ emsg(_(e_invarg));
+ return;
+ }
+
+ list_T *args = NULL;
+ int argsl = 0;
+ if (argvars[1].v_type == VAR_LIST) {
+ args = argvars[1].vval.v_list;
+ argsl = tv_list_len(args);
+ // Assert that all list items are strings
+ int i = 0;
+ TV_LIST_ITER_CONST(args, arg, {
+ if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) {
+ semsg(_("E5010: List item %d of the second argument is not a string"),
+ i);
+ return;
+ }
+ i++;
+ });
+ }
+
+ if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) {
+ emsg(_(e_api_spawn_failed));
+ return;
+ }
+
+ // Allocate extra memory for the argument vector and the NULL pointer
+ int argvl = argsl + 2;
+ char **argv = xmalloc(sizeof(char *) * (size_t)argvl);
+
+ // Copy program name
+ argv[0] = xstrdup(argvars[0].vval.v_string);
+
+ int i = 1;
+ // Copy arguments to the vector
+ if (argsl > 0) {
+ TV_LIST_ITER_CONST(args, arg, {
+ argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg)));
+ });
+ }
+
+ // The last item of argv must be NULL
+ argv[i] = NULL;
+
+ Channel *chan = channel_job_start(argv, NULL, CALLBACK_READER_INIT,
+ CALLBACK_READER_INIT, CALLBACK_NONE,
+ false, true, false, false,
+ kChannelStdinPipe, NULL, 0, 0, NULL,
+ &rettv->vval.v_number);
+ if (chan) {
+ channel_create_event(chan, NULL);
+ }
+}
+
+/// "rpcstop()" function
+void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ rettv->v_type = VAR_NUMBER;
+ rettv->vval.v_number = 0;
+
+ if (check_secure()) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_NUMBER) {
+ // Wrong argument types
+ emsg(_(e_invarg));
+ return;
+ }
+
+ // 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, fptr);
+ } else {
+ const char *error;
+ rettv->vval.v_number =
+ channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error);
+ if (!rettv->vval.v_number) {
+ emsg(error);
+ }
+ }
+}
+
+/// "last_buffer_nr()" function.
+void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ int n = 0;
+
+ FOR_ALL_BUFFERS(buf) {
+ if (n < buf->b_fnum) {
+ n = buf->b_fnum;
+ }
+ }
+
+ rettv->vval.v_number = n;
+}
+
+/// "termopen(cmd[, cwd])" function
+void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ if (check_secure()) {
+ return;
+ }
+
+ bool must_free = false;
+
+ if (argvars[1].v_type == VAR_UNKNOWN) {
+ must_free = true;
+ argvars[1].v_type = VAR_DICT;
+ argvars[1].vval.v_dict = tv_dict_alloc();
+ }
+
+ if (argvars[1].v_type != VAR_DICT) {
+ // Wrong argument types
+ semsg(_(e_invarg2), "expected dictionary");
+ return;
+ }
+
+ tv_dict_add_bool(argvars[1].vval.v_dict, S_LEN("term"), true);
+ f_jobstart(argvars, rettv, fptr);
+ if (must_free) {
+ tv_dict_free(argvars[1].vval.v_dict);
+ }
+}
diff --git a/src/nvim/eval/deprecated.h b/src/nvim/eval/deprecated.h
new file mode 100644
index 0000000000..e2e3ee436e
--- /dev/null
+++ b/src/nvim/eval/deprecated.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
+#include "nvim/types_defs.h" // IWYU pragma: keep
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "eval/deprecated.h.generated.h"
+#endif
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index 5b92f217d1..691fd405e9 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -8,7 +8,6 @@
#include "nvim/eval/typval_defs.h"
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/message.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 9e9e36d3c5..8db50a53b3 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -13,6 +13,8 @@
#include <uv.h>
#include "auto/config.h"
+#include "klib/kvec.h"
+#include "mpack/mpack_core.h"
#include "mpack/object.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
@@ -89,6 +91,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/normal.h"
#include "nvim/normal_defs.h"
@@ -545,8 +548,7 @@ static void f_byte2line(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
/// "call(func, arglist [, dict])" function
static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- if (argvars[1].v_type != VAR_LIST) {
- emsg(_(e_listreq));
+ if (tv_check_for_list_arg(argvars, 1) == FAIL) {
return;
}
if (argvars[1].vval.v_list == NULL) {
@@ -572,22 +574,32 @@ static void f_call(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (func == NULL || *func == NUL) {
return; // type error, empty name or null function
}
+ char *tofree = NULL;
+ if (argvars[0].v_type == VAR_STRING) {
+ char *p = func;
+ tofree = trans_function_name(&p, false, TFN_INT|TFN_QUIET, NULL, NULL);
+ if (tofree == NULL) {
+ emsg_funcname(e_unknown_function_str, func);
+ return;
+ }
+ func = tofree;
+ }
dict_T *selfdict = NULL;
if (argvars[2].v_type != VAR_UNKNOWN) {
if (tv_check_for_dict_arg(argvars, 2) == FAIL) {
- if (owned) {
- func_unref(func);
- }
- return;
+ goto done;
}
selfdict = argvars[2].vval.v_dict;
}
func_call(func, &argvars[1], partial, selfdict, rettv);
+
+done:
if (owned) {
func_unref(func);
}
+ xfree(tofree);
}
/// "changenr()" function
@@ -2402,14 +2414,15 @@ static void f_getchangelist(typval_T *argvars, typval_T *rettv, EvalFuncData fpt
if (buf == curwin->w_buffer) {
changelistindex = curwin->w_changelistidx;
} else {
- wininfo_T *wip;
+ changelistindex = buf->b_changelistlen;
- FOR_ALL_BUF_WININFO(buf, wip) {
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == curwin) {
+ changelistindex = wip->wi_changelistidx;
break;
}
}
- changelistindex = wip != NULL ? wip->wi_changelistidx : buf->b_changelistlen;
}
tv_list_append_number(rettv->vval.v_list, (varnumber_T)changelistindex);
@@ -3541,6 +3554,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
+ msg_ext_set_kind("list_cmd");
msg_start();
msg_row = Rows - 1; // for when 'cmdheight' > 1
lines_left = Rows; // avoid more prompt
@@ -3553,10 +3567,10 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
});
// Ask for choice.
- bool mouse_used;
- int selected = prompt_for_number(&mouse_used);
+ bool mouse_used = false;
+ int selected = prompt_for_input(NULL, 0, false, &mouse_used);
if (mouse_used) {
- selected -= lines_left;
+ selected = tv_list_len(argvars[0].vval.v_list) - (cmdline_row - mouse_row);
}
rettv->vval.v_number = selected;
@@ -3839,8 +3853,8 @@ static const char *required_env_vars[] = {
NULL
};
-static dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty,
- const char * const pty_term_name)
+dict_T *create_environment(const dictitem_T *job_env, const bool clear_env, const bool pty,
+ const char * const pty_term_name)
{
dict_T *env = tv_dict_alloc();
@@ -3932,7 +3946,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, EvalFuncData fptr)
+void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -3941,9 +3955,9 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
+ const char *cmd;
bool executable = true;
- char **argv = tv_to_argv(&argvars[0], NULL, &executable);
- dict_T *env = NULL;
+ char **argv = tv_to_argv(&argvars[0], &cmd, &executable);
if (!argv) {
rettv->vval.v_number = executable ? 0 : -1;
return; // Did error message in tv_to_argv.
@@ -3960,6 +3974,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
bool detach = false;
bool rpc = false;
bool pty = false;
+ bool term = false;
bool clear_env = false;
bool overlapped = false;
ChannelStdinMode stdin_mode = kChannelStdinPipe;
@@ -3973,7 +3988,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
detach = tv_dict_get_number(job_opts, "detach") != 0;
rpc = tv_dict_get_number(job_opts, "rpc") != 0;
- pty = tv_dict_get_number(job_opts, "pty") != 0;
+ term = tv_dict_get_number(job_opts, "term") != 0;
+ pty = term || tv_dict_get_number(job_opts, "pty") != 0;
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
@@ -3988,6 +4004,14 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
+ dictitem_T *const job_term = tv_dict_find(job_opts, S_LEN("term"));
+ if (job_term && VAR_BOOL != job_term->di_tv.v_type) {
+ // Restrict "term" field to boolean, in case we want to allow buffer numbers in the future.
+ semsg(_(e_invarg2), "'term' must be Boolean");
+ shell_free_argv(argv);
+ return;
+ }
+
if (pty && rpc) {
semsg(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
shell_free_argv(argv);
@@ -4031,29 +4055,92 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
uint16_t height = 0;
char *term_name = NULL;
- if (pty) {
- width = (uint16_t)tv_dict_get_number(job_opts, "width");
- height = (uint16_t)tv_dict_get_number(job_opts, "height");
- // Legacy method, before env option existed, to specify $TERM. No longer
- // documented, but still usable to avoid breaking scripts.
- term_name = tv_dict_get_string(job_opts, "TERM", false);
- if (!term_name) {
- term_name = "ansi";
+ if (term) {
+ if (text_locked()) {
+ text_locked_msg();
+ shell_free_argv(argv);
+ return;
}
+ if (curbuf->b_changed) {
+ emsg(_("jobstart(...,{term=true}) requires unmodified buffer"));
+ shell_free_argv(argv);
+ return;
+ }
+ assert(!rpc);
+ term_name = "xterm-256color";
+ cwd = cwd ? cwd : ".";
+ overlapped = false;
+ detach = false;
+ stdin_mode = kChannelStdinPipe;
+ width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin));
+ height = (uint16_t)curwin->w_height_inner;
}
- env = create_environment(job_env, clear_env, pty, term_name);
+ if (pty) {
+ width = width ? width : (uint16_t)tv_dict_get_number(job_opts, "width");
+ height = height ? height : (uint16_t)tv_dict_get_number(job_opts, "height");
+ // Deprecated TERM field is from before `env` option existed.
+ term_name = term_name ? term_name : tv_dict_get_string(job_opts, "TERM", false);
+ term_name = term_name ? term_name : "ansi";
+ }
+ dict_T *env = create_environment(job_env, clear_env, pty, term_name);
Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit, pty,
rpc, overlapped, detach, stdin_mode, cwd,
width, height, env, &rettv->vval.v_number);
- if (chan) {
+ if (!chan) {
+ return;
+ } else if (!term) {
channel_create_event(chan, NULL);
+ } else {
+ if (rettv->vval.v_number <= 0) {
+ return;
+ }
+
+ int pid = chan->stream.pty.proc.pid;
+
+ // "./…" => "/home/foo/…"
+ vim_FullName(cwd, NameBuff, sizeof(NameBuff), false);
+ // "/home/foo/…" => "~/…"
+ size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true);
+ // Trim slash.
+ if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) {
+ IObuff[len - 1] = NUL;
+ }
+
+ if (len == 1 && IObuff[0] == '/') {
+ // Avoid ambiguity in the URI when CWD is root directory.
+ IObuff[1] = '.';
+ IObuff[2] = NUL;
+ }
+
+ // Terminal URI: "term://$CWD//$PID:$CMD"
+ snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s", IObuff, pid, cmd);
+ // Buffer has no terminal associated yet; unset 'swapfile' to ensure no swapfile is created.
+ curbuf->b_p_swf = false;
+
+ apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
+ setfname(curbuf, NameBuff, NULL, true);
+ apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
+
+ Error err = ERROR_INIT;
+ // Set (deprecated) buffer-local vars (prefer 'channel' buffer-local option).
+ dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
+ INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err);
+ api_clear_error(&err);
+ dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
+ INTEGER_OBJ(pid), false, false, NULL, &err);
+ api_clear_error(&err);
+
+ channel_incref(chan);
+ channel_terminal_open(curbuf, chan);
+ channel_create_event(chan, NULL);
+ channel_decref(chan);
}
}
/// "jobstop()" function
-static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = 0;
@@ -4100,8 +4187,6 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
return;
}
- ui_busy_start();
- ui_flush();
list_T *args = argvars[0].vval.v_list;
Channel **jobs = xcalloc((size_t)tv_list_len(args), sizeof(*jobs));
MultiQueue *waiting_jobs = multiqueue_new_parent(loop_on_put, &main_loop);
@@ -4138,6 +4223,13 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
before = os_hrtime();
}
+ // Only mark the UI as busy when jobwait() blocks
+ const bool busy = remaining != 0;
+ if (busy) {
+ ui_busy_start();
+ ui_flush();
+ }
+
for (i = 0; i < tv_list_len(args); i++) {
if (remaining == 0) {
break; // Timeout.
@@ -4179,7 +4271,9 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
multiqueue_free(waiting_jobs);
xfree(jobs);
- ui_busy_stop();
+ if (busy) {
+ ui_busy_stop();
+ }
tv_list_ref(rv);
rettv->v_type = VAR_LIST;
rettv->vval.v_list = rv;
@@ -4240,20 +4334,6 @@ static void f_keytrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
xfree(escaped);
}
-/// "last_buffer_nr()" function.
-static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- int n = 0;
-
- FOR_ALL_BUFFERS(buf) {
- if (n < buf->b_fnum) {
- n = buf->b_fnum;
- }
- }
-
- rettv->vval.v_number = n;
-}
-
/// "len()" function
static void f_len(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -6348,103 +6428,6 @@ end:
api_clear_error(&err);
}
-/// "rpcstart()" function (DEPRECATED)
-static void f_rpcstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_STRING
- || (argvars[1].v_type != VAR_LIST && argvars[1].v_type != VAR_UNKNOWN)) {
- // Wrong argument types
- emsg(_(e_invarg));
- return;
- }
-
- list_T *args = NULL;
- int argsl = 0;
- if (argvars[1].v_type == VAR_LIST) {
- args = argvars[1].vval.v_list;
- argsl = tv_list_len(args);
- // Assert that all list items are strings
- int i = 0;
- TV_LIST_ITER_CONST(args, arg, {
- if (TV_LIST_ITEM_TV(arg)->v_type != VAR_STRING) {
- semsg(_("E5010: List item %d of the second argument is not a string"),
- i);
- return;
- }
- i++;
- });
- }
-
- if (argvars[0].vval.v_string == NULL || argvars[0].vval.v_string[0] == NUL) {
- emsg(_(e_api_spawn_failed));
- return;
- }
-
- // Allocate extra memory for the argument vector and the NULL pointer
- int argvl = argsl + 2;
- char **argv = xmalloc(sizeof(char *) * (size_t)argvl);
-
- // Copy program name
- argv[0] = xstrdup(argvars[0].vval.v_string);
-
- int i = 1;
- // Copy arguments to the vector
- if (argsl > 0) {
- TV_LIST_ITER_CONST(args, arg, {
- argv[i++] = xstrdup(tv_get_string(TV_LIST_ITEM_TV(arg)));
- });
- }
-
- // The last item of argv must be NULL
- argv[i] = NULL;
-
- Channel *chan = channel_job_start(argv, NULL, CALLBACK_READER_INIT,
- CALLBACK_READER_INIT, CALLBACK_NONE,
- false, true, false, false,
- kChannelStdinPipe, NULL, 0, 0, NULL,
- &rettv->vval.v_number);
- if (chan) {
- channel_create_event(chan, NULL);
- }
-}
-
-/// "rpcstop()" function
-static void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = 0;
-
- if (check_secure()) {
- return;
- }
-
- if (argvars[0].v_type != VAR_NUMBER) {
- // Wrong argument types
- emsg(_(e_invarg));
- return;
- }
-
- // 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, fptr);
- } else {
- const char *error;
- rettv->vval.v_number =
- channel_close((uint64_t)argvars[0].vval.v_number, kChannelPartRpc, &error);
- if (!rettv->vval.v_number) {
- emsg(error);
- }
- }
-}
-
static void screenchar_adjust(ScreenGrid **grid, int *row, int *col)
{
// TODO(bfredl): this is a hack for legacy tests which use screenchar()
@@ -8132,134 +8115,6 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
(char *)tag_pattern, (char *)fname);
}
-/// "termopen(cmd[, cwd])" function
-static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- if (check_secure()) {
- return;
- }
- if (text_locked()) {
- text_locked_msg();
- return;
- }
- if (curbuf->b_changed) {
- emsg(_("Can only call this function in an unmodified buffer"));
- return;
- }
-
- const char *cmd;
- bool executable = true;
- char **argv = tv_to_argv(&argvars[0], &cmd, &executable);
- if (!argv) {
- rettv->vval.v_number = executable ? 0 : -1;
- return; // Did error message in tv_to_argv.
- }
-
- if (argvars[1].v_type != VAR_DICT && argvars[1].v_type != VAR_UNKNOWN) {
- // Wrong argument type
- semsg(_(e_invarg2), "expected dictionary");
- shell_free_argv(argv);
- return;
- }
-
- CallbackReader on_stdout = CALLBACK_READER_INIT;
- CallbackReader on_stderr = CALLBACK_READER_INIT;
- Callback on_exit = CALLBACK_NONE;
- dict_T *job_opts = NULL;
- const char *cwd = ".";
- dict_T *env = NULL;
- const bool pty = true;
- bool clear_env = false;
- dictitem_T *job_env = NULL;
-
- if (argvars[1].v_type == VAR_DICT) {
- job_opts = argvars[1].vval.v_dict;
-
- const char *const new_cwd = tv_dict_get_string(job_opts, "cwd", false);
- if (new_cwd && *new_cwd != NUL) {
- cwd = new_cwd;
- // The new cwd must be a directory.
- if (!os_isdir(cwd)) {
- semsg(_(e_invarg2), "expected valid directory");
- shell_free_argv(argv);
- return;
- }
- }
-
- job_env = tv_dict_find(job_opts, S_LEN("env"));
- if (job_env && job_env->di_tv.v_type != VAR_DICT) {
- semsg(_(e_invarg2), "env");
- shell_free_argv(argv);
- return;
- }
-
- clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
-
- if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) {
- shell_free_argv(argv);
- return;
- }
- }
-
- env = create_environment(job_env, clear_env, pty, "xterm-256color");
-
- const bool rpc = false;
- const bool overlapped = false;
- const bool detach = false;
- ChannelStdinMode stdin_mode = kChannelStdinPipe;
- uint16_t term_width = (uint16_t)MAX(0, curwin->w_width_inner - win_col_off(curwin));
- Channel *chan = channel_job_start(argv, NULL, on_stdout, on_stderr, on_exit,
- pty, rpc, overlapped, detach, stdin_mode,
- cwd, term_width, (uint16_t)curwin->w_height_inner,
- env, &rettv->vval.v_number);
- if (rettv->vval.v_number <= 0) {
- return;
- }
-
- int pid = chan->stream.pty.proc.pid;
-
- // "./…" => "/home/foo/…"
- vim_FullName(cwd, NameBuff, sizeof(NameBuff), false);
- // "/home/foo/…" => "~/…"
- size_t len = home_replace(NULL, NameBuff, IObuff, sizeof(IObuff), true);
- // Trim slash.
- if (len != 1 && (IObuff[len - 1] == '\\' || IObuff[len - 1] == '/')) {
- IObuff[len - 1] = NUL;
- }
-
- if (len == 1 && IObuff[0] == '/') {
- // Avoid ambiguity in the URI when CWD is root directory.
- IObuff[1] = '.';
- IObuff[2] = NUL;
- }
-
- // Terminal URI: "term://$CWD//$PID:$CMD"
- snprintf(NameBuff, sizeof(NameBuff), "term://%s//%d:%s",
- IObuff, pid, cmd);
- // at this point the buffer has no terminal instance associated yet, so unset
- // the 'swapfile' option to ensure no swap file will be created
- curbuf->b_p_swf = false;
-
- apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf);
- setfname(curbuf, NameBuff, NULL, true);
- apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf);
-
- // Save the job id and pid in b:terminal_job_{id,pid}
- Error err = ERROR_INIT;
- // deprecated: use 'channel' buffer option
- dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"),
- INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err);
- api_clear_error(&err);
- dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"),
- INTEGER_OBJ(pid), false, false, NULL, &err);
- api_clear_error(&err);
-
- channel_incref(chan);
- channel_terminal_open(curbuf, chan);
- channel_create_event(chan, NULL);
- channel_decref(chan);
-}
-
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index e7b6a0feee..f9cf245e50 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -9,6 +9,7 @@
#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
@@ -32,6 +33,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/os/input.h"
#include "nvim/pos_defs.h"
@@ -2232,6 +2234,18 @@ dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, const ptr
return TV_DICT_HI2DI(hi);
}
+/// Check if a key is present in a dictionary.
+///
+/// @param[in] d Dictionary to check.
+/// @param[in] key Dictionary key.
+///
+/// @return whether the key is present in the dictionary.
+bool tv_dict_has_key(const dict_T *const d, const char *const key)
+ FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return tv_dict_find(d, key, -1) != NULL;
+}
+
/// Get a typval item from a dictionary and copy it into "rettv".
///
/// @param[in] d Dictionary to check.
@@ -2631,6 +2645,30 @@ int tv_dict_add_allocated_str(dict_T *const d, const char *const key, const size
return OK;
}
+/// Add a function entry to dictionary.
+///
+/// @param[out] d Dictionary to add entry to.
+/// @param[in] key Key to add.
+/// @param[in] key_len Key length.
+/// @param[in] fp Function to add.
+///
+/// @return OK in case of success, FAIL when key already exists.
+int tv_dict_add_func(dict_T *const d, const char *const key, const size_t key_len,
+ ufunc_T *const fp)
+ FUNC_ATTR_NONNULL_ARG(1, 2, 4)
+{
+ dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
+
+ item->di_tv.v_type = VAR_FUNC;
+ item->di_tv.vval.v_string = xmemdupz(fp->uf_name, fp->uf_namelen);
+ if (tv_dict_add(d, item) == FAIL) {
+ tv_dict_item_free(item);
+ return FAIL;
+ }
+ func_ref(item->di_tv.vval.v_string);
+ return OK;
+}
+
//{{{2 Operations on the whole dict
/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h
index 1ec7631c4b..90b0d4e4a9 100644
--- a/src/nvim/eval/typval_defs.h
+++ b/src/nvim/eval/typval_defs.h
@@ -357,9 +357,10 @@ struct ufunc {
funccall_T *uf_scoped; ///< l: local variables for closure
char *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
///< "<SNR>" as a string, otherwise NULL
- char uf_name[]; ///< Name of function (actual size equals name);
- ///< can start with <SNR>123_
- ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
+ size_t uf_namelen; ///< Length of uf_name (excluding the NUL)
+ char uf_name[]; ///< Name of function (actual size equals name);
+ ///< can start with <SNR>123_
+ ///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
};
struct partial_S {
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 0050a77fe3..84ee27493c 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -78,7 +78,6 @@ static funccall_T *current_funccal = NULL;
// item in it is still being used.
static funccall_T *previous_funccal = NULL;
-static const char *e_unknown_function_str = N_("E117: Unknown function: %s");
static const char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
static const char *e_funcdict = N_("E717: Dictionary entry already exists");
static const char *e_funcref = N_("E718: Funcref required");
@@ -105,6 +104,48 @@ hashtab_T *func_tbl_get(void)
return &func_hashtab;
}
+/// Get one function argument.
+/// Return a pointer to after the type.
+/// When something is wrong return "arg".
+static char *one_function_arg(char *arg, garray_T *newargs, bool skip)
+{
+ char *p = arg;
+
+ while (ASCII_ISALNUM(*p) || *p == '_') {
+ p++;
+ }
+ if (arg == p || isdigit((uint8_t)(*arg))
+ || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
+ || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
+ if (!skip) {
+ semsg(_("E125: Illegal argument: %s"), arg);
+ }
+ return arg;
+ }
+
+ if (newargs != NULL) {
+ ga_grow(newargs, 1);
+ uint8_t c = (uint8_t)(*p);
+ *p = NUL;
+ char *arg_copy = xstrdup(arg);
+
+ // Check for duplicate argument name.
+ for (int i = 0; i < newargs->ga_len; i++) {
+ if (strcmp(((char **)(newargs->ga_data))[i], arg_copy) == 0) {
+ semsg(_("E853: Duplicate argument name: %s"), arg_copy);
+ xfree(arg_copy);
+ return arg;
+ }
+ }
+ ((char **)(newargs->ga_data))[newargs->ga_len] = arg_copy;
+ newargs->ga_len++;
+
+ *p = (char)c;
+ }
+
+ return p;
+}
+
/// Get function arguments.
static int get_function_args(char **argp, char endchar, garray_T *newargs, int *varargs,
garray_T *default_args, bool skip)
@@ -135,36 +176,11 @@ static int get_function_args(char **argp, char endchar, garray_T *newargs, int *
mustend = true;
} else {
arg = p;
- while (ASCII_ISALNUM(*p) || *p == '_') {
- p++;
- }
- if (arg == p || isdigit((uint8_t)(*arg))
- || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
- || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
- if (!skip) {
- semsg(_("E125: Illegal argument: %s"), arg);
- }
+ p = one_function_arg(p, newargs, skip);
+ if (p == arg) {
break;
}
- if (newargs != NULL) {
- ga_grow(newargs, 1);
- uint8_t c = (uint8_t)(*p);
- *p = NUL;
- arg = xstrdup(arg);
-
- // Check for duplicate argument name.
- for (int i = 0; i < newargs->ga_len; i++) {
- if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) {
- semsg(_("E853: Duplicate argument name: %s"), arg);
- xfree(arg);
- goto err_ret;
- }
- }
- ((char **)(newargs->ga_data))[newargs->ga_len] = arg;
- newargs->ga_len++;
- *p = (char)c;
- }
if (*skipwhite(p) == '=' && default_args != NULL) {
typval_T rettv;
@@ -248,25 +264,47 @@ static void register_closure(ufunc_T *fp)
[current_funccal->fc_ufuncs.ga_len++] = fp;
}
+static char lambda_name[8 + NUMBUFLEN];
+static size_t lambda_namelen = 0;
+
/// @return a name for a lambda. Returned in static memory.
char *get_lambda_name(void)
{
- static char name[30];
static int lambda_no = 0;
- snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no);
- return name;
+ int n = snprintf(lambda_name, sizeof(lambda_name), "<lambda>%d", ++lambda_no);
+ if (n < 1) {
+ lambda_namelen = 0;
+ } else if (n >= (int)sizeof(lambda_name)) {
+ lambda_namelen = sizeof(lambda_name) - 1;
+ } else {
+ lambda_namelen = (size_t)n;
+ }
+
+ return lambda_name;
}
-static void set_ufunc_name(ufunc_T *fp, char *name)
+/// Get the length of the last lambda name.
+size_t get_lambda_name_len(void)
{
+ return lambda_namelen;
+}
+
+/// Allocate a "ufunc_T" for a function called "name".
+static ufunc_T *alloc_ufunc(const char *name, size_t namelen)
+{
+ size_t len = offsetof(ufunc_T, uf_name) + namelen + 1;
+ ufunc_T *fp = xcalloc(1, len);
STRCPY(fp->uf_name, name);
+ fp->uf_namelen = namelen;
if ((uint8_t)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);
+ len = namelen + 3;
+ fp->uf_name_exp = xmalloc(len);
+ snprintf(fp->uf_name_exp, len, "<SNR>%s", fp->uf_name + 3);
}
+
+ return fp;
}
/// Parse a lambda expression and get a Funcref from "*arg".
@@ -334,8 +372,9 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
garray_T newlines;
char *name = get_lambda_name();
+ size_t namelen = get_lambda_name_len();
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
+ fp = alloc_ufunc(name, namelen);
pt = xcalloc(1, sizeof(partial_T));
ga_init(&newlines, (int)sizeof(char *), 1);
@@ -353,7 +392,6 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
}
fp->uf_refcount = 1;
- set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
@@ -393,7 +431,10 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
errret:
ga_clear_strings(&newargs);
- xfree(fp);
+ if (fp != NULL) {
+ xfree(fp->uf_name_exp);
+ xfree(fp);
+ }
xfree(pt);
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
evalarg->eval_tofree = tofree;
@@ -611,33 +652,36 @@ static char *fname_trans_sid(const char *const name, char *const fname_buf, char
int *const error)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- const int llen = eval_fname_script(name);
- if (llen == 0) {
+ const char *script_name = name + eval_fname_script(name);
+ if (script_name == name) {
return (char *)name; // no prefix
}
fname_buf[0] = (char)K_SPECIAL;
fname_buf[1] = (char)KS_EXTRA;
fname_buf[2] = KE_SNR;
- int i = 3;
- if (eval_fname_sid(name)) { // "<SID>" or "s:"
+ size_t fname_buflen = 3;
+ if (!eval_fname_sid(name)) { // "<SID>" or "s:"
+ fname_buf[fname_buflen] = NUL;
+ } else {
if (current_sctx.sc_sid <= 0) {
*error = FCERR_SCRIPT;
} else {
- snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
- i = (int)strlen(fname_buf);
+ fname_buflen += (size_t)snprintf(fname_buf + fname_buflen,
+ FLEN_FIXED + 1 - fname_buflen,
+ "%" PRIdSCID "_",
+ current_sctx.sc_sid);
}
}
+ size_t fnamelen = fname_buflen + strlen(script_name);
char *fname;
- if ((size_t)i + strlen(name + llen) < FLEN_FIXED) {
- STRCPY(fname_buf + i, name + llen);
+ if (fnamelen < FLEN_FIXED) {
+ STRCPY(fname_buf + fname_buflen, script_name);
fname = fname_buf;
} else {
- fname = xmalloc((size_t)i + strlen(name + llen) + 1);
+ fname = xmalloc(fnamelen + 1);
*tofree = fname;
- memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
+ snprintf(fname, fnamelen + 1, "%s%s", fname_buf, script_name);
}
return fname;
}
@@ -695,20 +739,20 @@ ufunc_T *find_func(const char *name)
/// Copy the function name of "fp" to buffer "buf".
/// "buf" must be able to hold the function name plus three bytes.
/// Takes care of script-local function names.
-static void cat_func_name(char *buf, size_t buflen, ufunc_T *fp)
+static int cat_func_name(char *buf, size_t bufsize, ufunc_T *fp)
{
int len = -1;
- size_t uflen = strlen(fp->uf_name);
+ size_t uflen = fp->uf_namelen;
assert(uflen > 0);
if ((uint8_t)fp->uf_name[0] == K_SPECIAL && uflen > 3) {
- len = snprintf(buf, buflen, "<SNR>%s", fp->uf_name + 3);
+ len = snprintf(buf, bufsize, "<SNR>%s", fp->uf_name + 3);
} else {
- len = snprintf(buf, buflen, "%s", fp->uf_name);
+ len = snprintf(buf, bufsize, "%s", fp->uf_name);
}
- (void)len; // Avoid unused warning on release builds
assert(len > 0);
+ return (len >= (int)bufsize) ? (int)bufsize - 1 : len;
}
/// Add a number variable "name" to dict "dp" with value "nr".
@@ -875,7 +919,6 @@ 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_flags & FC_LUAREF) {
api_free_luaref(fp->uf_luaref);
@@ -914,6 +957,8 @@ static void func_free(ufunc_T *fp)
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) {
func_remove(fp);
}
+
+ XFREE_CLEAR(fp->uf_name_exp);
xfree(fp);
}
@@ -967,6 +1012,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool islambda = false;
char numbuf[NUMBUFLEN];
char *name;
+ size_t namelen;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
proftime_T wait_start;
@@ -1092,23 +1138,25 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
break;
}
}
+
+ namelen = strlen(name);
} else {
if ((fp->uf_flags & FC_NOARGS) != 0) {
// Bail out if no a: arguments used (in lambda).
break;
}
// "..." argument a:1, a:2, etc.
- snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
+ namelen = (size_t)snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
name = numbuf;
}
- if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
+ if (fixvar_idx < FIXVAR_CNT && namelen <= VAR_SHORT_LEN) {
v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ STRCPY(v->di_key, name);
} else {
- v = xmalloc(sizeof(dictitem_T) + strlen(name));
- v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
+ v = tv_dict_item_alloc_len(name, namelen);
+ v->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
}
- STRCPY(v->di_key, name);
// Note: the values are copied directly to avoid alloc/free.
// "argvars" must have VAR_FIXED for v_lock.
@@ -2085,7 +2133,7 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
len = (int)(end - lv.ll_name);
}
- size_t sid_buf_len = 0;
+ size_t sid_buflen = 0;
char sid_buf[20];
// Copy the function name to allocated memory.
@@ -2095,15 +2143,16 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
lead = 0; // do nothing
} else if (lead > 0) {
lead = 3;
- if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid(*pp)) {
+ if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name))
+ || eval_fname_sid(*pp)) {
// It's "s:" or "<SID>".
if (current_sctx.sc_sid <= 0) {
emsg(_(e_usingsid));
goto theend;
}
- sid_buf_len =
- (size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_", current_sctx.sc_sid);
- lead += (int)sid_buf_len;
+ sid_buflen = (size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_",
+ current_sctx.sc_sid);
+ lead += (int)sid_buflen;
}
} else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, (int)lv.ll_name_len)) {
semsg(_("E128: Function name must start with a capital or \"s:\": %s"),
@@ -2125,8 +2174,8 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
name[0] = (char)K_SPECIAL;
name[1] = (char)KS_EXTRA;
name[2] = KE_SNR;
- if (sid_buf_len > 0) { // If it's "<SID>"
- memcpy(name + 3, sid_buf, sid_buf_len);
+ if (sid_buflen > 0) { // If it's "<SID>"
+ memcpy(name + 3, sid_buf, sid_buflen);
}
}
memmove(name + lead, lv.ll_name, (size_t)len);
@@ -2162,12 +2211,12 @@ char *get_scriptlocal_funcname(char *funcname)
char sid_buf[25];
// Expand s: and <SID> prefix into <SNR>nr_<name>
- snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
+ size_t sid_buflen = (size_t)snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRIdSCID "_",
+ current_sctx.sc_sid);
const int off = *funcname == 's' ? 2 : 5;
- char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1);
- STRCPY(newname, sid_buf);
- strcat(newname, funcname + off);
+ size_t newnamesize = sid_buflen + strlen(funcname + off) + 1;
+ char *newname = xmalloc(newnamesize);
+ snprintf(newname, newnamesize, "%s%s", sid_buf, funcname + off);
return newname;
}
@@ -2193,8 +2242,6 @@ char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi)
return saved;
}
-#define MAX_FUNC_NESTING 50
-
/// List functions.
///
/// @param regmatch When NULL, all of them.
@@ -2225,12 +2272,291 @@ static void list_functions(regmatch_T *regmatch)
}
}
+#define MAX_FUNC_NESTING 50
+
+/// Read the body of a function, put every line in "newlines".
+/// This stops at "endfunction".
+/// "newlines" must already have been initialized.
+static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in,
+ char **line_to_free, bool show_block)
+{
+ bool saved_wait_return = need_wait_return;
+ char *line_arg = line_arg_in;
+ int indent = 2;
+ int nesting = 0;
+ char *skip_until = NULL;
+ int ret = FAIL;
+ bool is_heredoc = false;
+ char *heredoc_trimmed = NULL;
+ size_t heredoc_trimmedlen = 0;
+ bool do_concat = true;
+
+ while (true) {
+ if (KeyTyped) {
+ msg_scroll = true;
+ saved_wait_return = false;
+ }
+ need_wait_return = false;
+
+ char *theline;
+ char *p;
+ char *arg;
+
+ if (line_arg != NULL) {
+ // Use eap->arg, split up in parts by line breaks.
+ theline = line_arg;
+ p = vim_strchr(theline, '\n');
+ if (p == NULL) {
+ line_arg += strlen(line_arg);
+ } else {
+ *p = NUL;
+ line_arg = p + 1;
+ }
+ } else {
+ xfree(*line_to_free);
+ if (eap->ea_getline == NULL) {
+ theline = getcmdline(':', 0, indent, do_concat);
+ } else {
+ theline = eap->ea_getline(':', eap->cookie, indent, do_concat);
+ }
+ *line_to_free = theline;
+ }
+ if (KeyTyped) {
+ lines_left = Rows - 1;
+ }
+ if (theline == NULL) {
+ if (skip_until != NULL) {
+ semsg(_(e_missing_heredoc_end_marker_str), skip_until);
+ } else {
+ emsg(_("E126: Missing :endfunction"));
+ }
+ goto theend;
+ }
+ if (show_block) {
+ assert(indent >= 0);
+ ui_ext_cmdline_block_append((size_t)indent, theline);
+ }
+
+ // Detect line continuation: SOURCING_LNUM increased more than one.
+ linenr_T sourcing_lnum_off = get_sourced_lnum(eap->ea_getline, eap->cookie);
+ if (SOURCING_LNUM < sourcing_lnum_off) {
+ sourcing_lnum_off -= SOURCING_LNUM;
+ } else {
+ sourcing_lnum_off = 0;
+ }
+
+ if (skip_until != NULL) {
+ // Don't check for ":endfunc" between
+ // * ":append" and "."
+ // * ":python <<EOF" and "EOF"
+ // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
+ if (heredoc_trimmed == NULL
+ || (is_heredoc && skipwhite(theline) == theline)
+ || strncmp(theline, heredoc_trimmed, heredoc_trimmedlen) == 0) {
+ if (heredoc_trimmed == NULL) {
+ p = theline;
+ } else if (is_heredoc) {
+ p = skipwhite(theline) == theline ? theline : theline + heredoc_trimmedlen;
+ } else {
+ p = theline + heredoc_trimmedlen;
+ }
+ if (strcmp(p, skip_until) == 0) {
+ XFREE_CLEAR(skip_until);
+ XFREE_CLEAR(heredoc_trimmed);
+ heredoc_trimmedlen = 0;
+ do_concat = true;
+ is_heredoc = false;
+ }
+ }
+ } else {
+ // skip ':' and blanks
+ for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
+
+ // Check for "endfunction".
+ if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
+ if (*p == '!') {
+ p++;
+ }
+ char *nextcmd = NULL;
+ if (*p == '|') {
+ nextcmd = p + 1;
+ } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
+ nextcmd = line_arg;
+ } else if (*p != NUL && *p != '"' && p_verbose > 0) {
+ swmsg(true, _("W22: Text found after :endfunction: %s"), p);
+ }
+ if (nextcmd != NULL) {
+ // Another command follows. If the line came from "eap" we
+ // can simply point into it, otherwise we need to change
+ // "eap->cmdlinep".
+ eap->nextcmd = nextcmd;
+ if (*line_to_free != NULL) {
+ xfree(*eap->cmdlinep);
+ *eap->cmdlinep = *line_to_free;
+ *line_to_free = NULL;
+ }
+ }
+ break;
+ }
+
+ // Increase indent inside "if", "while", "for" and "try", decrease
+ // at "end".
+ if (indent > 2 && strncmp(p, "end", 3) == 0) {
+ indent -= 2;
+ } else if (strncmp(p, "if", 2) == 0
+ || strncmp(p, "wh", 2) == 0
+ || strncmp(p, "for", 3) == 0
+ || strncmp(p, "try", 3) == 0) {
+ indent += 2;
+ }
+
+ // Check for defining a function inside this function.
+ if (checkforcmd(&p, "function", 2)) {
+ if (*p == '!') {
+ p = skipwhite(p + 1);
+ }
+ p += eval_fname_script(p);
+ xfree(trans_function_name(&p, true, 0, NULL, NULL));
+ if (*skipwhite(p) == '(') {
+ if (nesting == MAX_FUNC_NESTING - 1) {
+ emsg(_(e_function_nesting_too_deep));
+ } else {
+ nesting++;
+ indent += 2;
+ }
+ }
+ }
+
+ // Check for ":append", ":change", ":insert".
+ p = skip_range(p, NULL);
+ if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
+ || (p[0] == 'c'
+ && (!ASCII_ISALPHA(p[1])
+ || (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
+ || (p[2] == 'a'
+ && (strncmp(&p[3], "nge", 3) != 0
+ || !ASCII_ISALPHA(p[6])))))))
+ || (p[0] == 'i'
+ && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
+ && (!ASCII_ISALPHA(p[2])
+ || (p[2] == 's')))))) {
+ skip_until = xmemdupz(".", 1);
+ }
+
+ // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
+ arg = skipwhite(skiptowhite(p));
+ if (arg[0] == '<' && arg[1] == '<'
+ && ((p[0] == 'p' && p[1] == 'y'
+ && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
+ || ((p[2] == '3' || p[2] == 'x')
+ && !ASCII_ISALPHA(p[3]))))
+ || (p[0] == 'p' && p[1] == 'e'
+ && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
+ || (p[0] == 't' && p[1] == 'c'
+ && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
+ || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
+ && !ASCII_ISALPHA(p[3]))
+ || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
+ && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
+ || (p[0] == 'm' && p[1] == 'z'
+ && (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
+ // ":python <<" continues until a dot, like ":append"
+ p = skipwhite(arg + 2);
+ if (strncmp(p, "trim", 4) == 0
+ && (p[4] == NUL || ascii_iswhite(p[4]))) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ heredoc_trimmedlen = (size_t)(skipwhite(theline) - theline);
+ heredoc_trimmed = xmemdupz(theline, heredoc_trimmedlen);
+ }
+ if (*p == NUL) {
+ skip_until = xmemdupz(".", 1);
+ } else {
+ skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
+ }
+ do_concat = false;
+ is_heredoc = true;
+ }
+
+ if (!is_heredoc) {
+ // Check for ":let v =<< [trim] EOF"
+ // and ":let [a, b] =<< [trim] EOF"
+ arg = p;
+ if (checkforcmd(&arg, "let", 2)) {
+ int var_count = 0;
+ int semicolon = 0;
+ arg = (char *)skip_var_list(arg, &var_count, &semicolon, true);
+ if (arg != NULL) {
+ arg = skipwhite(arg);
+ }
+ if (arg != NULL && strncmp(arg, "=<<", 3) == 0) {
+ p = skipwhite(arg + 3);
+ bool has_trim = false;
+ while (true) {
+ if (strncmp(p, "trim", 4) == 0
+ && (p[4] == NUL || ascii_iswhite(p[4]))) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ has_trim = true;
+ continue;
+ }
+ if (strncmp(p, "eval", 4) == 0
+ && (p[4] == NUL || ascii_iswhite(p[4]))) {
+ // Ignore leading white space.
+ p = skipwhite(p + 4);
+ continue;
+ }
+ break;
+ }
+ if (has_trim) {
+ heredoc_trimmedlen = (size_t)(skipwhite(theline) - theline);
+ heredoc_trimmed = xmemdupz(theline, heredoc_trimmedlen);
+ }
+ skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
+ do_concat = false;
+ is_heredoc = true;
+ }
+ }
+ }
+ }
+
+ // Add the line to the function.
+ ga_grow(newlines, 1 + (int)sourcing_lnum_off);
+
+ // Copy the line to newly allocated memory. get_one_sourceline()
+ // allocates 250 bytes per line, this saves 80% on average. The cost
+ // is an extra alloc/free.
+ p = xstrdup(theline);
+ ((char **)(newlines->ga_data))[newlines->ga_len++] = 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 **)(newlines->ga_data))[newlines->ga_len++] = NULL;
+ }
+
+ // Check for end of eap->arg.
+ if (line_arg != NULL && *line_arg == NUL) {
+ line_arg = NULL;
+ }
+ }
+
+ // Return OK when no error was detected.
+ if (!did_emsg) {
+ ret = OK;
+ }
+
+theend:
+ xfree(skip_until);
+ xfree(heredoc_trimmed);
+ need_wait_return |= saved_wait_return;
+ return ret;
+}
+
/// ":function"
void ex_function(exarg_T *eap)
{
- char *theline;
char *line_to_free = NULL;
- bool saved_wait_return = need_wait_return;
char *arg;
char *line_arg = NULL;
garray_T newargs;
@@ -2238,16 +2564,13 @@ void ex_function(exarg_T *eap)
garray_T newlines;
int varargs = false;
int flags = 0;
- ufunc_T *fp;
+ ufunc_T *fp = NULL;
+ bool free_fp = false;
bool overwrite = false;
funcdict_T fudi;
static int func_nr = 0; // number for nameless function
hashtab_T *ht;
- bool is_heredoc = false;
- char *skip_until = NULL;
- char *heredoc_trimmed = NULL;
bool show_block = false;
- bool do_concat = true;
// ":function" without argument: list functions.
if (ends_excmd(*eap->arg)) {
@@ -2494,251 +2817,14 @@ void ex_function(exarg_T *eap)
// Save the starting line number.
linenr_T sourcing_lnum_top = SOURCING_LNUM;
- int indent = 2;
- int nesting = 0;
- while (true) {
- if (KeyTyped) {
- msg_scroll = true;
- saved_wait_return = false;
- }
- need_wait_return = false;
-
- if (line_arg != NULL) {
- // Use eap->arg, split up in parts by line breaks.
- theline = line_arg;
- p = vim_strchr(theline, '\n');
- if (p == NULL) {
- line_arg += strlen(line_arg);
- } else {
- *p = NUL;
- line_arg = p + 1;
- }
- } else {
- xfree(line_to_free);
- if (eap->ea_getline == NULL) {
- theline = getcmdline(':', 0, indent, do_concat);
- } else {
- theline = eap->ea_getline(':', eap->cookie, indent, do_concat);
- }
- line_to_free = theline;
- }
- if (KeyTyped) {
- lines_left = Rows - 1;
- }
- if (theline == NULL) {
- if (skip_until != NULL) {
- semsg(_(e_missing_heredoc_end_marker_str), skip_until);
- } else {
- emsg(_("E126: Missing :endfunction"));
- }
- goto erret;
- }
- if (show_block) {
- assert(indent >= 0);
- ui_ext_cmdline_block_append((size_t)indent, theline);
- }
-
- // Detect line continuation: SOURCING_LNUM increased more than one.
- linenr_T sourcing_lnum_off = get_sourced_lnum(eap->ea_getline, eap->cookie);
- if (SOURCING_LNUM < sourcing_lnum_off) {
- sourcing_lnum_off -= SOURCING_LNUM;
- } else {
- sourcing_lnum_off = 0;
- }
-
- if (skip_until != NULL) {
- // Don't check for ":endfunc" between
- // * ":append" and "."
- // * ":python <<EOF" and "EOF"
- // * ":let {var-name} =<< [trim] {marker}" and "{marker}"
- if (heredoc_trimmed == NULL
- || (is_heredoc && skipwhite(theline) == theline)
- || strncmp(theline, heredoc_trimmed,
- strlen(heredoc_trimmed)) == 0) {
- if (heredoc_trimmed == NULL) {
- p = theline;
- } else if (is_heredoc) {
- p = skipwhite(theline) == theline
- ? theline : theline + strlen(heredoc_trimmed);
- } else {
- p = theline + strlen(heredoc_trimmed);
- }
- if (strcmp(p, skip_until) == 0) {
- XFREE_CLEAR(skip_until);
- XFREE_CLEAR(heredoc_trimmed);
- do_concat = true;
- is_heredoc = false;
- }
- }
- } else {
- // skip ':' and blanks
- for (p = theline; ascii_iswhite(*p) || *p == ':'; p++) {}
-
- // Check for "endfunction".
- if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) {
- if (*p == '!') {
- p++;
- }
- char *nextcmd = NULL;
- if (*p == '|') {
- nextcmd = p + 1;
- } else if (line_arg != NULL && *skipwhite(line_arg) != NUL) {
- nextcmd = line_arg;
- } else if (*p != NUL && *p != '"' && p_verbose > 0) {
- swmsg(true, _("W22: Text found after :endfunction: %s"), p);
- }
- if (nextcmd != NULL) {
- // Another command follows. If the line came from "eap" we
- // can simply point into it, otherwise we need to change
- // "eap->cmdlinep".
- eap->nextcmd = nextcmd;
- if (line_to_free != NULL) {
- xfree(*eap->cmdlinep);
- *eap->cmdlinep = line_to_free;
- line_to_free = NULL;
- }
- }
- break;
- }
-
- // Increase indent inside "if", "while", "for" and "try", decrease
- // at "end".
- if (indent > 2 && strncmp(p, "end", 3) == 0) {
- indent -= 2;
- } else if (strncmp(p, "if", 2) == 0
- || strncmp(p, "wh", 2) == 0
- || strncmp(p, "for", 3) == 0
- || strncmp(p, "try", 3) == 0) {
- indent += 2;
- }
-
- // Check for defining a function inside this function.
- if (checkforcmd(&p, "function", 2)) {
- if (*p == '!') {
- p = skipwhite(p + 1);
- }
- p += eval_fname_script(p);
- xfree(trans_function_name(&p, true, 0, NULL, NULL));
- if (*skipwhite(p) == '(') {
- if (nesting == MAX_FUNC_NESTING - 1) {
- emsg(_(e_function_nesting_too_deep));
- } else {
- nesting++;
- indent += 2;
- }
- }
- }
-
- // Check for ":append", ":change", ":insert".
- p = skip_range(p, NULL);
- if ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
- || (p[0] == 'c'
- && (!ASCII_ISALPHA(p[1])
- || (p[1] == 'h' && (!ASCII_ISALPHA(p[2])
- || (p[2] == 'a'
- && (strncmp(&p[3], "nge", 3) != 0
- || !ASCII_ISALPHA(p[6])))))))
- || (p[0] == 'i'
- && (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
- && (!ASCII_ISALPHA(p[2])
- || (p[2] == 's')))))) {
- skip_until = xstrdup(".");
- }
-
- // heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
- arg = skipwhite(skiptowhite(p));
- if (arg[0] == '<' && arg[1] == '<'
- && ((p[0] == 'p' && p[1] == 'y'
- && (!ASCII_ISALNUM(p[2]) || p[2] == 't'
- || ((p[2] == '3' || p[2] == 'x')
- && !ASCII_ISALPHA(p[3]))))
- || (p[0] == 'p' && p[1] == 'e'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
- || (p[0] == 't' && p[1] == 'c'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
- || (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
- && !ASCII_ISALPHA(p[3]))
- || (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
- && (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
- || (p[0] == 'm' && p[1] == 'z'
- && (!ASCII_ISALPHA(p[2]) || p[2] == 's')))) {
- // ":python <<" continues until a dot, like ":append"
- p = skipwhite(arg + 2);
- if (strncmp(p, "trim", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
- }
- if (*p == NUL) {
- skip_until = xstrdup(".");
- } else {
- skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
- }
- do_concat = false;
- is_heredoc = true;
- }
-
- // Check for ":let v =<< [trim] EOF"
- // and ":let [a, b] =<< [trim] EOF"
- arg = p;
- if (checkforcmd(&arg, "let", 2)) {
- int var_count = 0;
- int semicolon = 0;
- arg = (char *)skip_var_list(arg, &var_count, &semicolon, true);
- if (arg != NULL) {
- arg = skipwhite(arg);
- }
- if (arg != NULL && strncmp(arg, "=<<", 3) == 0) {
- p = skipwhite(arg + 3);
- while (true) {
- if (strncmp(p, "trim", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
- continue;
- }
- if (strncmp(p, "eval", 4) == 0) {
- // Ignore leading white space.
- p = skipwhite(p + 4);
- continue;
- }
- break;
- }
- skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
- do_concat = false;
- is_heredoc = true;
- }
- }
- }
-
- // Add the line to the function.
- ga_grow(&newlines, 1 + (int)sourcing_lnum_off);
-
- // Copy the line to newly allocated memory. get_one_sourceline()
- // allocates 250 bytes per line, this saves 80% on average. The cost
- // is an extra alloc/free.
- p = xstrdup(theline);
- ((char **)(newlines.ga_data))[newlines.ga_len++] = 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 **)(newlines.ga_data))[newlines.ga_len++] = NULL;
- }
-
- // Check for end of eap->arg.
- if (line_arg != NULL && *line_arg == NUL) {
- line_arg = NULL;
- }
- }
-
- // Don't define the function when skipping commands or when an error was
- // detected.
- if (eap->skip || did_emsg) {
+ // Do not define the function when getting the body fails and when skipping.
+ if (get_function_body(eap, &newlines, line_arg, &line_to_free, show_block) == FAIL
+ || eap->skip) {
goto erret;
}
// If there are no errors, add the function
+ size_t namelen = 0;
if (fudi.fd_dict == NULL) {
dictitem_T *v = find_var(name, strlen(name), &ht, false);
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
@@ -2754,11 +2840,11 @@ void ex_function(exarg_T *eap)
&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
|| fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) {
emsg_funcname(e_funcexts, name);
- goto erret;
+ goto errret_keep;
}
if (fp->uf_calls > 0) {
emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name);
- goto erret;
+ goto errret_keep;
}
if (fp->uf_refcount > 1) {
// This function is referenced somewhere, don't redefine it but
@@ -2779,7 +2865,7 @@ void ex_function(exarg_T *eap)
}
}
} else {
- char numbuf[20];
+ char numbuf[NUMBUFLEN];
fp = NULL;
if (fudi.fd_newkey == NULL && !eap->forceit) {
@@ -2799,8 +2885,8 @@ void ex_function(exarg_T *eap)
// Give the function a sequential number. Can only be used with a
// Funcref!
xfree(name);
- snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
- name = xstrdup(numbuf);
+ namelen = (size_t)snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
+ name = xmemdupz(numbuf, namelen);
}
if (fp == NULL) {
@@ -2824,7 +2910,10 @@ void ex_function(exarg_T *eap)
}
}
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
+ if (namelen == 0) {
+ namelen = strlen(name);
+ }
+ fp = alloc_ufunc(name, namelen);
if (fudi.fd_dict != NULL) {
if (fudi.fd_di == NULL) {
@@ -2832,7 +2921,7 @@ void ex_function(exarg_T *eap)
fudi.fd_di = tv_dict_item_alloc(fudi.fd_newkey);
if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
xfree(fudi.fd_di);
- xfree(fp);
+ XFREE_CLEAR(fp);
goto erret;
}
} else {
@@ -2840,19 +2929,18 @@ void ex_function(exarg_T *eap)
tv_clear(&fudi.fd_di->di_tv);
}
fudi.fd_di->di_tv.v_type = VAR_FUNC;
- fudi.fd_di->di_tv.vval.v_string = xstrdup(name);
+ fudi.fd_di->di_tv.vval.v_string = xmemdupz(name, namelen);
// behave like "dict" was used
flags |= FC_DICT;
}
// insert the new function in the function list
- set_ufunc_name(fp, name);
if (overwrite) {
hashitem_T *hi = hash_find(&func_hashtab, name);
hi->hi_key = UF2HIKEY(fp);
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
- xfree(fp);
+ free_fp = true;
goto erret;
}
fp->uf_refcount = 1;
@@ -2881,18 +2969,27 @@ void ex_function(exarg_T *eap)
goto ret_free;
erret:
+ if (fp != NULL) {
+ // these were set to "newargs" and "default_args", which are cleared below
+ ga_init(&fp->uf_args, (int)sizeof(char *), 1);
+ ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
+ }
+errret_2:
+ if (fp != NULL) {
+ XFREE_CLEAR(fp->uf_name_exp);
+ }
+ if (free_fp) {
+ XFREE_CLEAR(fp);
+ }
+errret_keep:
ga_clear_strings(&newargs);
ga_clear_strings(&default_args);
-errret_2:
ga_clear_strings(&newlines);
ret_free:
- xfree(skip_until);
- xfree(heredoc_trimmed);
xfree(line_to_free);
xfree(fudi.fd_newkey);
xfree(name);
did_emsg |= saved_did_emsg;
- need_wait_return |= saved_wait_return;
if (show_block) {
ui_ext_cmdline_block_leave();
}
@@ -2979,15 +3076,16 @@ char *get_user_func_name(expand_T *xp, int idx)
return ""; // don't show dict and lambda functions
}
- if (strlen(fp->uf_name) + 4 >= IOSIZE) {
+ if (fp->uf_namelen + 4 >= IOSIZE) {
return fp->uf_name; // Prevent overflow.
}
- cat_func_name(IObuff, IOSIZE, fp);
+ int len = cat_func_name(IObuff, IOSIZE, fp);
if (xp->xp_context != EXPAND_USER_FUNC) {
- xstrlcat(IObuff, "(", IOSIZE);
+ xstrlcpy(IObuff + len, "(", IOSIZE - (size_t)len);
if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) {
- xstrlcat(IObuff, ")", IOSIZE);
+ len++;
+ xstrlcpy(IObuff + len, ")", IOSIZE - (size_t)len);
}
}
return IObuff;
@@ -3589,21 +3687,26 @@ char *get_return_cmd(void *rettv)
{
char *s = NULL;
char *tofree = NULL;
+ size_t slen = 0;
if (rettv != NULL) {
tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
}
if (s == NULL) {
s = "";
+ } else {
+ slen = strlen(s);
}
xstrlcpy(IObuff, ":return ", IOSIZE);
xstrlcpy(IObuff + 8, s, IOSIZE - 8);
- if (strlen(s) + 8 >= IOSIZE) {
+ size_t IObufflen = 8 + slen;
+ if (IObufflen >= IOSIZE) {
STRCPY(IObuff + IOSIZE - 4, "...");
+ IObufflen = IOSIZE - 1;
}
xfree(tofree);
- return xstrdup(IObuff);
+ return xstrnsave(IObuff, IObufflen);
}
/// Get next function line.
@@ -4046,7 +4149,8 @@ bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
char *register_luafunc(LuaRef ref)
{
char *name = get_lambda_name();
- ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
+ size_t namelen = get_lambda_name_len();
+ ufunc_T *fp = alloc_ufunc(name, namelen);
fp->uf_refcount = 1;
fp->uf_varargs = true;
@@ -4055,7 +4159,6 @@ char *register_luafunc(LuaRef ref)
fp->uf_script_ctx = current_sctx;
fp->uf_luaref = ref;
- STRCPY(fp->uf_name, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
// coverity[leaked_storage]
diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c
index a8358aab51..c64477741d 100644
--- a/src/nvim/eval/vars.c
+++ b/src/nvim/eval/vars.c
@@ -40,7 +40,6 @@
#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
-#include "nvim/option_vars.h"
#include "nvim/os/os.h"
#include "nvim/search.h"
#include "nvim/strings.h"
@@ -811,9 +810,9 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
// Find the end of the name.
char *arg_end = NULL;
OptIndex opt_idx;
- int scope;
+ int opt_flags;
- char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &scope);
+ char *const p = (char *)find_option_var_end((const char **)&arg, &opt_idx, &opt_flags);
if (p == NULL || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) {
emsg(_(e_letunexp));
@@ -825,7 +824,7 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
bool is_tty_opt = is_tty_option(arg);
bool hidden = is_option_hidden(opt_idx);
- OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, scope);
+ OptVal curval = is_tty_opt ? get_tty_option(arg) : get_option_value(opt_idx, opt_flags);
OptVal newval = NIL_OPTVAL;
if (curval.type == kOptValTypeNil) {
@@ -845,11 +844,10 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
goto theend;
}
- // Don't assume current and new values are of the same type in order to future-proof the code for
- // when an option can have multiple types.
- const bool is_num = ((curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean)
- && (newval.type == kOptValTypeNumber || newval.type == kOptValTypeBoolean));
- const bool is_string = curval.type == kOptValTypeString && newval.type == kOptValTypeString;
+ // Current value and new value must have the same type.
+ assert(curval.type == newval.type);
+ const bool is_num = curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean;
+ const bool is_string = curval.type == kOptValTypeString;
if (op != NULL && *op != '=') {
if (!hidden && is_num) { // number or bool
@@ -874,15 +872,19 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const,
} else {
newval = BOOLEAN_OPTVAL(TRISTATE_FROM_INT(new_n));
}
- } else if (!hidden && is_string
- && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string
- OptVal newval_old = newval;
- newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data));
- optval_free(newval_old);
+ } else if (!hidden && is_string) { // string
+ const char *curval_data = curval.data.string.data;
+ const char *newval_data = newval.data.string.data;
+
+ if (curval_data != NULL && newval_data != NULL) {
+ OptVal newval_old = newval;
+ newval = CSTR_AS_OPTVAL(concat_str(curval_data, newval_data));
+ optval_free(newval_old);
+ }
}
}
- const char *err = set_option_value_handle_tty(arg, opt_idx, newval, scope);
+ const char *err = set_option_value_handle_tty(arg, opt_idx, newval, opt_flags);
arg_end = p;
if (err != NULL) {
emsg(_(err));
@@ -1409,6 +1411,7 @@ static void list_one_var(dictitem_T *v, const char *prefix, int *first)
static void list_one_var_a(const char *prefix, const char *name, const ptrdiff_t name_len,
const VarType type, const char *string, int *first)
{
+ msg_ext_set_kind("list_cmd");
// don't use msg() to avoid overwriting "v:statusmsg"
msg_start();
msg_puts(prefix);
@@ -1902,8 +1905,6 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off)
///
/// @return Typval converted to OptVal. Must be freed by caller.
/// Returns NIL_OPTVAL for invalid option name.
-///
-/// TODO(famiu): Refactor this to support multitype options.
static OptVal tv_to_optval(typval_T *tv, OptIndex opt_idx, const char *option, bool *error)
{
OptVal value = NIL_OPTVAL;
diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c
index 86495f1cb6..2e2758d3bc 100644
--- a/src/nvim/eval/window.c
+++ b/src/nvim/eval/window.c
@@ -31,7 +31,6 @@
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
-#include "nvim/winfloat.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/window.c.generated.h"
@@ -326,6 +325,7 @@ static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr)
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("leftcol"), wp->w_leftcol);
tv_dict_add_nr(dict, S_LEN("winbar"), wp->w_winbar_height);
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);
diff --git a/src/nvim/event/proc.c b/src/nvim/event/proc.c
index 5ae3bd8c2d..37cb102d11 100644
--- a/src/nvim/event/proc.c
+++ b/src/nvim/event/proc.c
@@ -1,8 +1,10 @@
#include <assert.h>
#include <inttypes.h>
#include <signal.h>
+#include <string.h>
#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/event/libuv_proc.h"
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
@@ -13,6 +15,7 @@
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/main.h"
+#include "nvim/memory_defs.h"
#include "nvim/os/proc.h"
#include "nvim/os/pty_proc.h"
#include "nvim/os/shell.h"
diff --git a/src/nvim/event/proc.h b/src/nvim/event/proc.h
index f525d46f87..cd4d5913ac 100644
--- a/src/nvim/event/proc.h
+++ b/src/nvim/event/proc.h
@@ -4,6 +4,7 @@
#include <stddef.h>
#include "nvim/event/defs.h" // IWYU pragma: keep
+#include "nvim/os/os_defs.h"
#include "nvim/types_defs.h"
static inline Proc proc_init(Loop *loop, ProcType type, void *data)
@@ -21,8 +22,8 @@ static inline Proc proc_init(Loop *loop, ProcType type, void *data)
.argv = NULL,
.exepath = NULL,
.in = { .closed = false },
- .out = { .s.closed = false },
- .err = { .s.closed = false },
+ .out = { .s.closed = false, .s.fd = STDOUT_FILENO },
+ .err = { .s.closed = false, .s.fd = STDERR_FILENO },
.cb = NULL,
.closed = false,
.internal_close_cb = NULL,
diff --git a/src/nvim/event/rstream.c b/src/nvim/event/rstream.c
index 15bdc547d5..6304953029 100644
--- a/src/nvim/event/rstream.c
+++ b/src/nvim/event/rstream.c
@@ -1,7 +1,6 @@
#include <assert.h>
#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
+#include <string.h>
#include <uv.h>
#include "nvim/event/multiqueue.h"
@@ -9,7 +8,8 @@
#include "nvim/event/stream.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
-#include "nvim/main.h"
+#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/os/os_defs.h"
#include "nvim/types_defs.h"
diff --git a/src/nvim/event/socket.c b/src/nvim/event/socket.c
index 1214c3e336..c340ef2826 100644
--- a/src/nvim/event/socket.c
+++ b/src/nvim/event/socket.c
@@ -33,7 +33,7 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher, const char *endpoint
char *host_end = strrchr(addr, ':');
if (host_end && addr != host_end) {
- // Split user specified address into two strings, addr(hostname) and port.
+ // Split user specified address into two strings, addr (hostname) and port.
// The port part in watcher->addr will be updated later.
*host_end = NUL;
char *port = host_end + 1;
diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c
index 71de6ee1ba..9c155b55ea 100644
--- a/src/nvim/event/stream.c
+++ b/src/nvim/event/stream.c
@@ -8,6 +8,7 @@
#include "nvim/event/loop.h"
#include "nvim/event/stream.h"
#include "nvim/log.h"
+#include "nvim/memory.h"
#include "nvim/types_defs.h"
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 8cccf08e11..34217107aa 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -15,6 +15,7 @@
#include "auto/config.h"
#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/arglist.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
@@ -52,7 +53,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -73,7 +73,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
@@ -3708,12 +3707,9 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
// Loop until 'y', 'n', 'q', CTRL-E or CTRL-Y typed.
while (subflags.do_ask) {
if (exmode_active) {
- char *prompt;
- char *resp;
- colnr_T sc, ec;
-
print_line_no_prefix(lnum, subflags.do_number, subflags.do_list);
+ colnr_T sc, ec;
getvcol(curwin, &curwin->w_cursor, &sc, NULL, NULL);
curwin->w_cursor.col = MAX(regmatch.endpos[0].col - 1, 0);
@@ -3725,10 +3721,11 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
ec += numw;
}
- prompt = xmallocz((size_t)ec + 1);
+ char *prompt = xmallocz((size_t)ec + 1);
memset(prompt, ' ', (size_t)sc);
memset(prompt + sc, '^', (size_t)(ec - sc) + 1);
- resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL, CALLBACK_NONE);
+ char *resp = getcmdline_prompt(-1, prompt, 0, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE, false, NULL);
msg_putchar('\n');
xfree(prompt);
if (resp != NULL) {
@@ -3795,35 +3792,17 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const int cmdpreview_n
redraw_later(curwin, UPD_SOME_VALID);
curwin->w_p_fen = save_p_fen;
- if (msg_row == Rows - 1) {
- msg_didout = false; // avoid a scroll-up
- }
- msg_starthere();
- i = msg_scroll;
- msg_scroll = 0; // truncate msg when
- // needed
- msg_no_more = true;
- msg_ext_set_kind("confirm_sub");
- // Same highlight as wait_return().
- smsg(HLF_R, _("replace with %s (y/n/a/q/l/^E/^Y)?"), sub);
- msg_no_more = false;
- msg_scroll = i;
- if (!ui_has(kUIMessages)) {
- ui_cursor_goto(msg_row, msg_col);
- }
- RedrawingDisabled = temp;
- no_mapping++; // don't map this key
- allow_keys++; // allow special keys
- typed = plain_vgetc();
- no_mapping--;
- allow_keys--;
+ char *p = _("replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)");
+ snprintf(IObuff, IOSIZE, p, sub);
+ p = xstrdup(IObuff);
+ typed = prompt_for_input(p, HLF_R, true, NULL);
+ xfree(p);
- // clear the question
- msg_didout = false; // don't scroll up
- msg_col = 0;
+ msg_didout = false; // don't scroll up
gotocmdline(true);
p_lz = save_p_lz;
+ RedrawingDisabled = temp;
// restore the line
if (orig_line != NULL) {
@@ -4809,7 +4788,7 @@ void ex_oldfiles(exarg_T *eap)
// File selection prompt on ":browse oldfiles"
if (cmdmod.cmod_flags & CMOD_BROWSE) {
quit_more = false;
- nr = prompt_for_number(false);
+ nr = prompt_for_input(NULL, 0, false, NULL);
msg_starthere();
if (nr > 0 && nr <= tv_list_len(l)) {
const char *const p = tv_list_find_str(l, nr - 1);
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index c8574c805c..0cdc397e9c 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -1947,6 +1947,12 @@ M.cmds = {
func = 'ex_packloadall',
},
{
+ command = 'pbuffer',
+ flags = bit.bor(BANG, RANGE, BUFNAME, BUFUNL, COUNT, EXTRA, CMDARG, TRLBAR),
+ addr_type = 'ADDR_BUFFERS',
+ func = 'ex_pbuffer',
+ },
+ {
command = 'pclose',
flags = bit.bor(BANG, TRLBAR),
addr_type = 'ADDR_NONE',
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index e37c37e8e6..61a6dab897 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -30,10 +30,10 @@
#include "nvim/fileio.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/macros_defs.h"
#include "nvim/mark.h"
+#include "nvim/memline_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h
index 4924e86470..9dc922b0d9 100644
--- a/src/nvim/ex_cmds_defs.h
+++ b/src/nvim/ex_cmds_defs.h
@@ -94,35 +94,6 @@ typedef struct exarg exarg_T;
typedef void (*ex_func_T)(exarg_T *eap);
typedef int (*ex_preview_func_T)(exarg_T *eap, int cmdpreview_ns, handle_T cmdpreview_bufnr);
-// NOTE: These possible could be removed and changed so that
-// Callback could take a "command" style string, and simply
-// execute that (instead of it being a function).
-//
-// But it's still a bit weird to do that.
-//
-// Another option would be that we just make a callback reference to
-// "execute($INPUT)" or something like that, so whatever the user
-// sends in via autocmds is just executed via this.
-//
-// However, that would probably have some performance cost (probably
-// very marginal, but still some cost either way).
-typedef enum {
- CALLABLE_NONE,
- CALLABLE_EX,
- CALLABLE_CB,
-} AucmdExecutableType;
-
-typedef struct aucmd_executable_t AucmdExecutable;
-struct aucmd_executable_t {
- AucmdExecutableType type;
- union {
- char *cmd;
- Callback cb;
- } callable;
-};
-
-#define AUCMD_EXECUTABLE_INIT { .type = CALLABLE_NONE }
-
typedef char *(*LineGetter)(int, void *, int, bool);
/// Structure for command definition.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index f5ecedf827..ceb17a995d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -51,7 +51,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/input.h"
@@ -980,12 +979,10 @@ void handle_did_throw(void)
force_abort = true;
}
- msg_ext_set_kind("emsg"); // kind=emsg for :throw, exceptions. #9993
-
if (messages != NULL) {
do {
msglist_T *next = messages->next;
- emsg_multiline(messages->msg, messages->multiline);
+ emsg_multiline(messages->msg, "emsg", HLF_E, messages->multiline);
xfree(messages->msg);
xfree(messages->sfile);
xfree(messages);
@@ -2204,7 +2201,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
errormsg = _("E493: Backwards range given");
goto doend;
}
- if (ask_yesno(_("Backwards range given, OK to swap"), false) != 'y') {
+ if (ask_yesno(_("Backwards range given, OK to swap")) != 'y') {
goto doend;
}
}
@@ -4504,6 +4501,12 @@ static void ex_bunload(exarg_T *eap)
/// :[N]sbuffer [N] to buffer N
static void ex_buffer(exarg_T *eap)
{
+ do_exbuffer(eap);
+}
+
+/// ":buffer" command and alike.
+static void do_exbuffer(exarg_T *eap)
+{
if (*eap->arg) {
eap->errmsg = ex_errmsg(e_trailing_arg, eap->arg);
} else {
@@ -6103,12 +6106,20 @@ static void ex_sleep(exarg_T *eap)
default:
semsg(_(e_invarg2), eap->arg); return;
}
- do_sleep(len);
+
+ // Hide the cursor if invoked with !
+ do_sleep(len, eap->forceit);
}
/// Sleep for "msec" milliseconds, but return early on CTRL-C.
-void do_sleep(int64_t msec)
+///
+/// @param hide_cursor hide the cursor if true
+void do_sleep(int64_t msec, bool hide_cursor)
{
+ if (hide_cursor) {
+ ui_busy_start();
+ }
+
ui_flush(); // flush before waiting
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, main_loop.events, msec, got_int);
@@ -6117,6 +6128,10 @@ void do_sleep(int64_t msec)
if (got_int) {
vpeekc();
}
+
+ if (hide_cursor) {
+ ui_busy_stop();
+ }
}
/// ":winsize" command (obsolete).
@@ -6992,14 +7007,35 @@ static void ex_ptag(exarg_T *eap)
static void ex_pedit(exarg_T *eap)
{
win_T *curwin_save = curwin;
+ prepare_preview_window();
+ // Edit the file.
+ do_exedit(eap, NULL);
+
+ back_to_current_window(curwin_save);
+}
+
+/// ":pbuffer"
+static void ex_pbuffer(exarg_T *eap)
+{
+ win_T *curwin_save = curwin;
+ prepare_preview_window();
+
+ // Go to the buffer.
+ do_exbuffer(eap);
+
+ back_to_current_window(curwin_save);
+}
+
+static void prepare_preview_window(void)
+{
// Open the preview window or popup and make it the current window.
g_do_tagpreview = (int)p_pvh;
prepare_tagpreview(true);
+}
- // Edit the file.
- do_exedit(eap, NULL);
-
+static void back_to_current_window(win_T *curwin_save)
+{
if (curwin != curwin_save && win_valid(curwin_save)) {
// Return cursor to where we were
validate_cursor(curwin);
@@ -7364,8 +7400,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum
*errormsg = _(e_usingsid);
return NULL;
}
- snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_",
- current_sctx.sc_sid);
+ snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_", current_sctx.sc_sid);
result = strbuf;
break;
@@ -7738,7 +7773,7 @@ static void ex_terminal(exarg_T *eap)
if (*eap->arg != NUL) { // Run {cmd} in 'shell'.
char *name = vim_strsave_escaped(eap->arg, "\"\\");
snprintf(ex_cmd + len, sizeof(ex_cmd) - len,
- " | call termopen(\"%s\")", name);
+ " | call jobstart(\"%s\",{'term':v:true})", name);
xfree(name);
} else { // No {cmd}: run the job with tokenized 'shell'.
if (*p_sh == NUL) {
@@ -7761,7 +7796,7 @@ static void ex_terminal(exarg_T *eap)
shell_free_argv(argv);
snprintf(ex_cmd + len, sizeof(ex_cmd) - len,
- " | call termopen([%s])", shell_argv + 1);
+ " | call jobstart([%s], {'term':v:true})", shell_argv + 1);
}
do_cmdline_cmd(ex_cmd);
diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c
index f9936dd88e..18c691d076 100644
--- a/src/nvim/ex_eval.c
+++ b/src/nvim/ex_eval.c
@@ -479,6 +479,9 @@ static int throw_exception(void *value, except_type_T type, char *cmdname)
excp->throw_lnum = SOURCING_LNUM;
}
+ excp->stacktrace = stacktrace_create();
+ tv_list_ref(excp->stacktrace);
+
if (p_verbose >= 13 || debug_break_level > 0) {
int save_msg_silent = msg_silent;
@@ -563,6 +566,7 @@ static void discard_exception(except_T *excp, bool was_finished)
free_msglist(excp->messages);
}
xfree(excp->throw_name);
+ tv_list_unref(excp->stacktrace);
xfree(excp);
}
@@ -584,6 +588,7 @@ static void catch_exception(except_T *excp)
excp->caught = caught_stack;
caught_stack = excp;
set_vim_var_string(VV_EXCEPTION, excp->value, -1);
+ set_vim_var_list(VV_STACKTRACE, excp->stacktrace);
if (*excp->throw_name != NUL) {
if (excp->throw_lnum != 0) {
vim_snprintf(IObuff, IOSIZE, _("%s, line %" PRId64),
@@ -633,6 +638,7 @@ static void finish_exception(except_T *excp)
caught_stack = caught_stack->caught;
if (caught_stack != NULL) {
set_vim_var_string(VV_EXCEPTION, caught_stack->value, -1);
+ set_vim_var_list(VV_STACKTRACE, caught_stack->stacktrace);
if (*caught_stack->throw_name != NUL) {
if (caught_stack->throw_lnum != 0) {
vim_snprintf(IObuff, IOSIZE,
@@ -651,6 +657,7 @@ static void finish_exception(except_T *excp)
} else {
set_vim_var_string(VV_EXCEPTION, NULL, -1);
set_vim_var_string(VV_THROWPOINT, NULL, -1);
+ set_vim_var_list(VV_STACKTRACE, NULL);
}
// Discard the exception, but use the finish message for 'verbose'.
diff --git a/src/nvim/ex_eval_defs.h b/src/nvim/ex_eval_defs.h
index 3f5e510a20..e0d06f3e93 100644
--- a/src/nvim/ex_eval_defs.h
+++ b/src/nvim/ex_eval_defs.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
+#include "nvim/eval/typval_defs.h"
#include "nvim/pos_defs.h"
/// A list used for saving values of "emsg_silent". Used by ex_try() to save the
@@ -107,6 +108,7 @@ struct vim_exception {
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
+ list_T *stacktrace; ///< stacktrace
except_T *caught; ///< next exception on the caught stack
};
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index ace62ea729..fc20748309 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -14,7 +14,6 @@
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
-#include "nvim/arabic.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -43,7 +42,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/keycodes.h"
@@ -64,7 +62,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
@@ -123,7 +120,7 @@ typedef struct {
int indent;
int c;
bool gotesc; // true when <ESC> just typed
- int do_abbr; // when true check for abbr.
+ bool do_abbr; // when true check for abbr.
char *lookfor; // string to match
int lookforlen;
int hiscnt; // current history line in use
@@ -131,17 +128,17 @@ typedef struct {
// to jump to next match
int histype; // history type to be used
incsearch_state_T is_state;
- int did_wild_list; // did wild_list() recently
+ bool did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
int save_msg_scroll;
int save_State; // remember State when called
int prev_cmdpos;
char *save_p_icm;
- int some_key_typed; // one of the keys was typed
+ bool some_key_typed; // one of the keys was typed
// mouse drag and release events are ignored, unless they are
// preceded with a mouse down event
- int ignore_drag_release;
- int break_ctrl_c;
+ bool ignore_drag_release;
+ bool break_ctrl_c;
expand_T xpc;
OptInt *b_im_ptr;
buf_T *b_im_ptr_buf; ///< buffer where b_im_ptr is valid
@@ -166,6 +163,7 @@ typedef struct {
typedef struct {
buf_T *buf;
OptInt save_b_p_ul;
+ int save_b_p_ma;
int save_b_changed;
pos_T save_b_op_start;
pos_T save_b_op_end;
@@ -403,7 +401,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s
parse_cmd_address(&ea, &dummy, true);
if (ea.addr_count > 0) {
// Allow for reverse match.
- search_first_line = MIN(ea.line1, ea.line1);
+ search_first_line = MIN(ea.line2, ea.line1);
search_last_line = MAX(ea.line2, ea.line1);
} else if (cmd[0] == 's' && cmd[1] != 'o') {
// :s defaults to the current line
@@ -681,6 +679,15 @@ static void init_ccline(int firstc, int indent)
}
}
+static void ui_ext_cmdline_hide(bool abort)
+{
+ if (ui_has(kUICmdline)) {
+ cmdline_was_last_drawn = false;
+ ccline.redraw_state = kCmdRedrawNone;
+ ui_call_cmdline_hide(ccline.level, abort);
+ }
+}
+
/// Internal entry point for cmdline mode.
///
/// @param count only used for incremental search
@@ -787,9 +794,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
setmouse();
setcursor();
- TryState tstate;
Error err = ERROR_INIT;
- bool tl_ret = true;
char firstcbuf[2];
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
firstcbuf[1] = 0;
@@ -802,20 +807,19 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
tv_dict_set_keys_readonly(dict);
- try_enter(&tstate);
-
- apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf);
- restore_v_event(dict, &save_v_event);
+ TRY_WRAP(&err, {
+ apply_autocmds(EVENT_CMDLINEENTER, firstcbuf, firstcbuf, false, curbuf);
+ restore_v_event(dict, &save_v_event);
+ });
- tl_ret = try_leave(&tstate, &err);
- if (!tl_ret && ERROR_SET(&err)) {
+ if (ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
msg_puts_hl(err.msg, HLF_E, true);
api_clear_error(&err);
redrawcmd();
}
- tl_ret = true;
+ err = ERROR_INIT;
}
may_trigger_modechanged();
@@ -847,6 +851,10 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
found_one = true;
}
+ if (redraw_custom_title_later()) {
+ found_one = true;
+ }
+
if (found_one) {
redraw_statuslines();
}
@@ -869,10 +877,10 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
// not readonly:
tv_dict_add_bool(dict, S_LEN("abort"),
s->gotesc ? kBoolVarTrue : kBoolVarFalse);
- try_enter(&tstate);
- apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf);
- // error printed below, to avoid redraw issues
- tl_ret = try_leave(&tstate, &err);
+ TRY_WRAP(&err, {
+ apply_autocmds(EVENT_CMDLINELEAVE, firstcbuf, firstcbuf, false, curbuf);
+ // error printed below, to avoid redraw issues
+ });
if (tv_dict_get_number(dict, "abort") != 0) {
s->gotesc = true;
}
@@ -925,7 +933,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
msg_scroll = s->save_msg_scroll;
redir_off = false;
- if (!tl_ret && ERROR_SET(&err)) {
+ if (ERROR_SET(&err)) {
msg_putchar('\n');
emsg(err.msg);
did_emsg = false;
@@ -933,7 +941,7 @@ static uint8_t *command_line_enter(int firstc, int count, int indent, bool clear
}
// When the command line was typed, no need for a wait-return prompt.
- if (s->some_key_typed && tl_ret) {
+ if (s->some_key_typed && !ERROR_SET(&err)) {
need_wait_return = false;
}
@@ -955,10 +963,11 @@ theend:
char *p = ccline.cmdbuff;
if (ui_has(kUICmdline)) {
- ui_call_cmdline_hide(ccline.level);
+ ui_ext_cmdline_hide(s->gotesc);
msg_ext_clear_later();
}
if (!cmd_silent) {
+ redraw_custom_title_later();
status_redraw_all(); // redraw to show mode change
}
@@ -1070,23 +1079,23 @@ static int command_line_wildchar_complete(CommandLineState *s)
{
int res;
int options = WILD_NO_BEEP;
- if (wim_flags[s->wim_index] & WIM_BUFLASTUSED) {
+ if (wim_flags[s->wim_index] & kOptWimFlagLastused) {
options |= WILD_BUFLASTUSED;
}
if (s->xpc.xp_numfiles > 0) { // typed p_wc at least twice
// if 'wildmode' contains "list" may still need to list
if (s->xpc.xp_numfiles > 1
&& !s->did_wild_list
- && ((wim_flags[s->wim_index] & WIM_LIST)
- || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0))) {
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ && ((wim_flags[s->wim_index] & kOptWimFlagList)
+ || (p_wmnu && (wim_flags[s->wim_index] & kOptWimFlagFull) != 0))) {
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
redrawcmd();
s->did_wild_list = true;
}
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ } else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
res = nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
} else {
res = OK; // don't insert 'wildchar' now
@@ -1097,7 +1106,7 @@ static int command_line_wildchar_complete(CommandLineState *s)
// if 'wildmode' first contains "longest", get longest
// common part
- if (wim_flags[0] & WIM_LONGEST) {
+ if (wim_flags[0] & kOptWimFlagLongest) {
res = nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
} else {
res = nextwild(&s->xpc, WILD_EXPAND_KEEP, options, s->firstc != '@');
@@ -1118,12 +1127,12 @@ static int command_line_wildchar_complete(CommandLineState *s)
if (res == OK && s->xpc.xp_numfiles > 1) {
// a "longest" that didn't do anything is skipped (but not
// "list:longest")
- if (wim_flags[0] == WIM_LONGEST && ccline.cmdpos == j) {
+ if (wim_flags[0] == kOptWimFlagLongest && ccline.cmdpos == j) {
s->wim_index = 1;
}
- if ((wim_flags[s->wim_index] & WIM_LIST)
- || (p_wmnu && (wim_flags[s->wim_index] & WIM_FULL) != 0)) {
- if (!(wim_flags[0] & WIM_LONGEST)) {
+ if ((wim_flags[s->wim_index] & kOptWimFlagList)
+ || (p_wmnu && (wim_flags[s->wim_index] & kOptWimFlagFull) != 0)) {
+ if (!(wim_flags[0] & kOptWimFlagLongest)) {
int p_wmnu_save = p_wmnu;
p_wmnu = 0;
// remove match
@@ -1131,17 +1140,17 @@ static int command_line_wildchar_complete(CommandLineState *s)
p_wmnu = p_wmnu_save;
}
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
redrawcmd();
s->did_wild_list = true;
- if (wim_flags[s->wim_index] & WIM_LONGEST) {
+ if (wim_flags[s->wim_index] & kOptWimFlagLongest) {
nextwild(&s->xpc, WILD_LONGEST, options, s->firstc != '@');
- } else if (wim_flags[s->wim_index] & WIM_FULL) {
+ } else if (wim_flags[s->wim_index] & kOptWimFlagFull) {
nextwild(&s->xpc, WILD_NEXT, options, s->firstc != '@');
}
} else {
- vim_beep(BO_WILD);
+ vim_beep(kOptBoFlagWildmode);
}
} else if (s->xpc.xp_numfiles == -1) {
s->xpc.xp_context = EXPAND_NOTHING;
@@ -1380,9 +1389,9 @@ static int command_line_execute(VimState *state, int key)
if (s->c == K_S_TAB && KeyTyped) {
if (nextwild(&s->xpc, WILD_EXPAND_KEEP, 0, s->firstc != '@') == OK) {
if (s->xpc.xp_numfiles > 1
- && ((!s->did_wild_list && (wim_flags[s->wim_index] & WIM_LIST)) || p_wmnu)) {
+ && ((!s->did_wild_list && (wim_flags[s->wim_index] & kOptWimFlagList)) || p_wmnu)) {
// Trigger the popup menu when wildoptions=pum
- showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & WIM_LIST) == 0));
+ showmatches(&s->xpc, p_wmnu && ((wim_flags[s->wim_index] & kOptWimFlagList) == 0));
}
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
nextwild(&s->xpc, WILD_PREV, 0, s->firstc != '@');
@@ -1511,7 +1520,7 @@ static int may_do_command_line_next_incsearch(int firstc, int count, incsearch_s
redrawcmdline();
curwin->w_cursor = s->match_end;
} else {
- vim_beep(BO_ERROR);
+ vim_beep(kOptBoFlagError);
}
restore_last_search_pattern();
return FAIL;
@@ -1849,6 +1858,12 @@ static int command_line_browse_history(CommandLineState *s)
static int command_line_handle_key(CommandLineState *s)
{
+ // For one key prompt, avoid putting ESC and Ctrl_C onto cmdline.
+ // For all other keys, just put onto cmdline and exit.
+ if (ccline.one_key && s->c != ESC && s->c != Ctrl_C) {
+ goto end;
+ }
+
// Big switch for a typed command line character.
switch (s->c) {
case K_BS:
@@ -1999,6 +2014,12 @@ static int command_line_handle_key(CommandLineState *s)
}
FALLTHROUGH;
case K_LEFTMOUSE:
+ // Return on left click above number prompt
+ if (ccline.mouse_used && mouse_row < cmdline_row) {
+ *ccline.mouse_used = true;
+ return 0;
+ }
+ FALLTHROUGH;
case K_RIGHTMOUSE:
command_line_left_right_mouse(s);
return command_line_not_changed(s);
@@ -2156,6 +2177,14 @@ static int command_line_handle_key(CommandLineState *s)
}
return command_line_not_changed(s);
+ case 'q':
+ // Number prompts use the mouse and return on 'q' press
+ if (ccline.mouse_used) {
+ *ccline.cmdbuff = NUL;
+ return 0;
+ }
+ FALLTHROUGH;
+
default:
// Normal character with no special meaning. Just set mod_mask
// to 0x0 so that typing Shift-Space in the GUI doesn't enter
@@ -2176,6 +2205,7 @@ static int command_line_handle_key(CommandLineState *s)
return command_line_changed(s);
}
+end:
// put the character in the command line
if (IS_SPECIAL(s->c) || mod_mask != 0) {
put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
@@ -2184,17 +2214,22 @@ static int command_line_handle_key(CommandLineState *s)
IObuff[j] = NUL; // exclude composing chars
put_on_cmdline(IObuff, j, true);
}
- return command_line_changed(s);
+ return ccline.one_key ? 0 : command_line_changed(s);
}
-static int command_line_not_changed(CommandLineState *s)
+/// Trigger CursorMovedC autocommands.
+static void may_trigger_cursormovedc(CommandLineState *s)
{
- // Trigger CursorMovedC autocommands.
if (ccline.cmdpos != s->prev_cmdpos) {
trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC);
s->prev_cmdpos = ccline.cmdpos;
+ ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos);
}
+}
+static int command_line_not_changed(CommandLineState *s)
+{
+ may_trigger_cursormovedc(s);
// Incremental searches for "/" and "?":
// Enter command_line_not_changed() when a character has been read but the
// command line did not change. Then we only search and redraw if something
@@ -2310,11 +2345,13 @@ static win_T *cmdpreview_open_win(buf_T *cmdpreview_buf)
win_T *preview_win = curwin;
Error err = ERROR_INIT;
+ int result = OK;
// Switch to preview buffer
- try_start();
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0);
- if (try_end(&err) || result == FAIL) {
+ TRY_WRAP(&err, {
+ result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, cmdpreview_buf->handle, 0);
+ });
+ if (ERROR_SET(&err) || result == FAIL) {
api_clear_error(&err);
return NULL;
}
@@ -2396,6 +2433,7 @@ static void cmdpreview_prepare(CpInfo *cpinfo)
if (!set_has(ptr_t, &saved_bufs, buf)) {
CpBufInfo cp_bufinfo;
cp_bufinfo.buf = buf;
+ cp_bufinfo.save_b_p_ma = buf->b_p_ma;
cp_bufinfo.save_b_p_ul = buf->b_p_ul;
cp_bufinfo.save_b_changed = buf->b_changed;
cp_bufinfo.save_b_op_start = buf->b_op_start;
@@ -2486,6 +2524,7 @@ static void cmdpreview_restore_state(CpInfo *cpinfo)
}
buf->b_p_ul = cp_bufinfo.save_b_p_ul; // Restore 'undolevels'
+ buf->b_p_ma = cp_bufinfo.save_b_p_ma; // Restore 'modifiable'
}
for (size_t i = 0; i < cpinfo->win_info.size; i++) {
@@ -2554,7 +2593,7 @@ static bool cmdpreview_may_show(CommandLineState *s)
// Place it there in case preview callback flushes it. #30696
cursorcmd();
// Flush now: external cmdline may itself wish to update the screen which is
- // currently disallowed during cmdpreview(no longer needed in case that changes).
+ // currently disallowed during cmdpreview (no longer needed in case that changes).
cmdline_ui_flush();
// Swap invalid command range if needed
@@ -2595,9 +2634,10 @@ static bool cmdpreview_may_show(CommandLineState *s)
// open the preview window. The preview callback also handles doing the changes and highlights for
// the preview.
Error err = ERROR_INIT;
- try_start();
- cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
- if (try_end(&err)) {
+ TRY_WRAP(&err, {
+ cmdpreview_type = execute_cmd(&ea, &cmdinfo, true);
+ });
+ if (ERROR_SET(&err)) {
api_clear_error(&err);
cmdpreview_type = 0;
}
@@ -2638,7 +2678,6 @@ end:
static void do_autocmd_cmdlinechanged(int firstc)
{
if (has_event(EVENT_CMDLINECHANGED)) {
- TryState tstate;
Error err = ERROR_INIT;
save_v_event_T save_v_event;
dict_T *dict = get_v_event(&save_v_event);
@@ -2651,13 +2690,11 @@ static void do_autocmd_cmdlinechanged(int firstc)
tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
tv_dict_set_keys_readonly(dict);
- try_enter(&tstate);
-
- apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf);
- restore_v_event(dict, &save_v_event);
-
- bool tl_ret = try_leave(&tstate, &err);
- if (!tl_ret && ERROR_SET(&err)) {
+ TRY_WRAP(&err, {
+ apply_autocmds(EVENT_CMDLINECHANGED, firstcbuf, firstcbuf, false, curbuf);
+ restore_v_event(dict, &save_v_event);
+ });
+ if (ERROR_SET(&err)) {
msg_putchar('\n');
msg_scroll = true;
msg_puts_hl(err.msg, HLF_E, true);
@@ -2672,18 +2709,13 @@ static int command_line_changed(CommandLineState *s)
// Trigger CmdlineChanged autocommands.
do_autocmd_cmdlinechanged(s->firstc > 0 ? s->firstc : '-');
- // Trigger CursorMovedC autocommands.
- if (ccline.cmdpos != s->prev_cmdpos) {
- trigger_cmd_autocmd(get_cmdline_type(), EVENT_CURSORMOVEDC);
- s->prev_cmdpos = ccline.cmdpos;
- }
+ may_trigger_cursormovedc(s);
const bool prev_cmdpreview = cmdpreview;
if (s->firstc == ':'
&& current_sctx.sc_sid == 0 // only if interactive
&& *p_icm != NUL // 'inccommand' is set
&& !exmode_active // not in ex mode
- && curbuf->b_p_ma // buffer is modifiable
&& cmdline_star == 0 // not typing a password
&& !vpeekc_any()
&& cmdpreview_may_show(s)) {
@@ -2718,12 +2750,14 @@ static int command_line_changed(CommandLineState *s)
static void abandon_cmdline(void)
{
dealloc_cmdbuff();
- ccline.redraw_state = kCmdRedrawNone;
if (msg_scrolled == 0) {
compute_cmdrow();
}
- msg("", 0);
- redraw_cmdline = true;
+ // Avoid overwriting key prompt
+ if (!ccline.one_key) {
+ msg("", 0);
+ redraw_cmdline = true;
+ }
}
/// getcmdline() - accept a command line starting with firstc.
@@ -2762,11 +2796,13 @@ char *getcmdline(int firstc, int count, int indent, bool do_concat FUNC_ATTR_UNU
/// @param[in] xp_context Type of expansion.
/// @param[in] xp_arg User-defined expansion argument.
/// @param[in] highlight_callback Callback used for highlighting user input.
+/// @param[in] one_key Return after one key press for button prompt.
+/// @param[in] mouse_used Set to true when returning after right mouse click.
///
/// @return [allocated] Command line or NULL.
char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl_id,
const int xp_context, const char *const xp_arg,
- const Callback highlight_callback)
+ const Callback highlight_callback, bool one_key, bool *mouse_used)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
{
const int msg_col_save = msg_col;
@@ -2787,16 +2823,22 @@ char *getcmdline_prompt(const int firstc, const char *const prompt, const int hl
ccline.xp_arg = (char *)xp_arg;
ccline.input_fn = (firstc == '@');
ccline.highlight_callback = highlight_callback;
+ ccline.one_key = one_key;
+ ccline.mouse_used = mouse_used;
+ const bool cmd_silent_saved = cmd_silent;
int msg_silent_saved = msg_silent;
msg_silent = 0;
+ cmd_silent = false; // Want to see the prompt.
char *const ret = (char *)command_line_enter(firstc, 1, 0, false);
+ ccline.redraw_state = kCmdRedrawNone;
if (did_save_ccline) {
restore_cmdline(&save_ccline);
}
msg_silent = msg_silent_saved;
+ cmd_silent = cmd_silent_saved;
// Restore msg_col, the prompt from input() may have changed it.
// But only if called recursively and the commandline is therefore being
// restored to an old one; if not, the input() prompt stays on the screen,
@@ -2820,19 +2862,19 @@ int check_opt_wim(void)
}
for (char *p = p_wim; *p; p++) {
- // Note: Keep this in sync with p_wim_values.
+ // Note: Keep this in sync with opt_wim_values.
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;
+ new_wim_flags[idx] |= kOptWimFlagLongest;
} else if (i == 4 && strncmp(p, "full", 4) == 0) {
- new_wim_flags[idx] |= WIM_FULL;
+ new_wim_flags[idx] |= kOptWimFlagFull;
} else if (i == 4 && strncmp(p, "list", 4) == 0) {
- new_wim_flags[idx] |= WIM_LIST;
+ new_wim_flags[idx] |= kOptWimFlagList;
} else if (i == 8 && strncmp(p, "lastused", 8) == 0) {
- new_wim_flags[idx] |= WIM_BUFLASTUSED;
+ new_wim_flags[idx] |= kOptWimFlagLastused;
} else {
return FAIL;
}
@@ -3140,8 +3182,9 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
#define PRINT_ERRMSG(...) \
do { \
+ msg_scroll = true; \
msg_putchar('\n'); \
- msg_printf_hl(HLF_E, __VA_ARGS__); \
+ smsg(HLF_E, __VA_ARGS__); \
printed_errmsg = true; \
} while (0)
bool ret = true;
@@ -3174,11 +3217,9 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
static int prev_prompt_errors = 0;
Callback color_cb = CALLBACK_NONE;
bool can_free_cb = false;
- TryState tstate;
Error err = ERROR_INIT;
const char *err_errmsg = e_intern2;
bool dgc_ret = true;
- bool tl_ret = true;
if (colored_ccline->prompt_id != prev_prompt_id) {
prev_prompt_errors = 0;
@@ -3191,16 +3232,16 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
assert(colored_ccline->input_fn);
color_cb = colored_ccline->highlight_callback;
} else if (colored_ccline->cmdfirstc == ':') {
- try_enter(&tstate);
- err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s");
- dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
- &color_cb);
- tl_ret = try_leave(&tstate, &err);
+ TRY_WRAP(&err, {
+ err_errmsg = N_("E5408: Unable to get g:Nvim_color_cmdline callback: %s");
+ dgc_ret = tv_dict_get_callback(&globvardict, S_LEN("Nvim_color_cmdline"),
+ &color_cb);
+ });
can_free_cb = true;
} else if (colored_ccline->cmdfirstc == '=') {
color_expr_cmdline(colored_ccline, ccline_colors);
}
- if (!tl_ret || !dgc_ret) {
+ if (ERROR_SET(&err) || !dgc_ret) {
goto color_cmdline_error;
}
@@ -3221,20 +3262,22 @@ static bool color_cmdline(CmdlineInfo *colored_ccline)
// correct, with msg_col it just misses leading `:`. Since `redraw!` in
// callback lags this is least of the user problems.
//
- // Also using try_enter() because error messages may overwrite typed
+ // Also using TRY_WRAP because error messages may overwrite typed
// command-line which is not expected.
getln_interrupted_highlight = false;
- try_enter(&tstate);
- err_errmsg = N_("E5407: Callback has thrown an exception: %s");
- const int saved_msg_col = msg_col;
- msg_silent++;
- const bool cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
- msg_silent--;
- msg_col = saved_msg_col;
- if (got_int) {
- getln_interrupted_highlight = true;
- }
- if (!try_leave(&tstate, &err) || !cbcall_ret) {
+ bool cbcall_ret = true;
+ TRY_WRAP(&err, {
+ err_errmsg = N_("E5407: Callback has thrown an exception: %s");
+ const int saved_msg_col = msg_col;
+ msg_silent++;
+ cbcall_ret = callback_call(&color_cb, 1, &arg, &tv);
+ msg_silent--;
+ msg_col = saved_msg_col;
+ if (got_int) {
+ getln_interrupted_highlight = true;
+ }
+ });
+ if (ERROR_SET(&err) || !cbcall_ret) {
goto color_cmdline_error;
}
if (tv.v_type != VAR_LIST) {
@@ -3350,7 +3393,7 @@ color_cmdline_error:
// when cmdline_star is true.
static void draw_cmdline(int start, int len)
{
- if (!color_cmdline(&ccline)) {
+ if (ccline.cmdbuff == NULL || !color_cmdline(&ccline)) {
return;
}
@@ -3420,8 +3463,7 @@ static void ui_ext_cmdline_show(CmdlineInfo *line)
ui_call_cmdline_show(content, line->cmdpos,
cstr_as_string(charbuf),
cstr_as_string((line->cmdprompt)),
- line->cmdindent,
- line->level);
+ line->cmdindent, line->level, line->hl_id);
if (line->special_char) {
charbuf[0] = line->special_char;
ui_call_cmdline_special_char(cstr_as_string(charbuf),
@@ -3457,8 +3499,7 @@ void ui_ext_cmdline_block_leave(void)
ui_call_cmdline_block_hide();
}
-/// Extra redrawing needed for redraw! and on ui_attach
-/// assumes "redrawcmdline()" will already be invoked
+/// Extra redrawing needed for redraw! and on ui_attach.
void cmdline_screen_cleared(void)
{
if (!ui_has(kUICmdline)) {
@@ -3481,6 +3522,7 @@ void cmdline_screen_cleared(void)
}
line = line->prev_ccline;
}
+ redrawcmd();
}
/// called by ui_flush, do what redraws necessary to keep cmdline updated.
@@ -3493,12 +3535,14 @@ void cmdline_ui_flush(void)
CmdlineInfo *line = &ccline;
while (level > 0 && line) {
if (line->level == level) {
- if (line->redraw_state == kCmdRedrawAll) {
+ CmdRedraw redraw_state = line->redraw_state;
+ line->redraw_state = kCmdRedrawNone;
+ if (redraw_state == kCmdRedrawAll) {
+ cmdline_was_last_drawn = true;
ui_ext_cmdline_show(line);
- } else if (line->redraw_state == kCmdRedrawPos) {
+ } else if (redraw_state == kCmdRedrawPos && cmdline_was_last_drawn) {
ui_call_cmdline_pos(line->cmdpos, line->level);
}
- line->redraw_state = kCmdRedrawNone;
level--;
}
line = line->prev_ccline;
@@ -3866,12 +3910,7 @@ void compute_cmdrow(void)
void cursorcmd(void)
{
- if (cmd_silent) {
- return;
- }
-
- if (ui_has(kUICmdline)) {
- ccline.redraw_state = MAX(ccline.redraw_state, kCmdRedrawPos);
+ if (cmd_silent || ui_has(kUICmdline)) {
return;
}
@@ -4473,10 +4512,7 @@ static int open_cmdwin(void)
curwin->w_cursor.col = ccline.cmdpos;
changed_line_abv_curs();
invalidate_botline(curwin);
- if (ui_has(kUICmdline)) {
- ccline.redraw_state = kCmdRedrawNone;
- ui_call_cmdline_hide(ccline.level);
- }
+ ui_ext_cmdline_hide(false);
redraw_later(curwin, UPD_SOME_VALID);
// No Ex mode here!
@@ -4764,9 +4800,6 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
}
}
- const bool cmd_silent_save = cmd_silent;
-
- cmd_silent = false; // Want to see the prompt.
// Only the part of the message after the last NL is considered as
// prompt for the command line, unlsess cmdline is externalized
const char *p = prompt;
@@ -4788,7 +4821,7 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
const int save_ex_normal_busy = ex_normal_busy;
ex_normal_busy = 0;
rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_hl_id(),
- xp_type, xp_arg, input_callback);
+ xp_type, xp_arg, input_callback, false, NULL);
ex_normal_busy = save_ex_normal_busy;
callback_free(&input_callback);
@@ -4801,5 +4834,4 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const
// Since the user typed this, no need to wait for return.
need_wait_return = false;
msg_didout = false;
- cmd_silent = cmd_silent_save;
}
diff --git a/src/nvim/ex_getln_defs.h b/src/nvim/ex_getln_defs.h
index 584c360450..e05d8f27db 100644
--- a/src/nvim/ex_getln_defs.h
+++ b/src/nvim/ex_getln_defs.h
@@ -65,4 +65,6 @@ struct cmdline_info {
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
+ bool one_key; ///< return after one key press for button prompt
+ bool *mouse_used; ///< mouse clicked in prompt
};
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index 50ee197ef4..ab7585e90a 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -9,10 +9,12 @@
#include <stdio.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/arglist.h"
#include "nvim/arglist_defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/errors.h"
@@ -71,7 +73,7 @@ static int put_view_curpos(FILE *fd, const win_T *wp, char *spaces)
static int ses_winsizes(FILE *fd, bool restore_size, win_T *tab_firstwin)
{
- if (restore_size && (ssop_flags & SSOP_WINSIZE)) {
+ if (restore_size && (ssop_flags & kOptSsopFlagWinsize)) {
int n = 0;
for (win_T *wp = tab_firstwin; wp != NULL; wp = wp->w_next) {
if (!ses_do_win(wp)) {
@@ -198,13 +200,13 @@ static int ses_do_win(win_T *wp)
if (wp->w_buffer->b_fname == NULL
// When 'buftype' is "nofile" can't restore the window contents.
|| (!wp->w_buffer->terminal && bt_nofilename(wp->w_buffer))) {
- return ssop_flags & SSOP_BLANK;
+ return ssop_flags & kOptSsopFlagBlank;
}
if (bt_help(wp->w_buffer)) {
- return ssop_flags & SSOP_HELP;
+ return ssop_flags & kOptSsopFlagHelp;
}
if (bt_terminal(wp->w_buffer)) {
- return ssop_flags & SSOP_TERMINAL;
+ return ssop_flags & kOptSsopFlagTerminal;
}
return true;
}
@@ -257,7 +259,7 @@ static char *ses_get_fname(buf_T *buf, const unsigned *flagp)
// directory is.
if (buf->b_sfname != NULL
&& flagp == &ssop_flags
- && (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR))
+ && (ssop_flags & (kOptSsopFlagCurdir | kOptSsopFlagSesdir))
&& !p_acd
&& !did_lcd) {
return buf->b_sfname;
@@ -289,7 +291,7 @@ static char *ses_escape_fname(char *name, unsigned *flagp)
char *p;
char *sname = home_replace_save(NULL, name);
- // Always SSOP_SLASH: change all backslashes to forward slashes.
+ // Always kOptSsopFlagSlash: change all backslashes to forward slashes.
for (p = sname; *p != NUL; MB_PTR_ADV(p)) {
if (*p == '\\') {
*p = '/';
@@ -328,7 +330,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// Always restore cursor position for ":mksession". For ":mkview" only
// when 'viewoptions' contains "cursor".
- bool do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR);
+ bool do_cursor = (flagp == &ssop_flags || *flagp & kOptSsopFlagCursor);
// Local argument list.
if (wp->w_alist == &global_alist) {
@@ -336,7 +338,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
} else {
if (ses_arglist(fd, "arglocal", &wp->w_alist->al_ga,
flagp == &vop_flags
- || !(*flagp & SSOP_CURDIR)
+ || !(*flagp & kOptSsopFlagCurdir)
|| wp->w_localdir != NULL, flagp) == FAIL) {
return FAIL;
}
@@ -417,7 +419,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
&& *alt->b_fname != NUL
&& alt->b_p_bl
// do not set balt if buffer is terminal and "terminal" is not set in options
- && !(bt_terminal(alt) && !(ssop_flags & SSOP_TERMINAL))
+ && !(bt_terminal(alt) && !(ssop_flags & kOptSsopFlagTerminal))
&& (fputs("balt ", fd) < 0
|| ses_fname(fd, alt, flagp, true) == FAIL)) {
return FAIL;
@@ -425,7 +427,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
}
// Local mappings and abbreviations.
- if ((*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS))
+ if ((*flagp & (kOptSsopFlagOptions | kOptSsopFlagLocaloptions))
&& makemap(fd, wp->w_buffer) == FAIL) {
return FAIL;
}
@@ -438,10 +440,10 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
win_T *save_curwin = curwin;
curwin = wp;
curbuf = curwin->w_buffer;
- if (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) {
+ if (*flagp & (kOptSsopFlagOptions | kOptSsopFlagLocaloptions)) {
f = makeset(fd, OPT_LOCAL,
- flagp == &vop_flags || !(*flagp & SSOP_OPTIONS));
- } else if (*flagp & SSOP_FOLDS) {
+ flagp == &vop_flags || !(*flagp & kOptSsopFlagOptions));
+ } else if (*flagp & kOptSsopFlagFolds) {
f = makefoldset(fd);
} else {
f = OK;
@@ -453,7 +455,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
}
// Save Folds when 'buftype' is empty and for help files.
- if ((*flagp & SSOP_FOLDS)
+ if ((*flagp & kOptSsopFlagFolds)
&& wp->w_buffer->b_ffname != NULL
&& (bt_normal(wp->w_buffer)
|| bt_help(wp->w_buffer))) {
@@ -516,7 +518,7 @@ static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int curr
// Local directory, if the current flag is not view options or the "curdir"
// option is included.
if (wp->w_localdir != NULL
- && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) {
+ && (flagp != &vop_flags || (*flagp & kOptSsopFlagCurdir))) {
if (fputs("lcd ", fd) < 0
|| ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
|| fprintf(fd, "\n") < 0) {
@@ -574,7 +576,7 @@ static int store_session_globals(FILE *fd)
/// Writes commands for restoring the current buffers, for :mksession.
///
-/// Legacy 'sessionoptions'/'viewoptions' flags SSOP_UNIX, SSOP_SLASH are
+/// Legacy 'sessionoptions'/'viewoptions' flags kOptSsopFlagUnix, kOptSsopFlagSlash are
/// always enabled.
///
/// @param dirnow Current directory name
@@ -591,13 +593,13 @@ static int makeopens(FILE *fd, char *dirnow)
int cur_arg_idx = 0;
int next_arg_idx = 0;
- if (ssop_flags & SSOP_BUFFERS) {
+ if (ssop_flags & kOptSsopFlagBuffers) {
only_save_windows = false; // Save ALL buffers
}
// Begin by setting v:this_session, and then other sessionable variables.
PUTLINE_FAIL("let v:this_session=expand(\"<sfile>:p\")");
- if (ssop_flags & SSOP_GLOBALS) {
+ if (ssop_flags & kOptSsopFlagGlobals) {
if (store_session_globals(fd) == FAIL) {
return FAIL;
}
@@ -605,15 +607,15 @@ static int makeopens(FILE *fd, char *dirnow)
// Close all windows and tabs but one.
PUTLINE_FAIL("silent only");
- if ((ssop_flags & SSOP_TABPAGES)
+ if ((ssop_flags & kOptSsopFlagTabpages)
&& put_line(fd, "silent tabonly") == FAIL) {
return FAIL;
}
// Now a :cd command to the session directory or the current directory
- if (ssop_flags & SSOP_SESDIR) {
+ if (ssop_flags & kOptSsopFlagSesdir) {
PUTLINE_FAIL("exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')");
- } else if (ssop_flags & SSOP_CURDIR) {
+ } else if (ssop_flags & kOptSsopFlagCurdir) {
char *sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow);
char *fname_esc = ses_escape_fname(sname, &ssop_flags);
if (fprintf(fd, "cd %s\n", fname_esc) < 0) {
@@ -637,7 +639,7 @@ static int makeopens(FILE *fd, char *dirnow)
}
// save 'shortmess' if not storing options
- if ((ssop_flags & SSOP_OPTIONS) == 0) {
+ if ((ssop_flags & kOptSsopFlagOptions) == 0) {
PUTLINE_FAIL("let s:shortmess_save = &shortmess");
}
@@ -654,14 +656,13 @@ static int makeopens(FILE *fd, char *dirnow)
// can be disrupted by prior `edit` or `tabedit` calls).
FOR_ALL_BUFFERS(buf) {
if (!(only_save_windows && buf->b_nwindows == 0)
- && !(buf->b_help && !(ssop_flags & SSOP_HELP))
- && !(bt_terminal(buf) && !(ssop_flags & SSOP_TERMINAL))
+ && !(buf->b_help && !(ssop_flags & kOptSsopFlagHelp))
+ && !(bt_terminal(buf) && !(ssop_flags & kOptSsopFlagTerminal))
&& buf->b_fname != NULL
&& buf->b_p_bl) {
if (fprintf(fd, "badd +%" PRId64 " ",
- buf->b_wininfo == NULL
- ? 1
- : (int64_t)buf->b_wininfo->wi_mark.mark.lnum) < 0
+ kv_size(buf->b_wininfo) == 0
+ ? 1 : (int64_t)kv_A(buf->b_wininfo, 0)->wi_mark.mark.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags, true) == FAIL) {
return FAIL;
}
@@ -670,11 +671,11 @@ static int makeopens(FILE *fd, char *dirnow)
// the global argument list
if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
- !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) {
+ !(ssop_flags & kOptSsopFlagCurdir), &ssop_flags) == FAIL) {
return FAIL;
}
- if (ssop_flags & SSOP_RESIZE) {
+ if (ssop_flags & kOptSsopFlagResize) {
// Note: after the restore we still check it worked!
if (fprintf(fd, "set lines=%" PRId64 " columns=%" PRId64 "\n",
(int64_t)Rows, (int64_t)Columns) < 0) {
@@ -692,7 +693,7 @@ static int makeopens(FILE *fd, char *dirnow)
restore_stal = true;
}
- if ((ssop_flags & SSOP_TABPAGES)) {
+ if ((ssop_flags & kOptSsopFlagTabpages)) {
// "tabpages" is in 'sessionoptions': Similar to ses_win_rec() below,
// populate the tab pages first so later local options won't be copied
// to the new tabs.
@@ -720,7 +721,7 @@ static int makeopens(FILE *fd, char *dirnow)
// 'sessionoptions'.
// Don't use goto_tabpage(), it may change directory and trigger
// autocommands.
- if ((ssop_flags & SSOP_TABPAGES)) {
+ if ((ssop_flags & kOptSsopFlagTabpages)) {
if (tp == curtab) {
tab_firstwin = firstwin;
tab_topframe = topframe;
@@ -820,7 +821,7 @@ static int makeopens(FILE *fd, char *dirnow)
// Restore the tab-local working directory if specified
// Do this before the windows, so that the window-local directory can
// override the tab-local directory.
- if ((ssop_flags & SSOP_CURDIR) && tp->tp_localdir != NULL) {
+ if ((ssop_flags & kOptSsopFlagCurdir) && tp->tp_localdir != NULL) {
if (fputs("tcd ", fd) < 0
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| put_eol(fd) == FAIL) {
@@ -862,12 +863,12 @@ static int makeopens(FILE *fd, char *dirnow)
// Don't continue in another tab page when doing only the current one
// or when at the last tab page.
- if (!(ssop_flags & SSOP_TABPAGES)) {
+ if (!(ssop_flags & kOptSsopFlagTabpages)) {
break;
}
}
- if (ssop_flags & SSOP_TABPAGES) {
+ if (ssop_flags & kOptSsopFlagTabpages) {
if (fprintf(fd, "tabnext %d\n", tabpage_index(curtab)) < 0) {
return FAIL;
}
@@ -894,7 +895,7 @@ static int makeopens(FILE *fd, char *dirnow)
}
// Restore 'shortmess'.
- if (ssop_flags & SSOP_OPTIONS) {
+ if (ssop_flags & kOptSsopFlagOptions) {
if (fprintf(fd, "set shortmess=%s\n", p_shm) < 0) {
return FAIL;
}
@@ -937,8 +938,8 @@ void ex_loadview(exarg_T *eap)
/// ":mkexrc", ":mkvimrc", ":mkview", ":mksession".
///
/// Legacy 'sessionoptions'/'viewoptions' flags are always enabled:
-/// - SSOP_UNIX: line-endings are LF
-/// - SSOP_SLASH: filenames are written with "/" slash
+/// - kOptSsopFlagUnix: line-endings are LF
+/// - kOptSsopFlagSlash: filenames are written with "/" slash
void ex_mkrc(exarg_T *eap)
{
bool view_session = false; // :mkview, :mksession
@@ -1002,10 +1003,10 @@ void ex_mkrc(exarg_T *eap)
}
if (!view_session || (eap->cmdidx == CMD_mksession
- && (*flagp & SSOP_OPTIONS))) {
+ && (*flagp & kOptSsopFlagOptions))) {
int flags = OPT_GLOBAL;
- if (eap->cmdidx == CMD_mksession && (*flagp & SSOP_SKIP_RTP)) {
+ if (eap->cmdidx == CMD_mksession && (*flagp & kOptSsopFlagSkiprtp)) {
flags |= OPT_SKIPRTP;
}
failed |= (makemap(fd, NULL) == FAIL
@@ -1028,12 +1029,12 @@ void ex_mkrc(exarg_T *eap)
|| os_chdir(dirnow) != 0) {
*dirnow = NUL;
}
- if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) {
+ if (*dirnow != NUL && (ssop_flags & kOptSsopFlagSesdir)) {
if (vim_chdirfile(fname, kCdCauseOther) == OK) {
shorten_fnames(true);
}
} else if (*dirnow != NUL
- && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) {
+ && (ssop_flags & kOptSsopFlagCurdir) && globaldir != NULL) {
if (os_chdir(globaldir) == 0) {
shorten_fnames(true);
}
@@ -1042,8 +1043,8 @@ void ex_mkrc(exarg_T *eap)
failed |= (makeopens(fd, dirnow) == FAIL);
// restore original dir
- if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR)
- || ((ssop_flags & SSOP_CURDIR) && globaldir !=
+ if (*dirnow != NUL && ((ssop_flags & kOptSsopFlagSesdir)
+ || ((ssop_flags & kOptSsopFlagCurdir) && globaldir !=
NULL))) {
if (os_chdir(dirnow) != 0) {
emsg(_(e_prev_dir));
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index c20c7dea23..7e6dfb8dea 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -95,6 +95,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col
MTKey mark = { { row, col }, ns_id, id, flags, decor.data };
marktree_put(buf->b_marktree, mark, end_row, end_col, end_right_gravity);
+ decor_state_invalidate(buf);
revised:
if (decor_flags || decor.ext) {
@@ -111,10 +112,10 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
{
MarkTreeIter itr[1] = { 0 };
MTKey key = marktree_lookup(buf->b_marktree, mark, itr);
- bool move = key.pos.row >= 0 && (key.pos.row != row || key.pos.col != col);
- // Already valid keys were being revalidated, presumably when encountering a
- // SavePos from a modified mark. Avoid adding that to the decor again.
- invalid = invalid && mt_invalid(key);
+ bool move = key.pos.row != row || key.pos.col != col;
+ if (key.pos.row < 0 || (!move && !invalid)) {
+ return; // Mark was deleted or no change needed
+ }
// Only the position before undo needs to be redrawn here,
// as the position after undo should be marked as changed.
@@ -124,13 +125,16 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
int row1 = 0;
int row2 = 0;
+ MarkTreeIter altitr[1] = { *itr };
+ MTKey alt = marktree_get_alt(buf->b_marktree, key, altitr);
+
if (invalid) {
mt_itr_rawkey(itr).flags &= (uint16_t) ~MT_FLAG_INVALID;
- marktree_revise_meta(buf->b_marktree, itr, key);
- } else if (move && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
- MTPos end = marktree_get_altpos(buf->b_marktree, key, NULL);
- row1 = MIN(end.row, MIN(key.pos.row, row));
- row2 = MAX(end.row, MAX(key.pos.row, row));
+ mt_itr_rawkey(altitr).flags &= (uint16_t) ~MT_FLAG_INVALID;
+ marktree_revise_meta(buf->b_marktree, mt_end(key) ? altitr : itr, mt_end(key) ? alt : key);
+ } else if (!mt_invalid(key) && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
+ row1 = MIN(alt.pos.row, MIN(key.pos.row, row));
+ row2 = MAX(alt.pos.row, MAX(key.pos.row, row));
buf_signcols_count_range(buf, row1, row2, 0, kTrue);
}
@@ -139,9 +143,8 @@ static void extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col, bool
}
if (invalid) {
- row2 = mt_paired(key) ? marktree_get_altpos(buf->b_marktree, key, NULL).row : row;
- buf_put_decor(buf, mt_decor(key), row, row2);
- } else if (move && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
+ buf_put_decor(buf, mt_decor(key), MIN(row, key.pos.row), MAX(row, key.pos.row));
+ } else if (!mt_invalid(key) && key.flags & MT_FLAG_DECOR_SIGNTEXT && buf->b_signcols.autom) {
buf_signcols_count_range(buf, row1, row2, 0, kNone);
}
}
@@ -184,6 +187,8 @@ void extmark_del(buf_T *buf, MarkTreeIter *itr, MTKey key, bool restore)
}
}
+ decor_state_invalidate(buf);
+
// TODO(bfredl): delete it from current undo header, opportunistically?
}
@@ -237,6 +242,10 @@ bool extmark_clear(buf_T *buf, uint32_t ns_id, int l_row, colnr_T l_col, int u_r
}
}
+ if (marks_cleared_any) {
+ decor_state_invalidate(buf);
+ }
+
return marks_cleared_any;
}
@@ -367,16 +376,28 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
marktree_itr_get(buf->b_marktree, (int32_t)l_row, l_col, itr);
while (true) {
MTKey mark = marktree_itr_current(itr);
- if (mark.pos.row < 0
- || mark.pos.row > u_row
- || (mark.pos.row == u_row && mark.pos.col > u_col)) {
+ if (mark.pos.row < 0 || mark.pos.row > u_row) {
break;
}
+ bool copy = true;
+ // No need to copy left gravity marks at the beginning of the range,
+ // and right gravity marks at the end of the range, unless invalidated.
+ if (mark.pos.row == l_row && mark.pos.col - !mt_right(mark) < l_col) {
+ copy = false;
+ } else if (mark.pos.row == u_row) {
+ if (mark.pos.col > u_col + 1) {
+ break;
+ } else if (mark.pos.col + mt_right(mark) > u_col) {
+ copy = false;
+ }
+ }
+
bool invalidated = false;
// Invalidate/delete mark
if (!only_copy && !mt_invalid(mark) && mt_invalidate(mark) && !mt_end(mark)) {
- MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, NULL);
+ MarkTreeIter enditr[1] = { *itr };
+ MTPos endpos = marktree_get_altpos(buf->b_marktree, mark, enditr);
// Invalidate unpaired marks in deleted lines and paired marks whose entire
// range has been deleted.
if ((!mt_paired(mark) && mark.pos.row < u_row)
@@ -388,8 +409,10 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
extmark_del(buf, itr, mark, true);
continue;
} else {
+ copy = true;
invalidated = true;
mt_itr_rawkey(itr).flags |= MT_FLAG_INVALID;
+ mt_itr_rawkey(enditr).flags |= MT_FLAG_INVALID;
marktree_revise_meta(buf->b_marktree, itr, mark);
buf_decor_remove(buf, mark.pos.row, endpos.row, mark.pos.col, mt_decor(mark), false);
}
@@ -397,7 +420,7 @@ void extmark_splice_delete(buf_T *buf, int l_row, colnr_T l_col, int u_row, coln
}
// Push mark to undo header
- if (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark))) {
+ if (copy && (only_copy || (uvp != NULL && op == kExtmarkUndo && !mt_no_undo(mark)))) {
ExtmarkSavePos pos = {
.mark = mt_lookup_key(mark),
.invalidated = invalidated,
@@ -541,10 +564,8 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t
if (old_row > 0 || old_col > 0) {
// Copy and invalidate marks that would be effected by delete
- // TODO(bfredl): Be "smart" about gravity here, left-gravity at the
- // beginning and right-gravity at the end need not be preserved.
- // Also be smart about marks that already have been saved (important for
- // merge!)
+ // TODO(bfredl): Be smart about marks that already have been
+ // saved (important for merge!)
int end_row = start_row + old_row;
int end_col = (old_row ? 0 : start_col) + old_col;
u_header_T *uhp = u_force_get_undo_header(buf);
diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c
index d183978d2d..1e6153bf8d 100644
--- a/src/nvim/file_search.c
+++ b/src/nvim/file_search.c
@@ -41,6 +41,7 @@
// functions.
#include <assert.h>
+#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
@@ -52,6 +53,7 @@
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/errors.h"
@@ -67,6 +69,7 @@
#include "nvim/message.h"
#include "nvim/normal.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
@@ -76,7 +79,6 @@
#include "nvim/path.h"
#include "nvim/strings.h"
#include "nvim/vim_defs.h"
-#include "nvim/window.h"
static char *ff_expand_buffer = NULL; // used for expanding filenames
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index fc7fabc009..61b252f823 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -38,7 +38,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/iconv_defs.h"
#include "nvim/log.h"
@@ -55,7 +54,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/fs.h"
#include "nvim/os/fs_defs.h"
#include "nvim/os/input.h"
@@ -3278,7 +3276,11 @@ static void vim_mktempdir(void)
expand_env((char *)temp_dirs[i], tmp, TEMP_FILE_PATH_MAXLEN - 64);
if (!os_isdir(tmp)) {
if (strequal("$TMPDIR", temp_dirs[i])) {
- WLOG("$TMPDIR tempdir not a directory (or does not exist): %s", tmp);
+ if (!os_getenv("TMPDIR")) {
+ DLOG("$TMPDIR is unset");
+ } else {
+ WLOG("$TMPDIR tempdir not a directory (or does not exist): \"%s\"", tmp);
+ }
}
continue;
}
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index c9699cb161..b59933d600 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -1658,7 +1658,7 @@ static void foldDelMarker(buf_T *buf, linenr_T lnum, char *marker, size_t marker
if (*cms != NUL) {
// Also delete 'commentstring' if it matches.
char *cms2 = strstr(cms, "%s");
- if (p - line >= cms2 - cms
+ if (cms2 != NULL && p - line >= cms2 - cms
&& strncmp(p - (cms2 - cms), cms, (size_t)(cms2 - cms)) == 0
&& strncmp(p + len, cms2 + 2, strlen(cms2 + 2)) == 0) {
p -= cms2 - cms;
diff --git a/src/nvim/garray.c b/src/nvim/garray.c
index 4f8ba30522..a8d15a1fa8 100644
--- a/src/nvim/garray.c
+++ b/src/nvim/garray.c
@@ -7,12 +7,13 @@
#include "nvim/garray.h"
#include "nvim/log.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/path.h"
#include "nvim/strings.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "garray.c.generated.h"
+# include "garray.c.generated.h" // IWYU pragma: keep
#endif
/// Clear an allocated growing array.
diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua
index ed6e30ea10..890c260843 100644
--- a/src/nvim/generators/c_grammar.lua
+++ b/src/nvim/generators/c_grammar.lua
@@ -1,10 +1,37 @@
-local lpeg = vim.lpeg
-
-- lpeg grammar for building api metadata from a set of header files. It
-- ignores comments and preprocessor commands and parses a very small subset
-- of C prototypes with a limited set of types
-local P, R, S = lpeg.P, lpeg.R, lpeg.S
-local C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg
+
+--- @class nvim.c_grammar.Proto
+--- @field [1] 'proto'
+--- @field pos integer
+--- @field endpos integer
+--- @field fast boolean
+--- @field name string
+--- @field return_type string
+--- @field parameters [string, string][]
+--- @field static true?
+--- @field inline true?
+
+--- @class nvim.c_grammar.Preproc
+--- @field [1] 'preproc'
+--- @field content string
+
+--- @class nvim.c_grammar.Empty
+--- @field [1] 'empty'
+
+--- @alias nvim.c_grammar.result
+--- | nvim.c_grammar.Proto
+--- | nvim.c_grammar.Preproc
+--- | nvim.c_grammar.Empty
+
+--- @class nvim.c_grammar
+--- @field match fun(self, input: string): nvim.c_grammar.result[]
+
+local lpeg = vim.lpeg
+
+local P, R, S, V = lpeg.P, lpeg.R, lpeg.S, lpeg.V
+local C, Ct, Cc, Cg, Cp = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg, lpeg.Cp
--- @param pat vim.lpeg.Pattern
local function rep(pat)
@@ -21,95 +48,253 @@ local function opt(pat)
return pat ^ -1
end
-local any = P(1) -- (consume one character)
+local any = P(1)
local letter = R('az', 'AZ') + S('_$')
local num = R('09')
local alpha = letter + num
local nl = P('\r\n') + P('\n')
-local not_nl = any - nl
local space = S(' \t')
+local str = P('"') * rep((P('\\') * any) + (1 - P('"'))) * P('"')
+local char = P("'") * (any - P("'")) * P("'")
local ws = space + nl
-local fill = rep(ws)
-local c_comment = P('//') * rep(not_nl)
-local cdoc_comment = P('///') * opt(Ct(Cg(rep(space) * rep(not_nl), 'comment')))
-local c_preproc = P('#') * rep(not_nl)
-local dllexport = P('DLLEXPORT') * rep1(ws)
-
-local typed_container = ((P('ArrayOf(') + P('DictOf(') + P('Dict(')) * rep1(any - P(')')) * P(')'))
-
-local c_id = (typed_container + (letter * rep(alpha)))
-local c_void = P('void')
-
-local c_param_type = (
- ((P('Error') * fill * P('*') * fill) * Cc('error'))
- + ((P('Arena') * fill * P('*') * fill) * Cc('arena'))
- + ((P('lua_State') * fill * P('*') * fill) * Cc('lstate'))
- + C(opt(P('const ')) * c_id * rep1(ws) * rep1(P('*')))
- + (C(c_id) * rep1(ws))
+local wb = #-alpha -- word boundary
+local id = letter * rep(alpha)
+
+local comment_inline = P('/*') * rep(1 - P('*/')) * P('*/')
+local comment = P('//') * rep(1 - nl) * nl
+local preproc = Ct(Cc('preproc') * P('#') * Cg(rep(1 - nl) * nl, 'content'))
+
+local fill = rep(ws + comment_inline + comment + preproc)
+
+--- @param s string
+--- @return vim.lpeg.Pattern
+local function word(s)
+ return fill * P(s) * wb * fill
+end
+
+--- @param x vim.lpeg.Pattern
+local function comma1(x)
+ return x * rep(fill * P(',') * fill * x)
+end
+
+--- @param v string
+local function Pf(v)
+ return fill * P(v) * fill
+end
+
+--- @param x vim.lpeg.Pattern
+local function paren(x)
+ return P('(') * fill * x * fill * P(')')
+end
+
+local cdoc_comment = P('///') * opt(Ct(Cg(rep(space) * rep(1 - nl), 'comment')))
+
+local braces = P({
+ 'S',
+ A = comment_inline + comment + preproc + str + char + (any - S('{}')),
+ S = P('{') * rep(V('A')) * rep(V('S') + V('A')) * P('}'),
+})
+
+-- stylua: ignore start
+local typed_container = P({
+ 'S',
+ S = (
+ (P('Union') * paren(comma1(V('ID'))))
+ + (P('ArrayOf') * paren(id * opt(P(',') * fill * rep1(num))))
+ + (P('DictOf') * paren(id))
+ + (P('LuaRefOf') * paren(
+ paren(comma1((V('ID') + str) * rep1(ws) * opt(P('*')) * id))
+ * opt(P(',') * fill * opt(P('*')) * V('ID'))
+ ))
+ + (P('Dict') * paren(id))),
+ ID = V('S') + id,
+})
+-- stylua: ignore end
+
+local ptr_mod = word('restrict') + word('__restrict') + word('const')
+local opt_ptr = rep(Pf('*') * opt(ptr_mod))
+
+--- @param name string
+--- @param var string
+--- @return vim.lpeg.Pattern
+local function attr(name, var)
+ return Cg((P(name) * Cc(true)), var)
+end
+
+--- @param name string
+--- @param var string
+--- @return vim.lpeg.Pattern
+local function attr_num(name, var)
+ return Cg((P(name) * paren(C(rep1(num)))), var)
+end
+
+local fattr = (
+ attr_num('FUNC_API_SINCE', 'since')
+ + attr_num('FUNC_API_DEPRECATED_SINCE', 'deprecated_since')
+ + attr('FUNC_API_FAST', 'fast')
+ + attr('FUNC_API_RET_ALLOC', 'ret_alloc')
+ + attr('FUNC_API_NOEXPORT', 'noexport')
+ + attr('FUNC_API_REMOTE_ONLY', 'remote_only')
+ + attr('FUNC_API_LUA_ONLY', 'lua_only')
+ + attr('FUNC_API_TEXTLOCK_ALLOW_CMDWIN', 'textlock_allow_cmdwin')
+ + attr('FUNC_API_TEXTLOCK', 'textlock')
+ + attr('FUNC_API_REMOTE_IMPL', 'remote_impl')
+ + attr('FUNC_API_COMPOSITOR_IMPL', 'compositor_impl')
+ + attr('FUNC_API_CLIENT_IMPL', 'client_impl')
+ + attr('FUNC_API_CLIENT_IGNORE', 'client_ignore')
+ + (P('FUNC_') * rep(alpha) * opt(fill * paren(rep(1 - P(')') * any))))
)
-local c_type = (C(c_void) * (ws ^ 1)) + c_param_type
-local c_param = Ct(c_param_type * C(c_id))
-local c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0)
-local c_params = Ct(c_void + c_param_list)
+local void = P('void') * wb
-local impl_line = (any - P('}')) * opt(rep(not_nl)) * nl
+local api_param_type = (
+ (word('Error') * opt_ptr * Cc('error'))
+ + (word('Arena') * opt_ptr * Cc('arena'))
+ + (word('lua_State') * opt_ptr * Cc('lstate'))
+)
-local ignore_line = rep1(not_nl) * nl
+local ctype = C(
+ opt(word('const'))
+ * (
+ typed_container
+ -- 'unsigned' is a type modifier, and a type itself
+ + (word('unsigned char') + word('unsigned'))
+ + (word('struct') * fill * id)
+ + id
+ )
+ * opt(word('const'))
+ * opt_ptr
+)
+
+local return_type = (C(void) * fill) + ctype
+-- stylua: ignore start
+local params = Ct(
+ (void * #P(')'))
+ + comma1(Ct(
+ (api_param_type + ctype)
+ * fill
+ * C(id)
+ * rep(Pf('[') * rep(alpha) * Pf(']'))
+ * rep(fill * fattr)
+ ))
+ * opt(Pf(',') * P('...'))
+)
+-- stylua: ignore end
+
+local ignore_line = rep1(1 - nl) * nl
local empty_line = Ct(Cc('empty') * nl * nl)
-local c_proto = Ct(
- Cc('proto')
- * opt(dllexport)
- * opt(Cg(P('static') * fill * Cc(true), 'static'))
- * Cg(c_type, 'return_type')
- * Cg(c_id, 'name')
- * fill
- * (P('(') * fill * Cg(c_params, 'parameters') * fill * P(')'))
- * Cg(Cc(false), 'fast')
- * (fill * Cg((P('FUNC_API_SINCE(') * C(rep1(num))) * P(')'), 'since') ^ -1)
- * (fill * Cg((P('FUNC_API_DEPRECATED_SINCE(') * C(rep1(num))) * P(')'), 'deprecated_since') ^ -1)
- * (fill * Cg((P('FUNC_API_FAST') * Cc(true)), 'fast') ^ -1)
- * (fill * Cg((P('FUNC_API_RET_ALLOC') * Cc(true)), 'ret_alloc') ^ -1)
- * (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1)
- * (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1)
- * (fill * Cg((P('FUNC_API_LUA_ONLY') * Cc(true)), 'lua_only') ^ -1)
- * (fill * (Cg(P('FUNC_API_TEXTLOCK_ALLOW_CMDWIN') * Cc(true), 'textlock_allow_cmdwin') + Cg(
- P('FUNC_API_TEXTLOCK') * Cc(true),
- 'textlock'
- )) ^ -1)
- * (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1)
- * (fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1)
- * (fill * Cg((P('FUNC_API_CLIENT_IMPL') * Cc(true)), 'client_impl') ^ -1)
- * (fill * Cg((P('FUNC_API_CLIENT_IGNORE') * Cc(true)), 'client_ignore') ^ -1)
- * fill
- * (P(';') + (P('{') * nl + (impl_line ^ 0) * P('}')))
+local proto_name = opt_ptr * fill * id
+
+-- __inline is used in MSVC
+local decl_mod = (
+ Cg(word('static') * Cc(true), 'static')
+ + Cg((word('inline') + word('__inline')) * Cc(true), 'inline')
)
-local dict_key = P('DictKey(') * Cg(rep1(any - P(')')), 'dict_key') * P(')')
-local keyset_field =
- Ct(Cg(c_id, 'type') * ws * Cg(c_id, 'name') * fill * (dict_key ^ -1) * fill * P(';') * fill)
-local c_keyset = Ct(
- P('typedef')
- * ws
- * P('struct')
+local proto = Ct(
+ Cg(Cp(), 'pos')
+ * Cc('proto')
+ * -#P('typedef')
+ * #alpha
+ * opt(P('DLLEXPORT') * rep1(ws))
+ * rep(decl_mod)
+ * Cg(return_type, 'return_type')
* fill
- * P('{')
+ * Cg(proto_name, 'name')
* fill
- * Cg(Ct(keyset_field ^ 1), 'fields')
- * P('}')
- * fill
- * P('Dict')
+ * paren(Cg(params, 'parameters'))
+ * Cg(Cc(false), 'fast')
+ * rep(fill * fattr)
+ * Cg(Cp(), 'endpos')
+ * (fill * (S(';') + braces))
+)
+
+local keyset_field = Ct(
+ Cg(ctype, 'type')
* fill
- * P('(')
- * Cg(c_id, 'keyset_name')
+ * Cg(id, 'name')
* fill
- * P(')')
- * P(';')
+ * opt(P('DictKey') * paren(Cg(rep1(1 - P(')')), 'dict_key')))
+ * Pf(';')
)
-local grammar = Ct(
- rep1(empty_line + c_proto + cdoc_comment + c_comment + c_preproc + ws + c_keyset + ignore_line)
+local keyset = Ct(
+ P('typedef')
+ * word('struct')
+ * Pf('{')
+ * Cg(Ct(rep1(keyset_field)), 'fields')
+ * Pf('}')
+ * P('Dict')
+ * paren(Cg(id, 'keyset_name'))
+ * Pf(';')
)
-return { grammar = grammar, typed_container = typed_container }
+
+local grammar =
+ Ct(rep1(empty_line + proto + cdoc_comment + comment + preproc + ws + keyset + ignore_line))
+
+if arg[1] == '--test' then
+ for i, t in ipairs({
+ 'void multiqueue_put_event(MultiQueue *self, Event event) {} ',
+ 'void *xmalloc(size_t size) {} ',
+ {
+ 'struct tm *os_localtime_r(const time_t *restrict clock,',
+ ' struct tm *restrict result) FUNC_ATTR_NONNULL_ALL {}',
+ },
+ {
+ '_Bool',
+ '# 163 "src/nvim/event/multiqueue.c"',
+ ' multiqueue_empty(MultiQueue *self)',
+ '{}',
+ },
+ 'const char *find_option_end(const char *arg, OptIndex *opt_idxp) {}',
+ 'bool semsg(const char *const fmt, ...) {}',
+ 'int32_t utf_ptr2CharInfo_impl(uint8_t const *p, uintptr_t const len) {}',
+ 'void ex_argdedupe(exarg_T *eap FUNC_ATTR_UNUSED) {}',
+ 'static TermKeySym register_c0(TermKey *tk, TermKeySym sym, unsigned char ctrl, const char *name) {}',
+ 'unsigned get_bkc_flags(buf_T *buf) {}',
+ 'char *xstpcpy(char *restrict dst, const char *restrict src) {}',
+ 'bool try_leave(const TryState *const tstate, Error *const err) {}',
+ 'void api_set_error(ErrorType errType) {}',
+ {
+ 'void nvim_subscribe(uint64_t channel_id, String event)',
+ 'FUNC_API_SINCE(1) FUNC_API_DEPRECATED_SINCE(13) FUNC_API_REMOTE_ONLY',
+ '{}',
+ },
+
+ -- Do not consume leading preproc statements
+ {
+ '#line 1 "D:/a/neovim/neovim/src\\nvim/mark.h"',
+ 'static __inline int mark_global_index(const char name)',
+ ' FUNC_ATTR_CONST',
+ '{}',
+ },
+ {
+ '',
+ '#line 1 "D:/a/neovim/neovim/src\\nvim/mark.h"',
+ 'static __inline int mark_global_index(const char name)',
+ '{}',
+ },
+ {
+ 'size_t xstrlcpy(char *__restrict dst, const char *__restrict src, size_t dsize)',
+ ' FUNC_ATTR_NONNULL_ALL',
+ ' {}',
+ },
+ }) do
+ if type(t) == 'table' then
+ t = table.concat(t, '\n') .. '\n'
+ end
+ t = t:gsub(' +', ' ')
+ local r = grammar:match(t)
+ if not r then
+ print('Test ' .. i .. ' failed')
+ print(' |' .. table.concat(vim.split(t, '\n'), '\n |'))
+ end
+ end
+end
+
+return {
+ grammar = grammar --[[@as nvim.c_grammar]],
+ typed_container = typed_container,
+}
diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua
index a78f746fee..c987037324 100644
--- a/src/nvim/generators/gen_api_dispatch.lua
+++ b/src/nvim/generators/gen_api_dispatch.lua
@@ -1,3 +1,10 @@
+-- Example (manual) invocation:
+--
+-- make
+-- cp build/nvim_version.lua src/nvim/
+-- cd src/nvim
+-- nvim -l generators/gen_api_dispatch.lua "../../build/src/nvim/auto/api/private/dispatch_wrappers.generated.h" "../../build/src/nvim/auto/api/private/api_metadata.generated.h" "../../build/funcs_metadata.mpack" "../../build/src/nvim/auto/lua_api_c_bindings.generated.h" "../../build/src/nvim/auto/keysets_defs.generated.h" "../../build/ui_metadata.mpack" "../../build/cmake.config/auto/versiondef_git.h" "./api/autocmd.h" "./api/buffer.h" "./api/command.h" "./api/deprecated.h" "./api/extmark.h" "./api/keysets_defs.h" "./api/options.h" "./api/tabpage.h" "./api/ui.h" "./api/vim.h" "./api/vimscript.h" "./api/win_config.h" "./api/window.h" "../../build/include/api/autocmd.h.generated.h" "../../build/include/api/buffer.h.generated.h" "../../build/include/api/command.h.generated.h" "../../build/include/api/deprecated.h.generated.h" "../../build/include/api/extmark.h.generated.h" "../../build/include/api/options.h.generated.h" "../../build/include/api/tabpage.h.generated.h" "../../build/include/api/ui.h.generated.h" "../../build/include/api/vim.h.generated.h" "../../build/include/api/vimscript.h.generated.h" "../../build/include/api/win_config.h.generated.h" "../../build/include/api/window.h.generated.h"
+
local mpack = vim.mpack
local hashy = require 'generators.hashy'
@@ -8,7 +15,7 @@ assert(#arg >= pre_args)
local dispatch_outputf = arg[1]
-- output h file with packed metadata (api_metadata.generated.h)
local api_metadata_outputf = arg[2]
--- output metadata mpack file, for use by other build scripts (api_metadata.mpack)
+-- output metadata mpack file, for use by other build scripts (funcs_metadata.mpack)
local mpack_outputf = arg[3]
local lua_c_bindings_outputf = arg[4] -- lua_api_c_bindings.generated.c
local keysets_outputf = arg[5] -- keysets_defs.generated.h
@@ -235,7 +242,7 @@ for x in string.gmatch(ui_options_text, '"([a-z][a-z_]+)"') do
table.insert(ui_options, x)
end
-local version = require 'nvim_version'
+local version = require 'nvim_version' -- `build/nvim_version.lua` file.
local git_version = io.open(git_version_inputf):read '*a'
local version_build = string.match(git_version, '#define NVIM_VERSION_BUILD "([^"]+)"') or vim.NIL
@@ -266,10 +273,7 @@ fixdict(1 + #version)
for _, item in ipairs(version) do
-- NB: all items are mandatory. But any error will be less confusing
-- with placeholder vim.NIL (than invalid mpack data)
- local val = item[2]
- if val == nil then
- val = vim.NIL
- end
+ local val = item[2] == nil and vim.NIL or item[2]
put(item[1], val)
end
put('build', version_build)
@@ -347,12 +351,16 @@ for _, k in ipairs(keysets) do
local function typename(type)
if type == 'HLGroupID' then
return 'kObjectTypeInteger'
+ elseif not type or vim.startswith(type, 'Union') then
+ return 'kObjectTypeNil'
+ elseif vim.startswith(type, 'LuaRefOf') then
+ return 'kObjectTypeLuaRef'
elseif type == 'StringArray' then
return 'kUnpackTypeStringArray'
- elseif type ~= nil then
- return 'kObjectType' .. type
+ elseif vim.startswith(type, 'ArrayOf') then
+ return 'kObjectTypeArray'
else
- return 'kObjectTypeNil'
+ return 'kObjectType' .. type
end
end
diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua
index 30a83330eb..a3bb76cb91 100644
--- a/src/nvim/generators/gen_api_ui_events.lua
+++ b/src/nvim/generators/gen_api_ui_events.lua
@@ -98,7 +98,7 @@ local function call_ui_event_method(output, ev)
end
events = vim.tbl_filter(function(ev)
- return ev[1] ~= 'empty'
+ return ev[1] ~= 'empty' and ev[1] ~= 'preproc'
end, events)
for i = 1, #events do
diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua
index 2ec0e9ab68..6e1ea92572 100644
--- a/src/nvim/generators/gen_declarations.lua
+++ b/src/nvim/generators/gen_declarations.lua
@@ -1,136 +1,105 @@
-local fname = arg[1]
-local static_fname = arg[2]
-local non_static_fname = arg[3]
-local preproc_fname = arg[4]
-local static_basename = arg[5]
-
-local lpeg = vim.lpeg
-
-local fold = function(func, ...)
- local result = nil
- for _, v in ipairs({ ... }) do
- if result == nil then
- result = v
- else
- result = func(result, v)
+local grammar = require('generators.c_grammar').grammar
+
+--- @param fname string
+--- @return string?
+local function read_file(fname)
+ local f = io.open(fname, 'r')
+ if not f then
+ return
+ end
+ local contents = f:read('*a')
+ f:close()
+ return contents
+end
+
+--- @param fname string
+--- @param contents string[]
+local function write_file(fname, contents)
+ local contents_s = table.concat(contents, '\n') .. '\n'
+ local fcontents = read_file(fname)
+ if fcontents == contents_s then
+ return
+ end
+ local f = assert(io.open(fname, 'w'))
+ f:write(contents_s)
+ f:close()
+end
+
+--- @param fname string
+--- @param non_static_fname string
+--- @return string? non_static
+local function add_iwyu_non_static(fname, non_static_fname)
+ if fname:find('.*/src/nvim/.*%.c$') then
+ -- Add an IWYU pragma comment if the corresponding .h file exists.
+ local header_fname = fname:sub(1, -3) .. '.h'
+ local header_f = io.open(header_fname, 'r')
+ if header_f then
+ header_f:close()
+ return (header_fname:gsub('.*/src/nvim/', 'nvim/'))
end
+ elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then
+ return 'nvim/api/private/dispatch.h'
+ elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then
+ return 'nvim/ui.h'
+ elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then
+ return 'nvim/ui_client.h'
+ elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then
+ return 'nvim/api/ui.h'
end
- return result
end
-local folder = function(func)
- return function(...)
- return fold(func, ...)
+--- @param d string
+local function process_decl(d)
+ -- Comments are really handled by preprocessor, so the following is not
+ -- needed
+ d = d:gsub('/%*.-%*/', '')
+ d = d:gsub('//.-\n', '\n')
+ d = d:gsub('# .-\n', '')
+ d = d:gsub('\n', ' ')
+ d = d:gsub('%s+', ' ')
+ d = d:gsub(' ?%( ?', '(')
+ d = d:gsub(' ?, ?', ', ')
+ d = d:gsub(' ?(%*+) ?', ' %1')
+ d = d:gsub(' ?(FUNC_ATTR_)', ' %1')
+ d = d:gsub(' $', '')
+ d = d:gsub('^ ', '')
+ return d .. ';'
+end
+
+--- @param fname string
+--- @param text string
+--- @return string[] static
+--- @return string[] non_static
+--- @return boolean any_static
+local function gen_declarations(fname, text)
+ local non_static = {} --- @type string[]
+ local static = {} --- @type string[]
+
+ local neededfile = fname:match('[^/]+$')
+ local curfile = nil
+ local any_static = false
+ for _, node in ipairs(grammar:match(text)) do
+ if node[1] == 'preproc' then
+ curfile = node.content:match('^%a* %d+ "[^"]-/?([^"/]+)"') or curfile
+ elseif node[1] == 'proto' and curfile == neededfile then
+ local node_text = text:sub(node.pos, node.endpos - 1)
+ local declaration = process_decl(node_text)
+
+ if node.static then
+ if not any_static and declaration:find('FUNC_ATTR_') then
+ any_static = true
+ end
+ static[#static + 1] = declaration
+ else
+ non_static[#non_static + 1] = 'DLLEXPORT ' .. declaration
+ end
+ end
end
-end
-local lit = lpeg.P
-local set = function(...)
- return lpeg.S(fold(function(a, b)
- return a .. b
- end, ...))
-end
-local any_character = lpeg.P(1)
-local rng = function(s, e)
- return lpeg.R(s .. e)
-end
-local concat = folder(function(a, b)
- return a * b
-end)
-local branch = folder(function(a, b)
- return a + b
-end)
-local one_or_more = function(v)
- return v ^ 1
-end
-local two_or_more = function(v)
- return v ^ 2
-end
-local any_amount = function(v)
- return v ^ 0
-end
-local one_or_no = function(v)
- return v ^ -1
-end
-local look_behind = lpeg.B
-local look_ahead = function(v)
- return #v
-end
-local neg_look_ahead = function(v)
- return -v
-end
-local neg_look_behind = function(v)
- return -look_behind(v)
+ return static, non_static, any_static
end
-local w = branch(rng('a', 'z'), rng('A', 'Z'), lit('_'))
-local aw = branch(w, rng('0', '9'))
-local s = set(' ', '\n', '\t')
-local raw_word = concat(w, any_amount(aw))
-local right_word = concat(raw_word, neg_look_ahead(aw))
-local word = branch(
- concat(
- branch(lit('ArrayOf('), lit('DictOf('), lit('Dict(')), -- typed container macro
- one_or_more(any_character - lit(')')),
- lit(')')
- ),
- concat(neg_look_behind(aw), right_word)
-)
-local inline_comment =
- concat(lit('/*'), any_amount(concat(neg_look_ahead(lit('*/')), any_character)), lit('*/'))
-local spaces = any_amount(branch(
- s,
- -- Comments are really handled by preprocessor, so the following is not needed
- inline_comment,
- concat(lit('//'), any_amount(concat(neg_look_ahead(lit('\n')), any_character)), lit('\n')),
- -- Linemarker inserted by preprocessor
- concat(lit('# '), any_amount(concat(neg_look_ahead(lit('\n')), any_character)), lit('\n'))
-))
-local typ_part = concat(word, any_amount(concat(spaces, lit('*'))), spaces)
-
-local typ_id = two_or_more(typ_part)
-local arg = typ_id -- argument name is swallowed by typ
-local pattern = concat(
- any_amount(branch(set(' ', '\t'), inline_comment)),
- typ_id, -- return type with function name
- spaces,
- lit('('),
- spaces,
- one_or_no(branch( -- function arguments
- concat(
- arg, -- first argument, does not require comma
- any_amount(concat( -- following arguments, start with a comma
- spaces,
- lit(','),
- spaces,
- arg,
- any_amount(concat(lit('['), spaces, any_amount(aw), spaces, lit(']')))
- )),
- one_or_no(concat(spaces, lit(','), spaces, lit('...')))
- ),
- lit('void') -- also accepts just void
- )),
- spaces,
- lit(')'),
- any_amount(concat( -- optional attributes
- spaces,
- lit('FUNC_'),
- any_amount(aw),
- one_or_no(concat( -- attribute argument
- spaces,
- lit('('),
- any_amount(concat(neg_look_ahead(lit(')')), any_character)),
- lit(')')
- ))
- )),
- look_ahead(concat( -- definition must be followed by "{"
- spaces,
- lit('{')
- ))
-)
-
-if fname == '--help' then
- print([[
+local usage = [[
Usage:
gen_declarations.lua definitions.c static.h non-static.h definitions.i
@@ -141,204 +110,77 @@ non-static.h. File `definitions.i' should contain an already preprocessed
version of definitions.c and it is the only one which is actually parsed,
definitions.c is needed only to determine functions from which file out of all
functions found in definitions.i are needed and to generate an IWYU comment.
-
-Additionally uses the following environment variables:
-
- NVIM_GEN_DECLARATIONS_LINE_NUMBERS:
- If set to 1 then all generated declarations receive a comment with file
- name and line number after the declaration. This may be useful for
- debugging gen_declarations script, but not much beyond that with
- configured development environment (i.e. with with clang/etc).
-
- WARNING: setting this to 1 will cause extensive rebuilds: declarations
- generator script will not regenerate non-static.h file if its
- contents did not change, but including line numbers will make
- contents actually change.
-
- With contents changed timestamp of the file is regenerated even
- when no real changes were made (e.g. a few lines were added to
- a function which is not at the bottom of the file).
-
- With changed timestamp build system will assume that header
- changed, triggering rebuilds of all C files which depend on the
- "changed" header.
-]])
- os.exit()
-end
-
-local preproc_f = io.open(preproc_fname)
-local text = preproc_f:read('*all')
-preproc_f:close()
-
-local non_static = [[
-#define DEFINE_FUNC_ATTRIBUTES
-#include "nvim/func_attr.h"
-#undef DEFINE_FUNC_ATTRIBUTES
-#ifndef DLLEXPORT
-# ifdef MSWIN
-# define DLLEXPORT __declspec(dllexport)
-# else
-# define DLLEXPORT
-# endif
-#endif
-]]
-
-local static = [[
-#define DEFINE_FUNC_ATTRIBUTES
-#include "nvim/func_attr.h"
-#undef DEFINE_FUNC_ATTRIBUTES
]]
-local non_static_footer = [[
-#include "nvim/func_attr.h"
-]]
+local function main()
+ local fname = arg[1]
+ local static_fname = arg[2]
+ local non_static_fname = arg[3]
+ local preproc_fname = arg[4]
+ local static_basename = arg[5]
-local static_footer = [[
-#define DEFINE_EMPTY_ATTRIBUTES
-#include "nvim/func_attr.h" // IWYU pragma: export
-]]
-
-if fname:find('.*/src/nvim/.*%.c$') then
- -- Add an IWYU pragma comment if the corresponding .h file exists.
- local header_fname = fname:sub(1, -3) .. '.h'
- local header_f = io.open(header_fname, 'r')
- if header_f ~= nil then
- header_f:close()
- non_static = ([[
-// IWYU pragma: private, include "%s"
-]]):format(header_fname:gsub('.*/src/nvim/', 'nvim/')) .. non_static
+ if fname == '--help' or #arg < 5 then
+ print(usage)
+ os.exit()
end
-elseif fname:find('.*/src/nvim/.*%.h$') then
- static = ([[
-// IWYU pragma: private, include "%s"
-]]):format(fname:gsub('.*/src/nvim/', 'nvim/')) .. static
-elseif non_static_fname:find('/include/api/private/dispatch_wrappers%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/api/private/dispatch.h"
-]] .. non_static
-elseif non_static_fname:find('/include/ui_events_call%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/ui.h"
-]] .. non_static
-elseif non_static_fname:find('/include/ui_events_client%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/ui_client.h"
-]] .. non_static
-elseif non_static_fname:find('/include/ui_events_remote%.h%.generated%.h$') then
- non_static = [[
-// IWYU pragma: private, include "nvim/api/ui.h"
-]] .. non_static
-end
-local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"'
+ local text = assert(read_file(preproc_fname))
-local init = 1
-local curfile = nil
-local neededfile = fname:match('[^/]+$')
-local declline = 0
-local declendpos = 0
-local curdir = nil
-local is_needed_file = false
-local init_is_nl = true
-local any_static = false
-while init ~= nil do
- if init_is_nl and text:sub(init, init) == '#' then
- local line, dir, file = text:match(filepattern, init)
- if file ~= nil then
- curfile = file
- is_needed_file = (curfile == neededfile)
- declline = tonumber(line) - 1
- curdir = dir:gsub('.*/src/nvim/', '')
- else
- declline = declline - 1
- end
- elseif init < declendpos then -- luacheck: ignore 542
- -- Skipping over declaration
- elseif is_needed_file then
- s = init
- local e = pattern:match(text, init)
- if e ~= nil then
- local declaration = text:sub(s, e - 1)
- -- Comments are really handled by preprocessor, so the following is not
- -- needed
- declaration = declaration:gsub('/%*.-%*/', '')
- declaration = declaration:gsub('//.-\n', '\n')
-
- declaration = declaration:gsub('# .-\n', '')
+ local static_decls, non_static_decls, any_static = gen_declarations(fname, text)
- declaration = declaration:gsub('\n', ' ')
- declaration = declaration:gsub('%s+', ' ')
- declaration = declaration:gsub(' ?%( ?', '(')
- -- declaration = declaration:gsub(' ?%) ?', ')')
- declaration = declaration:gsub(' ?, ?', ', ')
- declaration = declaration:gsub(' ?(%*+) ?', ' %1')
- declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1')
- declaration = declaration:gsub(' $', '')
- declaration = declaration:gsub('^ ', '')
- declaration = declaration .. ';'
-
- if os.getenv('NVIM_GEN_DECLARATIONS_LINE_NUMBERS') == '1' then
- declaration = declaration .. (' // %s/%s:%u'):format(curdir, curfile, declline)
- end
- declaration = declaration .. '\n'
- if declaration:sub(1, 6) == 'static' then
- if declaration:find('FUNC_ATTR_') then
- any_static = true
- end
- static = static .. declaration
- else
- declaration = 'DLLEXPORT ' .. declaration
- non_static = non_static .. declaration
- end
- declendpos = e
- end
- end
- init = text:find('[\n;}]', init)
- if init == nil then
- break
- end
- init_is_nl = text:sub(init, init) == '\n'
- init = init + 1
- if init_is_nl and is_needed_file then
- declline = declline + 1
+ local static = {} --- @type string[]
+ if fname:find('.*/src/nvim/.*%.h$') then
+ static[#static + 1] = ('// IWYU pragma: private, include "%s"'):format(
+ fname:gsub('.*/src/nvim/', 'nvim/')
+ )
end
-end
-
-non_static = non_static .. non_static_footer
-static = static .. static_footer
-
-local F
-F = io.open(static_fname, 'w')
-F:write(static)
-F:close()
-
-if any_static then
- F = io.open(fname, 'r')
- local orig_text = F:read('*a')
- local pat = '\n#%s?include%s+"' .. static_basename .. '"\n'
- local pat_comment = '\n#%s?include%s+"' .. static_basename .. '"%s*//'
- if not string.find(orig_text, pat) and not string.find(orig_text, pat_comment) then
- error('fail: missing include for ' .. static_basename .. ' in ' .. fname)
+ vim.list_extend(static, {
+ '#define DEFINE_FUNC_ATTRIBUTES',
+ '#include "nvim/func_attr.h"',
+ '#undef DEFINE_FUNC_ATTRIBUTES',
+ })
+ vim.list_extend(static, static_decls)
+ vim.list_extend(static, {
+ '#define DEFINE_EMPTY_ATTRIBUTES',
+ '#include "nvim/func_attr.h" // IWYU pragma: export',
+ '',
+ })
+
+ write_file(static_fname, static)
+
+ if any_static then
+ local orig_text = assert(read_file(fname))
+ local pat = '\n#%s?include%s+"' .. static_basename .. '"\n'
+ local pat_comment = '\n#%s?include%s+"' .. static_basename .. '"%s*//'
+ if not orig_text:find(pat) and not orig_text:find(pat_comment) then
+ error(('fail: missing include for %s in %s'):format(static_basename, fname))
+ end
end
- F:close()
-end
-if non_static_fname == 'SKIP' then
- return -- only want static declarations
-end
-
--- Before generating the non-static headers, check if the current file (if
--- exists) is different from the new one. If they are the same, we won't touch
--- the current version to avoid triggering an unnecessary rebuilds of modules
--- that depend on this one
-F = io.open(non_static_fname, 'r')
-if F ~= nil then
- if F:read('*a') == non_static then
- os.exit(0)
+ if non_static_fname ~= 'SKIP' then
+ local non_static = {} --- @type string[]
+ local iwyu_non_static = add_iwyu_non_static(fname, non_static_fname)
+ if iwyu_non_static then
+ non_static[#non_static + 1] = ('// IWYU pragma: private, include "%s"'):format(
+ iwyu_non_static
+ )
+ end
+ vim.list_extend(non_static, {
+ '#define DEFINE_FUNC_ATTRIBUTES',
+ '#include "nvim/func_attr.h"',
+ '#undef DEFINE_FUNC_ATTRIBUTES',
+ '#ifndef DLLEXPORT',
+ '# ifdef MSWIN',
+ '# define DLLEXPORT __declspec(dllexport)',
+ '# else',
+ '# define DLLEXPORT',
+ '# endif',
+ '#endif',
+ })
+ vim.list_extend(non_static, non_static_decls)
+ non_static[#non_static + 1] = '#include "nvim/func_attr.h"'
+ write_file(non_static_fname, non_static)
end
- F:close()
end
-F = io.open(non_static_fname, 'w')
-F:write(non_static)
-F:close()
+return main()
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index 443c68e008..0b6ee6cb24 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -19,6 +19,7 @@ hashpipe:write([[
#include "nvim/digraph.h"
#include "nvim/eval.h"
#include "nvim/eval/buffer.h"
+#include "nvim/eval/deprecated.h"
#include "nvim/eval/fs.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua
index 02f3ac3257..e5dba90925 100644
--- a/src/nvim/generators/gen_options.lua
+++ b/src/nvim/generators/gen_options.lua
@@ -1,143 +1,30 @@
-local options_file = arg[1]
-local options_enum_file = arg[2]
-local options_map_file = arg[3]
-
-local opt_fd = assert(io.open(options_file, 'w'))
-local opt_enum_fd = assert(io.open(options_enum_file, 'w'))
-local opt_map_fd = assert(io.open(options_map_file, 'w'))
-
-local w = function(s)
- if s:match('^ %.') then
- opt_fd:write(s .. ',\n')
- else
- opt_fd:write(s .. '\n')
- end
-end
-
---- @param s string
-local function enum_w(s)
- opt_enum_fd:write(s .. '\n')
-end
-
---- @param s string
-local function map_w(s)
- opt_map_fd:write(s .. '\n')
-end
-
--- @module 'nvim.options'
local options = require('options')
local options_meta = options.options
-
local cstr = options.cstr
local valid_scopes = options.valid_scopes
---- Options for each scope.
---- @type table<string, vim.option_meta[]>
-local scope_options = {}
-for _, scope in ipairs(valid_scopes) do
- scope_options[scope] = {}
+--- @param o vim.option_meta
+--- @return string
+local function get_values_var(o)
+ return ('opt_%s_values'):format(o.abbreviation or o.full_name)
end
--- @param s string
--- @return string
-local lowercase_to_titlecase = function(s)
- return s:sub(1, 1):upper() .. s:sub(2)
+local function lowercase_to_titlecase(s)
+ return table.concat(vim.tbl_map(function(word) --- @param word string
+ return word:sub(1, 1):upper() .. word:sub(2)
+ end, vim.split(s, '[-_]')))
end
--- Generate options enum file
-enum_w('// IWYU pragma: private, include "nvim/option_defs.h"')
-enum_w('')
-
---- Map of option name to option index
---- @type table<string, string>
-local option_index = {}
-
--- Generate option index enum and populate the `option_index` and `scope_option` dicts.
-enum_w('typedef enum {')
-enum_w(' kOptInvalid = -1,')
-
-for i, o in ipairs(options_meta) do
- local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
- enum_w((' %s = %u,'):format(enum_val_name, i - 1))
-
- option_index[o.full_name] = enum_val_name
-
- if o.abbreviation then
- option_index[o.abbreviation] = enum_val_name
- end
-
- if o.alias then
- o.alias = type(o.alias) == 'string' and { o.alias } or o.alias
-
- for _, v in ipairs(o.alias) do
- option_index[v] = enum_val_name
- end
- end
-
- for _, scope in ipairs(o.scope) do
- table.insert(scope_options[scope], o)
- end
-end
-
-enum_w(' // Option count')
-enum_w('#define kOptCount ' .. tostring(#options_meta))
-enum_w('} OptIndex;')
-
--- @param scope string
--- @param option_name string
--- @return string
-local get_scope_option = function(scope, option_name)
+local function get_scope_option(scope, option_name)
return ('k%sOpt%s'):format(lowercase_to_titlecase(scope), lowercase_to_titlecase(option_name))
end
--- Generate option index enum for each scope
-for _, scope in ipairs(valid_scopes) do
- enum_w('')
-
- local scope_name = lowercase_to_titlecase(scope)
- enum_w('typedef enum {')
- enum_w((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
-
- for idx, option in ipairs(scope_options[scope]) do
- enum_w((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
- end
-
- enum_w((' // %s option count'):format(scope_name))
- enum_w(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
- enum_w(('} %sOptIndex;'):format(scope_name))
-end
-
--- Generate reverse lookup from option scope index to option index for each scope.
-for _, scope in ipairs(valid_scopes) do
- enum_w('')
- enum_w(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
- for _, option in ipairs(scope_options[scope]) do
- local idx = option_index[option.full_name]
- enum_w((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
- end
- enum_w('});')
-end
-
-opt_enum_fd:close()
-
--- Generate option index map.
-local hashy = require('generators.hashy')
-local neworder, hashfun = hashy.hashy_hash('find_option', vim.tbl_keys(option_index), function(idx)
- return ('option_hash_elems[%s].name'):format(idx)
-end)
-
-map_w('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
-
-for _, name in ipairs(neworder) do
- assert(option_index[name] ~= nil)
- map_w((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
-end
-
-map_w('};\n')
-map_w('static ' .. hashfun)
-
-opt_map_fd:close()
-
local redraw_flags = {
ui_option = 'kOptFlagUIOption',
tabline = 'kOptFlagRedrTabl',
@@ -161,28 +48,29 @@ local list_flags = {
--- @param o vim.option_meta
--- @return string
local function get_flags(o)
- --- @type string
- local flags = '0'
+ --- @type string[]
+ local flags = { '0' }
--- @param f string
- local add_flag = function(f)
- flags = flags .. '|' .. f
+ local function add_flag(f)
+ table.insert(flags, f)
end
if o.list then
add_flag(list_flags[o.list])
end
- if o.redraw then
- for _, r_flag in ipairs(o.redraw) do
- add_flag(redraw_flags[r_flag])
- end
+
+ for _, r_flag in ipairs(o.redraw or {}) do
+ add_flag(redraw_flags[r_flag])
end
+
if o.expand then
add_flag('kOptFlagExpand')
if o.expand == 'nodefault' then
add_flag('kOptFlagNoDefExp')
end
end
+
for _, flag_desc in ipairs({
{ 'nodefault', 'NoDefault' },
{ 'no_mkrc', 'NoMkrc' },
@@ -197,13 +85,14 @@ local function get_flags(o)
{ 'modelineexpr', 'MLE' },
{ 'func' },
}) do
- local key_name = flag_desc[1]
- local def_name = 'kOptFlag' .. (flag_desc[2] or lowercase_to_titlecase(key_name))
+ local key_name, flag_suffix = flag_desc[1], flag_desc[2]
if o[key_name] then
+ local def_name = 'kOptFlag' .. (flag_suffix or lowercase_to_titlecase(key_name))
add_flag(def_name)
end
end
- return flags
+
+ return table.concat(flags, '|')
end
--- @param opt_type vim.option_type
@@ -220,21 +109,6 @@ end
--- @param o vim.option_meta
--- @return string
-local function get_type_flags(o)
- local opt_types = (type(o.type) == 'table') and o.type or { o.type }
- local type_flags = '0'
- assert(type(opt_types) == 'table')
-
- for _, opt_type in ipairs(opt_types) do
- assert(type(opt_type) == 'string')
- type_flags = ('%s | (1 << %s)'):format(type_flags, opt_type_enum(opt_type))
- end
-
- return type_flags
-end
-
---- @param o vim.option_meta
---- @return string
local function get_scope_flags(o)
local scope_flags = '0'
@@ -262,35 +136,15 @@ local function get_scope_idx(o)
return ('{\n%s\n }'):format(table.concat(strs, ',\n'))
end
---- @param c string|string[]
---- @param base_string? string
---- @return string
-local function get_cond(c, base_string)
- local cond_string = base_string or '#if '
- if type(c) == 'table' then
- cond_string = cond_string .. get_cond(c[1], '')
- for i, subc in ipairs(c) do
- if i > 1 then
- cond_string = cond_string .. ' && ' .. get_cond(subc, '')
- end
- end
- elseif c:sub(1, 1) == '!' then
- cond_string = cond_string .. '!defined(' .. c:sub(2) .. ')'
- else
- cond_string = cond_string .. 'defined(' .. c .. ')'
- end
- return cond_string
-end
-
--- @param s string
--- @return string
-local static_cstr_as_string = function(s)
+local function static_cstr_as_string(s)
return ('{ .data = %s, .size = sizeof(%s) - 1 }'):format(s, s)
end
--- @param v vim.option_value|function
--- @return string
-local get_opt_val = function(v)
+local function get_opt_val(v)
--- @type vim.option_type
local v_type
@@ -308,6 +162,7 @@ local get_opt_val = function(v)
elseif v_type == 'number' then
v = ('%iL'):format(v)
elseif v_type == 'string' then
+ --- @cast v string
v = static_cstr_as_string(cstr(v))
end
end
@@ -318,7 +173,7 @@ end
--- @param d vim.option_value|function
--- @param n string
--- @return string
-local get_defaults = function(d, n)
+local function get_defaults(d, n)
if d == nil then
error("option '" .. n .. "' should have a default value")
end
@@ -327,84 +182,354 @@ end
--- @param i integer
--- @param o vim.option_meta
-local function dump_option(i, o)
- w(' [' .. ('%u'):format(i - 1) .. ']={')
- w(' .fullname=' .. cstr(o.full_name))
+--- @param write fun(...: string)
+local function dump_option(i, o, write)
+ write(' [', ('%u'):format(i - 1) .. ']={')
+ write(' .fullname=', cstr(o.full_name))
if o.abbreviation then
- w(' .shortname=' .. cstr(o.abbreviation))
+ write(' .shortname=', cstr(o.abbreviation))
end
- w(' .flags=' .. get_flags(o))
- w(' .type_flags=' .. get_type_flags(o))
- w(' .scope_flags=' .. get_scope_flags(o))
- w(' .scope_idx=' .. get_scope_idx(o))
+ write(' .type=', opt_type_enum(o.type))
+ write(' .flags=', get_flags(o))
+ write(' .scope_flags=', get_scope_flags(o))
+ write(' .scope_idx=', get_scope_idx(o))
+ write(' .values=', (o.values and get_values_var(o) or 'NULL'))
+ write(' .values_len=', (o.values and #o.values or '0'))
+ write(' .flags_var=', (o.flags_varname and ('&%s'):format(o.flags_varname) or 'NULL'))
if o.enable_if then
- w(get_cond(o.enable_if))
+ write(('#if defined(%s)'):format(o.enable_if))
end
local is_window_local = #o.scope == 1 and o.scope[1] == 'win'
- if not is_window_local then
- if o.varname then
- w(' .var=&' .. o.varname)
- elseif o.immutable then
- -- Immutable options can directly point to the default value.
- w((' .var=&options[%u].def_val.data'):format(i - 1))
- else
- -- Option must be immutable or have a variable.
- assert(false)
- end
+ if is_window_local then
+ write(' .var=NULL')
+ elseif o.varname then
+ write(' .var=&', o.varname)
+ elseif o.immutable then
+ -- Immutable options can directly point to the default value.
+ write((' .var=&options[%u].def_val.data'):format(i - 1))
else
- w(' .var=NULL')
- end
- w(' .immutable=' .. (o.immutable and 'true' or 'false'))
- if o.cb then
- w(' .opt_did_set_cb=' .. o.cb)
- end
- if o.expand_cb then
- w(' .opt_expand_cb=' .. o.expand_cb)
+ error('Option must be immutable or have a variable.')
end
+
+ write(' .immutable=', (o.immutable and 'true' or 'false'))
+ write(' .opt_did_set_cb=', o.cb or 'NULL')
+ write(' .opt_expand_cb=', o.expand_cb or 'NULL')
+
if o.enable_if then
- w('#else')
+ write('#else')
-- Hidden option directly points to default value.
- w((' .var=&options[%u].def_val.data'):format(i - 1))
+ write((' .var=&options[%u].def_val.data'):format(i - 1))
-- Option is always immutable on the false branch of `enable_if`.
- w(' .immutable=true')
- w('#endif')
+ write(' .immutable=true')
+ write('#endif')
end
- if o.defaults then
- if o.defaults.condition then
- w(get_cond(o.defaults.condition))
+
+ if not o.defaults then
+ write(' .def_val=NIL_OPTVAL')
+ elseif o.defaults.condition then
+ write(('#if defined(%s)'):format(o.defaults.condition))
+ write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name))
+ if o.defaults.if_false then
+ write('#else')
+ write(' .def_val=', get_defaults(o.defaults.if_false, o.full_name))
end
- w(' .def_val=' .. get_defaults(o.defaults.if_true, o.full_name))
- if o.defaults.condition then
- if o.defaults.if_false then
- w('#else')
- w(' .def_val=' .. get_defaults(o.defaults.if_false, o.full_name))
- end
- w('#endif')
+ write('#endif')
+ else
+ write(' .def_val=', get_defaults(o.defaults.if_true, o.full_name))
+ end
+
+ write(' },')
+end
+
+--- @param prefix string
+--- @param values vim.option_valid_values
+local function preorder_traversal(prefix, values)
+ local out = {} --- @type string[]
+
+ local function add(s)
+ table.insert(out, s)
+ end
+
+ add('')
+ add(('EXTERN const char *(%s_values[%s]) INIT( = {'):format(prefix, #vim.tbl_keys(values) + 1))
+
+ --- @type [string,vim.option_valid_values][]
+ local children = {}
+
+ for _, value in ipairs(values) do
+ if type(value) == 'string' then
+ add((' "%s",'):format(value))
+ else
+ assert(type(value) == 'table' and type(value[1]) == 'string' and type(value[2]) == 'table')
+ add((' "%s",'):format(value[1]))
+ table.insert(children, value)
end
+ end
+
+ add(' NULL')
+ add('});')
+
+ for _, value in pairs(children) do
+ -- Remove trailing colon from the added prefix to prevent syntax errors.
+ add(preorder_traversal(prefix .. '_' .. value[1]:gsub(':$', ''), value[2]))
+ end
+
+ return table.concat(out, '\n')
+end
+
+--- @param o vim.option_meta
+--- @return string
+local function gen_opt_enum(o)
+ local out = {} --- @type string[]
+
+ local function add(s)
+ table.insert(out, s)
+ end
+
+ add('')
+ add('typedef enum {')
+
+ local opt_name = lowercase_to_titlecase(o.abbreviation or o.full_name)
+ --- @type table<string,integer>
+ local enum_values
+
+ if type(o.flags) == 'table' then
+ enum_values = o.flags --[[ @as table<string,integer> ]]
else
- w(' .def_val=NIL_OPTVAL')
+ enum_values = {}
+ for i, flag_name in ipairs(o.values) do
+ assert(type(flag_name) == 'string')
+ enum_values[flag_name] = math.pow(2, i - 1)
+ end
+ end
+
+ -- Sort the keys by the flag value so that the enum can be generated in order.
+ --- @type string[]
+ local flag_names = vim.tbl_keys(enum_values)
+ table.sort(flag_names, function(a, b)
+ return enum_values[a] < enum_values[b]
+ end)
+
+ for _, flag_name in pairs(flag_names) do
+ add(
+ (' kOpt%sFlag%s = 0x%02x,'):format(
+ opt_name,
+ lowercase_to_titlecase(flag_name:gsub(':$', '')),
+ enum_values[flag_name]
+ )
+ )
+ end
+
+ add(('} Opt%sFlags;'):format(opt_name))
+
+ return table.concat(out, '\n')
+end
+
+--- @param output_file string
+--- @return table<string,string> options_index Map of option name to option index
+local function gen_enums(output_file)
+ --- Options for each scope.
+ --- @type table<string, vim.option_meta[]>
+ local scope_options = {}
+ for _, scope in ipairs(valid_scopes) do
+ scope_options[scope] = {}
+ end
+
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param s string
+ local function write(s)
+ fd:write(s)
+ fd:write('\n')
+ end
+
+ -- Generate options enum file
+ write('// IWYU pragma: private, include "nvim/option_defs.h"')
+ write('')
+
+ --- Map of option name to option index
+ --- @type table<string, string>
+ local option_index = {}
+
+ -- Generate option index enum and populate the `option_index` and `scope_option` dicts.
+ write('typedef enum {')
+ write(' kOptInvalid = -1,')
+
+ for i, o in ipairs(options_meta) do
+ local enum_val_name = 'kOpt' .. lowercase_to_titlecase(o.full_name)
+ write((' %s = %u,'):format(enum_val_name, i - 1))
+
+ option_index[o.full_name] = enum_val_name
+
+ if o.abbreviation then
+ option_index[o.abbreviation] = enum_val_name
+ end
+
+ local alias = o.alias or {} --[[@as string[] ]]
+ for _, v in ipairs(alias) do
+ option_index[v] = enum_val_name
+ end
+
+ for _, scope in ipairs(o.scope) do
+ table.insert(scope_options[scope], o)
+ end
+ end
+
+ write(' // Option count')
+ write('#define kOptCount ' .. tostring(#options_meta))
+ write('} OptIndex;')
+
+ -- Generate option index enum for each scope
+ for _, scope in ipairs(valid_scopes) do
+ write('')
+
+ local scope_name = lowercase_to_titlecase(scope)
+ write('typedef enum {')
+ write((' %s = -1,'):format(get_scope_option(scope, 'Invalid')))
+
+ for idx, option in ipairs(scope_options[scope]) do
+ write((' %s = %u,'):format(get_scope_option(scope, option.full_name), idx - 1))
+ end
+
+ write((' // %s option count'):format(scope_name))
+ write(('#define %s %d'):format(get_scope_option(scope, 'Count'), #scope_options[scope]))
+ write(('} %sOptIndex;'):format(scope_name))
end
- w(' },')
+
+ -- Generate reverse lookup from option scope index to option index for each scope.
+ for _, scope in ipairs(valid_scopes) do
+ write('')
+ write(('EXTERN const OptIndex %s_opt_idx[] INIT( = {'):format(scope))
+ for _, option in ipairs(scope_options[scope]) do
+ local idx = option_index[option.full_name]
+ write((' [%s] = %s,'):format(get_scope_option(scope, option.full_name), idx))
+ end
+ write('});')
+ end
+
+ fd:close()
+
+ return option_index
end
--- Generate options[] array.
-w([[
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/insexpand.h"
-#include "nvim/mapping.h"
-#include "nvim/ops.h"
-#include "nvim/option.h"
-#include "nvim/optionstr.h"
-#include "nvim/quickfix.h"
-#include "nvim/runtime.h"
-#include "nvim/tag.h"
-#include "nvim/window.h"
-
-static vimoption_T options[] = {]])
-for i, o in ipairs(options.options) do
- dump_option(i, o)
+--- @param output_file string
+--- @param option_index table<string,string>
+local function gen_map(output_file, option_index)
+ -- Generate option index map.
+ local hashy = require('generators.hashy')
+
+ local neworder, hashfun = hashy.hashy_hash(
+ 'find_option',
+ vim.tbl_keys(option_index),
+ function(idx)
+ return ('option_hash_elems[%s].name'):format(idx)
+ end
+ )
+
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param s string
+ local function write(s)
+ fd:write(s)
+ fd:write('\n')
+ end
+
+ write('static const struct { const char *name; OptIndex opt_idx; } option_hash_elems[] = {')
+
+ for _, name in ipairs(neworder) do
+ assert(option_index[name] ~= nil)
+ write((' { .name = "%s", .opt_idx = %s },'):format(name, option_index[name]))
+ end
+
+ write('};')
+ write('')
+ write('static ' .. hashfun)
+
+ fd:close()
end
-w('};')
+
+--- @param output_file string
+local function gen_vars(output_file)
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param s string
+ local function write(s)
+ fd:write(s)
+ fd:write('\n')
+ end
+
+ write('// IWYU pragma: private, include "nvim/option_vars.h"')
+
+ -- Generate enums for option flags.
+ for _, o in ipairs(options_meta) do
+ if o.flags and (type(o.flags) == 'table' or o.values) then
+ write(gen_opt_enum(o))
+ end
+ end
+
+ -- Generate valid values for each option.
+ for _, option in ipairs(options_meta) do
+ -- Since option values can be nested, we need to do preorder traversal to generate the values.
+ if option.values then
+ local values_var = ('opt_%s'):format(option.abbreviation or option.full_name)
+ write(preorder_traversal(values_var, option.values))
+ end
+ end
+
+ fd:close()
+end
+
+--- @param output_file string
+local function gen_options(output_file)
+ local fd = assert(io.open(output_file, 'w'))
+
+ --- @param ... string
+ local function write(...)
+ local s = table.concat({ ... }, '')
+ fd:write(s)
+ if s:match('^ %.') then
+ fd:write(',')
+ end
+ fd:write('\n')
+ end
+
+ -- Generate options[] array.
+ write([[
+ #include "nvim/ex_docmd.h"
+ #include "nvim/ex_getln.h"
+ #include "nvim/insexpand.h"
+ #include "nvim/mapping.h"
+ #include "nvim/ops.h"
+ #include "nvim/option.h"
+ #include "nvim/optionstr.h"
+ #include "nvim/quickfix.h"
+ #include "nvim/runtime.h"
+ #include "nvim/tag.h"
+ #include "nvim/window.h"
+
+ static vimoption_T options[] = {]])
+
+ for i, o in ipairs(options_meta) do
+ dump_option(i, o, write)
+ end
+
+ write('};')
+
+ fd:close()
+end
+
+local function main()
+ local options_file = arg[1]
+ local options_enum_file = arg[2]
+ local options_map_file = arg[3]
+ local option_vars_file = arg[4]
+
+ local option_index = gen_enums(options_enum_file)
+ gen_map(options_map_file, option_index)
+ gen_vars(option_vars_file)
+ gen_options(options_file)
+end
+
+main()
diff --git a/src/nvim/generators/gen_vimvim.lua b/src/nvim/generators/gen_vimvim.lua
index 0675f04b73..d8053822bf 100644
--- a/src/nvim/generators/gen_vimvim.lua
+++ b/src/nvim/generators/gen_vimvim.lua
@@ -52,11 +52,13 @@ local function is_special_cased_cmd(cmd)
end
local vimcmd_start = 'syn keyword vimCommand contained '
+local vimcmd_end = ' nextgroup=vimBang'
w(vimcmd_start)
+
local prev_cmd = nil
for _, cmd_desc in ipairs(ex_cmds.cmds) do
if lld.line_length > 850 then
- w('\n' .. vimcmd_start)
+ w(vimcmd_end .. '\n' .. vimcmd_start)
end
local cmd = cmd_desc.command
if cmd:match('%w') and cmd ~= 'z' and not is_special_cased_cmd(cmd) then
@@ -79,9 +81,11 @@ for _, cmd_desc in ipairs(ex_cmds.cmds) do
prev_cmd = cmd
end
+w(vimcmd_end .. '\n')
+
local vimopt_start = 'syn keyword vimOption contained '
local vimopt_end = ' skipwhite nextgroup=vimSetEqual,vimSetMod'
-w('\n\n' .. vimopt_start)
+w('\n' .. vimopt_start)
for _, opt_desc in ipairs(options.options) do
if not opt_desc.immutable then
diff --git a/src/nvim/generators/hashy.lua b/src/nvim/generators/hashy.lua
index ea35064962..74b7655324 100644
--- a/src/nvim/generators/hashy.lua
+++ b/src/nvim/generators/hashy.lua
@@ -55,7 +55,7 @@ function M.build_pos_hash(strings)
end
function M.switcher(put, tab, maxlen, worst_buck_size)
- local neworder = {}
+ local neworder = {} --- @type string[]
put ' switch (len) {\n'
local bucky = worst_buck_size > 1
for len = 1, maxlen do
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 82abd2888a..0817d40bb8 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2,7 +2,6 @@
// file, manipulations with redo buffer and stuff buffer.
#include <assert.h>
-#include <lauxlib.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
@@ -11,6 +10,7 @@
#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/vim.h"
@@ -29,6 +29,7 @@
#include "nvim/ex_cmds.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
+#include "nvim/ex_getln_defs.h"
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
@@ -38,6 +39,7 @@
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/mapping.h"
#include "nvim/mapping_defs.h"
@@ -45,6 +47,7 @@
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -53,6 +56,7 @@
#include "nvim/ops.h"
#include "nvim/option_vars.h"
#include "nvim/os/fileio.h"
+#include "nvim/os/fileio_defs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
@@ -471,7 +475,7 @@ void beep_flush(void)
{
if (emsg_silent == 0) {
flush_buffers(FLUSH_MINIMAL);
- vim_beep(BO_ERROR);
+ vim_beep(kOptBoFlagError);
}
}
@@ -1513,7 +1517,7 @@ int merge_modifiers(int c_arg, int *modifiers)
int c = c_arg;
if (*modifiers & MOD_MASK_CTRL) {
- if ((c >= '`' && c <= 0x7f) || (c >= '@' && c <= '_')) {
+ if (c >= '@' && c <= 0x7f) {
c &= 0x1f;
if (c == NUL) {
c = K_ZERO;
@@ -1862,29 +1866,73 @@ bool char_avail(void)
return retval != NUL;
}
+static int no_reduce_keys = 0; ///< Do not apply modifiers to the key.
+
/// "getchar()" and "getcharstr()" functions
-static void getchar_common(typval_T *argvars, typval_T *rettv)
+static void getchar_common(typval_T *argvars, typval_T *rettv, bool allow_number)
FUNC_ATTR_NONNULL_ALL
{
- varnumber_T n;
+ varnumber_T n = 0;
+ const int called_emsg_start = called_emsg;
bool error = false;
+ bool simplify = true;
+ char cursor_flag = NUL;
+
+ if (argvars[0].v_type != VAR_UNKNOWN
+ && tv_check_for_opt_dict_arg(argvars, 1) == FAIL) {
+ return;
+ }
+
+ if (argvars[0].v_type != VAR_UNKNOWN && argvars[1].v_type == VAR_DICT) {
+ dict_T *d = argvars[1].vval.v_dict;
+
+ if (allow_number) {
+ allow_number = tv_dict_get_bool(d, "number", true);
+ } else if (tv_dict_has_key(d, "number")) {
+ semsg(_(e_invarg2), "number");
+ }
+
+ simplify = tv_dict_get_bool(d, "simplify", true);
+
+ const char *cursor_str = tv_dict_get_string(d, "cursor", false);
+ if (cursor_str != NULL) {
+ if (strcmp(cursor_str, "hide") != 0
+ && strcmp(cursor_str, "keep") != 0
+ && strcmp(cursor_str, "msg") != 0) {
+ semsg(_(e_invargNval), "cursor", cursor_str);
+ } else {
+ cursor_flag = cursor_str[0];
+ }
+ }
+ }
+
+ if (called_emsg != called_emsg_start) {
+ return;
+ }
+
+ if (cursor_flag == 'h') {
+ ui_busy_start();
+ }
no_mapping++;
allow_keys++;
+ if (!simplify) {
+ no_reduce_keys++;
+ }
while (true) {
- if (msg_col > 0) {
- // Position the cursor. Needed after a message that ends in a space.
+ if (cursor_flag == 'm' || (cursor_flag == NUL && msg_col > 0)) {
ui_cursor_goto(msg_row, msg_col);
}
- if (argvars[0].v_type == VAR_UNKNOWN) {
+ if (argvars[0].v_type == VAR_UNKNOWN
+ || (argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number == -1)) {
// getchar(): blocking wait.
// TODO(bfredl): deduplicate shared logic with state_enter ?
if (!char_avail()) {
// Flush screen updates before blocking.
ui_flush();
input_get(NULL, 0, -1, typebuf.tb_change_cnt, main_loop.events);
- if (!multiqueue_empty(main_loop.events)) {
+ if (!input_available() && !multiqueue_empty(main_loop.events)) {
state_handle_k_event();
continue;
}
@@ -1912,14 +1960,20 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
}
no_mapping--;
allow_keys--;
+ if (!simplify) {
+ no_reduce_keys--;
+ }
+
+ if (cursor_flag == 'h') {
+ ui_busy_stop();
+ }
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)) {
+ if (n != 0 && (!allow_number || IS_SPECIAL(n) || mod_mask != 0)) {
char temp[10]; // modifier: 3, mbyte-char: 6, NUL: 1
int i = 0;
@@ -1966,35 +2020,23 @@ static void getchar_common(typval_T *argvars, typval_T *rettv)
set_vim_var_nr(VV_MOUSE_COL, col + 1);
}
}
+ } else if (!allow_number) {
+ rettv->v_type = VAR_STRING;
+ } else {
+ rettv->vval.v_number = n;
}
}
/// "getchar()" function
void f_getchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- getchar_common(argvars, rettv);
+ getchar_common(argvars, rettv, true);
}
/// "getcharstr()" function
void f_getcharstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- getchar_common(argvars, rettv);
-
- if (rettv->v_type != VAR_NUMBER) {
- return;
- }
-
- 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, temp);
- }
- assert(i < 7);
- temp[i] = NUL;
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = xmemdupz(temp, (size_t)i);
+ getchar_common(argvars, rettv, false);
}
/// "getcharmod()" function
@@ -2052,6 +2094,12 @@ static bool at_ins_compl_key(void)
/// @return the length of the replaced bytes, 0 if nothing changed, -1 for error.
static int check_simplify_modifier(int max_offset)
{
+ // We want full modifiers in Terminal mode so that the key can be correctly
+ // encoded
+ if ((State & MODE_TERMINAL) || no_reduce_keys > 0) {
+ return 0;
+ }
+
for (int offset = 0; offset < max_offset; offset++) {
if (offset + 3 >= typebuf.tb_len) {
break;
diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h
index 4e962c9b03..766c5eb2a4 100644
--- a/src/nvim/getchar.h
+++ b/src/nvim/getchar.h
@@ -5,7 +5,6 @@
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/getchar_defs.h" // IWYU pragma: keep
-#include "nvim/os/fileio_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
/// Argument for flush_buffers().
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 472b77ccbe..a473180349 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -405,9 +405,6 @@ EXTERN buf_T *curbuf INIT( = NULL); // currently active buffer
#define FOR_ALL_BUFFERS_BACKWARDS(buf) \
for (buf_T *buf = lastbuf; buf != NULL; buf = buf->b_prev)
-#define FOR_ALL_BUF_WININFO(buf, wip) \
- for ((wip) = (buf)->b_wininfo; (wip) != NULL; (wip) = (wip)->wi_next)
-
// List of files being edited (global argument list). curwin->w_alist points
// to this when the window is using the global argument list.
EXTERN alist_T global_alist; // global argument list
diff --git a/src/nvim/grid.c b/src/nvim/grid.c
index acb336c725..df93ad1655 100644
--- a/src/nvim/grid.c
+++ b/src/nvim/grid.c
@@ -383,7 +383,8 @@ void grid_line_start(ScreenGrid *grid, int row)
assert((size_t)grid_line_maxcol <= linebuf_size);
- if (rdb_flags & RDB_INVALID) {
+ if (full_screen && (rdb_flags & kOptRdbFlagInvalid)) {
+ assert(linebuf_char);
// Current batch must not depend on previous contents of linebuf_char.
// Set invalid values which will cause assertion failures later if they are used.
memset(linebuf_char, 0xFF, sizeof(schar_T) * linebuf_size);
@@ -602,7 +603,7 @@ void grid_line_flush(void)
void grid_line_flush_if_valid_row(void)
{
if (grid_line_row < 0 || grid_line_row >= grid_line_grid->rows) {
- if (rdb_flags & RDB_INVALID) {
+ if (rdb_flags & kOptRdbFlagInvalid) {
abort();
} else {
grid_line_grid = NULL;
@@ -639,7 +640,7 @@ static int grid_char_needs_redraw(ScreenGrid *grid, int col, size_t off_to, int
|| (cols > 1 && linebuf_char[col + 1] == 0
&& linebuf_char[col + 1] != grid->chars[off_to + 1]))
|| exmode_active // TODO(bfredl): what in the actual fuck
- || rdb_flags & RDB_NODELTA));
+ || rdb_flags & kOptRdbFlagNodelta));
}
/// Move one buffered line to the window grid, but only the characters that
@@ -784,7 +785,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int col, int endcol
size_t off = off_to + (size_t)col;
if (grid->chars[off] != schar_from_ascii(' ')
|| grid->attrs[off] != bg_attr
- || rdb_flags & RDB_NODELTA) {
+ || rdb_flags & kOptRdbFlagNodelta) {
grid->chars[off] = schar_from_ascii(' ');
grid->attrs[off] = bg_attr;
if (clear_dirty_start == -1) {
diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c
index 95df5e5383..4d09fc10db 100644
--- a/src/nvim/hashtab.c
+++ b/src/nvim/hashtab.c
@@ -26,6 +26,7 @@
#include "nvim/ascii_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/hashtab.h"
+#include "nvim/macros_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c
index 5f0a2e0e4e..1a4b211f9c 100644
--- a/src/nvim/highlight.c
+++ b/src/nvim/highlight.c
@@ -1075,10 +1075,10 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
return hlattrs;
}
if (HAS_KEY_X(dict, global_link)) {
- *link_id = object_to_hl_id(dict->global_link, "link", err);
+ *link_id = (int)dict->global_link;
mask |= HL_GLOBAL;
} else {
- *link_id = object_to_hl_id(dict->link, "link", err);
+ *link_id = (int)dict->link;
}
if (ERROR_SET(err)) {
@@ -1100,6 +1100,9 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
CHECK_FLAG(cterm, cterm_mask, italic, , HL_ITALIC);
CHECK_FLAG(cterm, cterm_mask, underline, , HL_UNDERLINE);
CHECK_FLAG(cterm, cterm_mask, undercurl, , HL_UNDERCURL);
+ CHECK_FLAG(cterm, cterm_mask, underdouble, , HL_UNDERDOUBLE);
+ CHECK_FLAG(cterm, cterm_mask, underdotted, , HL_UNDERDOTTED);
+ CHECK_FLAG(cterm, cterm_mask, underdashed, , HL_UNDERDASHED);
CHECK_FLAG(cterm, cterm_mask, standout, , HL_STANDOUT);
CHECK_FLAG(cterm, cterm_mask, strikethrough, , HL_STRIKETHROUGH);
CHECK_FLAG(cterm, cterm_mask, altfont, , HL_ALTFONT);
diff --git a/src/nvim/highlight.h b/src/nvim/highlight.h
index 1be70100de..a89d778474 100644
--- a/src/nvim/highlight.h
+++ b/src/nvim/highlight.h
@@ -15,7 +15,6 @@ EXTERN const char *hlf_names[] INIT( = {
[HLF_8] = "SpecialKey",
[HLF_EOB] = "EndOfBuffer",
[HLF_TERM] = "TermCursor",
- [HLF_TERMNC] = "TermCursorNC",
[HLF_AT] = "NonText",
[HLF_D] = "Directory",
[HLF_E] = "ErrorMsg",
diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h
index a3c4062714..cbbc28311f 100644
--- a/src/nvim/highlight_defs.h
+++ b/src/nvim/highlight_defs.h
@@ -63,7 +63,6 @@ typedef enum {
///< displayed different from what it is
HLF_EOB, ///< after the last line in the buffer
HLF_TERM, ///< terminal cursor focused
- HLF_TERMNC, ///< terminal cursor unfocused
HLF_AT, ///< @ characters at end of screen, characters that don't really exist in the text
HLF_D, ///< directories in CTRL-D listing
HLF_E, ///< error messages
diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c
index b3c4aca1af..901d2c84bc 100644
--- a/src/nvim/highlight_group.c
+++ b/src/nvim/highlight_group.c
@@ -1,5 +1,6 @@
// highlight_group.c: code for managing highlight groups
+#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
@@ -31,6 +32,7 @@
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_group.h"
#include "nvim/lua/executor.h"
@@ -148,7 +150,7 @@ static const char *highlight_init_both[] = {
"PmenuMatchSel gui=bold cterm=bold",
"PmenuSel gui=reverse cterm=reverse,underline blend=0",
"RedrawDebugNormal gui=reverse cterm=reverse",
- "TabLineSel gui=bold cterm=bold",
+ "TabLineSel gui=bold cterm=NONE",
"TermCursor gui=reverse cterm=reverse",
"Underlined gui=underline cterm=underline",
"lCursor guifg=bg guibg=fg",
@@ -173,12 +175,12 @@ static const char *highlight_init_both[] = {
"default link PmenuKind Pmenu",
"default link PmenuKindSel PmenuSel",
"default link PmenuSbar Pmenu",
+ "default link ComplMatchIns NONE",
"default link Substitute Search",
"default link StatusLineTerm StatusLine",
"default link StatusLineTermNC StatusLineNC",
"default link TabLine StatusLineNC",
"default link TabLineFill TabLine",
- "default link TermCursorNC NONE",
"default link VertSplit WinSeparator",
"default link VisualNOS Visual",
"default link Whitespace NonText",
@@ -230,6 +232,11 @@ static const char *highlight_init_both[] = {
"default link DiagnosticVirtualTextInfo DiagnosticInfo",
"default link DiagnosticVirtualTextHint DiagnosticHint",
"default link DiagnosticVirtualTextOk DiagnosticOk",
+ "default link DiagnosticVirtualLinesError DiagnosticError",
+ "default link DiagnosticVirtualLinesWarn DiagnosticWarn",
+ "default link DiagnosticVirtualLinesInfo DiagnosticInfo",
+ "default link DiagnosticVirtualLinesHint DiagnosticHint",
+ "default link DiagnosticVirtualLinesOk DiagnosticOk",
"default link DiagnosticSignError DiagnosticError",
"default link DiagnosticSignWarn DiagnosticWarn",
"default link DiagnosticSignInfo DiagnosticInfo",
@@ -303,7 +310,7 @@ static const char *highlight_init_both[] = {
"default link @tag.builtin Special",
// :help
- // Higlight "===" and "---" heading delimiters specially.
+ // Highlight "===" and "---" heading delimiters specially.
"default @markup.heading.1.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underdouble,nocombine ctermbg=NONE ctermfg=NONE cterm=underdouble,nocombine",
"default @markup.heading.2.delimiter.vimdoc guibg=bg guifg=bg guisp=fg gui=underline,nocombine ctermbg=NONE ctermfg=NONE cterm=underline,nocombine",
@@ -357,7 +364,7 @@ static const char *highlight_init_light[] = {
"ErrorMsg guifg=NvimDarkRed ctermfg=1",
"FloatShadow guibg=NvimLightGrey4 ctermbg=0 blend=80",
"FloatShadowThrough guibg=NvimLightGrey4 ctermbg=0 blend=100",
- "Folded guifg=NvimDarkGrey4 guibg=NvimLightGrey3",
+ "Folded guifg=NvimDarkGrey4 guibg=NvimLightGrey1",
"LineNr guifg=NvimLightGrey4",
"MatchParen guibg=NvimLightGrey4 gui=bold cterm=bold,underline",
"ModeMsg guifg=NvimDarkGreen ctermfg=2",
@@ -380,7 +387,7 @@ static const char *highlight_init_light[] = {
"SpellLocal guisp=NvimDarkGreen gui=undercurl cterm=undercurl",
"SpellRare guisp=NvimDarkCyan gui=undercurl cterm=undercurl",
"StatusLine guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=reverse",
- "StatusLineNC guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=bold,underline",
+ "StatusLineNC guifg=NvimDarkGrey2 guibg=NvimLightGrey4 cterm=bold,underline",
"Title guifg=NvimDarkGrey2 gui=bold cterm=bold",
"Visual guibg=NvimLightGrey4 ctermfg=15 ctermbg=0",
"WarningMsg guifg=NvimDarkYellow ctermfg=3",
@@ -441,7 +448,7 @@ static const char *highlight_init_dark[] = {
"ErrorMsg guifg=NvimLightRed ctermfg=9",
"FloatShadow guibg=NvimDarkGrey4 ctermbg=0 blend=80",
"FloatShadowThrough guibg=NvimDarkGrey4 ctermbg=0 blend=100",
- "Folded guifg=NvimLightGrey4 guibg=NvimDarkGrey3",
+ "Folded guifg=NvimLightGrey4 guibg=NvimDarkGrey1",
"LineNr guifg=NvimDarkGrey4",
"MatchParen guibg=NvimDarkGrey4 gui=bold cterm=bold,underline",
"ModeMsg guifg=NvimLightGreen ctermfg=10",
@@ -464,7 +471,7 @@ static const char *highlight_init_dark[] = {
"SpellLocal guisp=NvimLightGreen gui=undercurl cterm=undercurl",
"SpellRare guisp=NvimLightCyan gui=undercurl cterm=undercurl",
"StatusLine guifg=NvimDarkGrey3 guibg=NvimLightGrey3 cterm=reverse",
- "StatusLineNC guifg=NvimLightGrey3 guibg=NvimDarkGrey3 cterm=bold,underline",
+ "StatusLineNC guifg=NvimLightGrey2 guibg=NvimDarkGrey4 cterm=bold,underline",
"Title guifg=NvimLightGrey2 gui=bold cterm=bold",
"Visual guibg=NvimDarkGrey4 ctermfg=0 ctermbg=15",
"WarningMsg guifg=NvimLightYellow ctermfg=11",
@@ -1001,6 +1008,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
{
// If no argument, list current highlighting.
if (!init && ends_excmd((uint8_t)(*line))) {
+ msg_ext_set_kind("list_cmd");
for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) {
// TODO(brammool): only call when the group has attributes set
highlight_list_one(i);
@@ -1038,6 +1046,7 @@ void do_highlight(const char *line, const bool forceit, const bool init)
if (id == 0) {
semsg(_(e_highlight_group_name_not_found_str), line);
} else {
+ msg_ext_set_kind("list_cmd");
highlight_list_one(id);
}
return;
@@ -1885,8 +1894,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool
if (got_int) {
return true;
}
- msg_outtrans(hl_table[id - 1].sg_name, 0, false);
- name_col = msg_col;
+ msg_col = name_col = msg_outtrans(hl_table[id - 1].sg_name, 0, false);
endcol = 15;
} else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) {
msg_putchar(' ');
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index e487728901..170346ec0d 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -34,7 +34,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/plines.h"
#include "nvim/pos_defs.h"
@@ -793,7 +792,7 @@ bool briopt_check(char *briopt, win_T *wp)
}
while (*p != NUL) {
- // Note: Keep this in sync with p_briopt_values
+ // Note: Keep this in sync with opt_briopt_values.
if (strncmp(p, "shift:", 6) == 0
&& ((p[6] == '-' && ascii_isdigit(p[7])) || ascii_isdigit(p[6]))) {
p += 6;
@@ -872,7 +871,7 @@ int get_breakindent_win(win_T *wp, char *line)
|| prev_tick != buf_get_changedtick(wp->w_buffer)
|| prev_listopt != wp->w_briopt_list
|| prev_no_ts != no_ts
- || prev_dy_uhex != (dy_flags & DY_UHEX)
+ || prev_dy_uhex != (dy_flags & kOptDyFlagUhex)
|| prev_flp == NULL
|| strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0
|| prev_line == NULL || strcmp(prev_line, line) != 0) {
@@ -893,7 +892,7 @@ int get_breakindent_win(win_T *wp, char *line)
prev_listopt = wp->w_briopt_list;
prev_list = 0;
prev_no_ts = no_ts;
- prev_dy_uhex = (dy_flags & DY_UHEX);
+ prev_dy_uhex = (dy_flags & kOptDyFlagUhex);
xfree(prev_flp);
prev_flp = xstrdup(get_flp_value(wp->w_buffer));
// add additional indent for numbered lists
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
index 98b0d6003a..c9b7a1ba3f 100644
--- a/src/nvim/indent_c.c
+++ b/src/nvim/indent_c.c
@@ -14,6 +14,7 @@
#include "nvim/indent_c.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
+#include "nvim/math.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/option.h"
@@ -1689,7 +1690,7 @@ void parse_cino(buf_T *buf)
p++;
}
char *digits_start = p; // remember where the digits start
- int n = getdigits_int(&p, true, 0);
+ int64_t n = getdigits_int(&p, true, 0);
divider = 0;
if (*p == '.') { // ".5s" means a fraction.
fraction = atoi(++p);
@@ -1708,7 +1709,7 @@ void parse_cino(buf_T *buf)
} else {
n *= sw;
if (divider) {
- n += (sw * fraction + divider / 2) / divider;
+ n += ((int64_t)sw * fraction + divider / 2) / divider;
}
}
p++;
@@ -1717,119 +1718,121 @@ void parse_cino(buf_T *buf)
n = -n;
}
+ n = trim_to_int(n);
+
// When adding an entry here, also update the default 'cinoptions' in
// doc/indent.txt, and add explanation for it!
switch (*l) {
case '>':
- buf->b_ind_level = n;
+ buf->b_ind_level = (int)n;
break;
case 'e':
- buf->b_ind_open_imag = n;
+ buf->b_ind_open_imag = (int)n;
break;
case 'n':
- buf->b_ind_no_brace = n;
+ buf->b_ind_no_brace = (int)n;
break;
case 'f':
- buf->b_ind_first_open = n;
+ buf->b_ind_first_open = (int)n;
break;
case '{':
- buf->b_ind_open_extra = n;
+ buf->b_ind_open_extra = (int)n;
break;
case '}':
- buf->b_ind_close_extra = n;
+ buf->b_ind_close_extra = (int)n;
break;
case '^':
- buf->b_ind_open_left_imag = n;
+ buf->b_ind_open_left_imag = (int)n;
break;
case 'L':
- buf->b_ind_jump_label = n;
+ buf->b_ind_jump_label = (int)n;
break;
case ':':
- buf->b_ind_case = n;
+ buf->b_ind_case = (int)n;
break;
case '=':
- buf->b_ind_case_code = n;
+ buf->b_ind_case_code = (int)n;
break;
case 'b':
- buf->b_ind_case_break = n;
+ buf->b_ind_case_break = (int)n;
break;
case 'p':
- buf->b_ind_param = n;
+ buf->b_ind_param = (int)n;
break;
case 't':
- buf->b_ind_func_type = n;
+ buf->b_ind_func_type = (int)n;
break;
case '/':
- buf->b_ind_comment = n;
+ buf->b_ind_comment = (int)n;
break;
case 'c':
- buf->b_ind_in_comment = n;
+ buf->b_ind_in_comment = (int)n;
break;
case 'C':
- buf->b_ind_in_comment2 = n;
+ buf->b_ind_in_comment2 = (int)n;
break;
case 'i':
- buf->b_ind_cpp_baseclass = n;
+ buf->b_ind_cpp_baseclass = (int)n;
break;
case '+':
- buf->b_ind_continuation = n;
+ buf->b_ind_continuation = (int)n;
break;
case '(':
- buf->b_ind_unclosed = n;
+ buf->b_ind_unclosed = (int)n;
break;
case 'u':
- buf->b_ind_unclosed2 = n;
+ buf->b_ind_unclosed2 = (int)n;
break;
case 'U':
- buf->b_ind_unclosed_noignore = n;
+ buf->b_ind_unclosed_noignore = (int)n;
break;
case 'W':
- buf->b_ind_unclosed_wrapped = n;
+ buf->b_ind_unclosed_wrapped = (int)n;
break;
case 'w':
- buf->b_ind_unclosed_whiteok = n;
+ buf->b_ind_unclosed_whiteok = (int)n;
break;
case 'm':
- buf->b_ind_matching_paren = n;
+ buf->b_ind_matching_paren = (int)n;
break;
case 'M':
- buf->b_ind_paren_prev = n;
+ buf->b_ind_paren_prev = (int)n;
break;
case ')':
- buf->b_ind_maxparen = n;
+ buf->b_ind_maxparen = (int)n;
break;
case '*':
- buf->b_ind_maxcomment = n;
+ buf->b_ind_maxcomment = (int)n;
break;
case 'g':
- buf->b_ind_scopedecl = n;
+ buf->b_ind_scopedecl = (int)n;
break;
case 'h':
- buf->b_ind_scopedecl_code = n;
+ buf->b_ind_scopedecl_code = (int)n;
break;
case 'j':
- buf->b_ind_java = n;
+ buf->b_ind_java = (int)n;
break;
case 'J':
- buf->b_ind_js = n;
+ buf->b_ind_js = (int)n;
break;
case 'l':
- buf->b_ind_keep_case_label = n;
+ buf->b_ind_keep_case_label = (int)n;
break;
case '#':
- buf->b_ind_hash_comment = n;
+ buf->b_ind_hash_comment = (int)n;
break;
case 'N':
- buf->b_ind_cpp_namespace = n;
+ buf->b_ind_cpp_namespace = (int)n;
break;
case 'k':
- buf->b_ind_if_for_while = n;
+ buf->b_ind_if_for_while = (int)n;
break;
case 'E':
- buf->b_ind_cpp_extern_c = n;
+ buf->b_ind_cpp_extern_c = (int)n;
break;
case 'P':
- buf->b_ind_pragma = n;
+ buf->b_ind_pragma = (int)n;
break;
}
if (*p == ',') {
diff --git a/src/nvim/input.c b/src/nvim/input.c
index 3d3240c59f..fb89d86a75 100644
--- a/src/nvim/input.c
+++ b/src/nvim/input.c
@@ -1,16 +1,15 @@
// input.c: high level functions for prompting the user or input
// like yes/no or number prompts.
-#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "nvim/ascii_defs.h"
+#include "nvim/ex_getln.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/keycodes.h"
@@ -34,44 +33,36 @@
/// No other characters are accepted, the message is repeated until a valid
/// reply is entered or <C-c> is hit.
///
-/// @param[in] str Prompt: question to ask user. Is always followed by
-/// " (y/n)?".
-/// @param[in] direct Determines what function to use to get user input. If
-/// true then input_get() will be used, otherwise vgetc().
-/// I.e. when direct is true then characters are obtained
-/// directly from the user without buffers involved.
+/// @param[in] str Prompt: question to ask user. Is always followed by " (y/n)?".
///
/// @return 'y' or 'n'. Last is also what will be returned in case of interrupt.
-int ask_yesno(const char *const str, const bool direct)
+int ask_yesno(const char *const str)
{
const int save_State = State;
no_wait_return++;
State = MODE_CONFIRM; // Mouse behaves like with :confirm.
setmouse(); // Disable mouse in xterm.
- no_mapping++;
- allow_keys++; // no mapping here, but recognize keys
+ snprintf(IObuff, IOSIZE, _("%s (y/n)?"), str);
+ char *prompt = xstrdup(IObuff);
int r = ' ';
while (r != 'y' && r != 'n') {
// same highlighting as for wait_return()
- smsg(HLF_R, "%s (y/n)?", str);
- if (direct) {
- r = get_keystroke(NULL);
- } else {
- r = plain_vgetc();
- }
+ r = prompt_for_input(prompt, HLF_R, true, NULL);
if (r == Ctrl_C || r == ESC) {
r = 'n';
+ if (!ui_has(kUIMessages)) {
+ msg_putchar(r);
+ }
}
- msg_putchar(r); // Show what you typed.
- ui_flush();
}
+
+ need_wait_return = msg_scrolled;
no_wait_return--;
State = save_State;
setmouse();
- no_mapping--;
- allow_keys--;
+ xfree(prompt);
return r;
}
@@ -157,104 +148,42 @@ int get_keystroke(MultiQueue *events)
return n;
}
-/// Get a number from the user.
-/// When "mouse_used" is not NULL allow using the mouse.
+/// Ask the user for input through a cmdline prompt.
///
-/// @param colon allow colon to abort
-int get_number(int colon, bool *mouse_used)
+/// @param one_key Return from cmdline after one key press.
+/// @param mouse_used When not NULL, allow using the mouse to press a number.
+int prompt_for_input(char *prompt, int hl_id, bool one_key, bool *mouse_used)
{
- int n = 0;
- int typed = 0;
+ int ret = one_key ? ESC : 0;
+ char *kmsg = keep_msg ? xstrdup(keep_msg) : NULL;
- if (mouse_used != NULL) {
- *mouse_used = false;
+ if (prompt == NULL) {
+ if (mouse_used != NULL) {
+ prompt = _("Type number and <Enter> or click with the mouse (q or empty cancels): ");
+ } else {
+ prompt = _("Type number and <Enter> (q or empty cancels): ");
+ }
}
- // When not printing messages, the user won't know what to type, return a
- // zero (as if CR was hit).
- if (msg_silent != 0) {
- return 0;
- }
+ cmdline_row = msg_row;
+ ui_flush();
- no_mapping++;
- allow_keys++; // no mapping here, but recognize keys
- while (true) {
- ui_cursor_goto(msg_row, msg_col);
- int c = safe_vgetc();
- if (ascii_isdigit(c)) {
- if (vim_append_digit_int(&n, c - '0') == FAIL) {
- return 0;
- }
- msg_putchar(c);
- typed++;
- } else if (c == K_DEL || c == K_KDEL || c == K_BS || c == Ctrl_H) {
- if (typed > 0) {
- msg_puts("\b \b");
- typed--;
- }
- n /= 10;
- } else if (mouse_used != NULL && c == K_LEFTMOUSE) {
- *mouse_used = true;
- n = mouse_row + 1;
- break;
- } else if (n == 0 && c == ':' && colon) {
- stuffcharReadbuff(':');
- if (!exmode_active) {
- cmdline_row = msg_row;
- }
- skip_redraw = true; // skip redraw once
- do_redraw = false;
- break;
- } else if (c == Ctrl_C || c == ESC || c == 'q') {
- n = 0;
- break;
- } else if (c == CAR || c == NL) {
- break;
- }
- }
- no_mapping--;
+ no_mapping++; // don't map prompt input
+ allow_keys++; // allow special keys
+ char *resp = getcmdline_prompt(-1, prompt, hl_id, EXPAND_NOTHING, NULL,
+ CALLBACK_NONE, one_key, mouse_used);
allow_keys--;
- return n;
-}
+ no_mapping--;
-/// Ask the user to enter a number.
-///
-/// When "mouse_used" is not NULL allow using the mouse and in that case return
-/// the line number.
-int prompt_for_number(bool *mouse_used)
-{
- // When using ":silent" assume that <CR> was entered.
- if (mouse_used != NULL) {
- msg_puts(_("Type number and <Enter> or click with the mouse "
- "(q or empty cancels): "));
- } else {
- msg_puts(_("Type number and <Enter> (q or empty cancels): "));
+ if (resp) {
+ ret = one_key ? (int)(*resp) : atoi(resp);
+ xfree(resp);
}
- // Set the state such that text can be selected/copied/pasted and we still
- // get mouse events.
- int save_cmdline_row = cmdline_row;
- cmdline_row = 0;
- int save_State = State;
- State = MODE_ASKMORE; // prevents a screen update when using a timer
- // May show different mouse shape.
- setmouse();
-
- int i = get_number(true, mouse_used);
- if (KeyTyped) {
- // don't call wait_return() now
- if (msg_row > 0) {
- cmdline_row = msg_row - 1;
- }
- need_wait_return = false;
- msg_didany = false;
- msg_didout = false;
- } else {
- cmdline_row = save_cmdline_row;
+ if (kmsg != NULL) {
+ set_keep_msg(kmsg, keep_msg_hl_id);
+ xfree(kmsg);
}
- State = save_State;
- // May need to restore mouse shape.
- setmouse();
- return i;
+ return ret;
}
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index bd7ee55722..e21433f5ec 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -10,6 +10,7 @@
#include <string.h>
#include "klib/kvec.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/autocmd_defs.h"
@@ -37,7 +38,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
@@ -156,7 +156,8 @@ typedef struct compl_S compl_T;
struct compl_S {
compl_T *cp_next;
compl_T *cp_prev;
- char *cp_str; ///< matched text
+ compl_T *cp_match_next; ///< matched next compl_T
+ String cp_str; ///< matched text
char *(cp_text[CPT_COUNT]); ///< text for the menu
typval_T cp_user_data;
char *cp_fname; ///< file containing the match, allocated when
@@ -164,6 +165,7 @@ struct compl_S {
int cp_flags; ///< CP_ values
int cp_number; ///< sequence number
int cp_score; ///< fuzzy match score
+ bool cp_in_match_array; ///< collected by compl_match_array
int cp_user_abbr_hlattr; ///< highlight attribute for abbr
int cp_user_kind_hlattr; ///< highlight attribute for kind
};
@@ -219,7 +221,7 @@ static bool compl_enter_selects = false;
/// When "compl_leader" is not NULL only matches that start with this string
/// are used.
-static char *compl_leader = NULL;
+static String compl_leader = STRING_INIT;
static bool compl_get_longest = false; ///< put longest common string in compl_leader
@@ -244,8 +246,7 @@ static bool compl_started = false;
static int ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; ///< number of completion matches
-static char *compl_pattern = NULL;
-static size_t compl_patternlen = 0;
+static String compl_pattern = STRING_INIT;
static Direction compl_direction = FORWARD;
static Direction compl_shows_dir = FORWARD;
static int compl_pending = 0; ///< > 1 for postponed CTRL-N
@@ -255,8 +256,9 @@ static pos_T compl_startpos;
static int compl_length = 0;
static colnr_T compl_col = 0; ///< column where the text starts
///< that is being completed
-static char *compl_orig_text = NULL; ///< text as it was before
- ///< completion started
+static colnr_T compl_ins_end_col = 0;
+static String compl_orig_text = STRING_INIT; ///< text as it was before
+ ///< completion started
/// Undo information to restore extmarks for original text.
static extmark_undo_vec_t compl_orig_extmarks;
static int compl_cont_mode = 0;
@@ -281,6 +283,11 @@ static size_t spell_bad_len = 0; // length of located bad word
static int compl_selected_item = -1;
+// "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;
+
/// CTRL-X pressed in Insert mode.
void ins_ctrl_x(void)
{
@@ -475,7 +482,7 @@ bool check_compl_option(bool dict_opt)
msg((dict_opt ? _("'dictionary' option is empty") : _("'thesaurus' option is empty")),
HLF_E);
if (emsg_silent == 0 && !in_assert_fails) {
- vim_beep(BO_COMPL);
+ vim_beep(kOptBoFlagComplete);
setcursor();
ui_flush();
os_delay(2004, false);
@@ -560,11 +567,19 @@ static bool is_first_match(const compl_T *const match)
return match == compl_first_match;
}
-static void do_autocmd_completedone(int c)
+static void do_autocmd_completedone(int c, int mode, char *word)
{
save_v_event_T save_v_event;
dict_T *v_event = get_v_event(&save_v_event);
+ mode = mode & ~CTRL_X_WANT_IDENT;
+ char *mode_str = NULL;
+ if (ctrl_x_mode_names[mode]) {
+ mode_str = ctrl_x_mode_names[mode];
+ }
+ tv_dict_add_str(v_event, S_LEN("complete_word"), word != NULL ? word : "");
+ tv_dict_add_str(v_event, S_LEN("complete_type"), mode_str != NULL ? mode_str : "");
+
tv_dict_add_str(v_event, S_LEN("reason"), (c == Ctrl_Y ? "accept" : "cancel"));
tv_dict_set_keys_readonly(v_event);
@@ -624,7 +639,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Rule 1: Were any chars converted to lower?
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -644,7 +659,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// upper case.
if (!has_lower) {
bool was_letter = false;
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
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])) {
@@ -660,7 +675,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
// Copy the original case of the part we typed.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
for (int i = 0; i < min_len; i++) {
const int c = mb_ptr2char_adv(&p);
if (mb_islower(c)) {
@@ -690,7 +705,7 @@ static char *ins_compl_infercase_gettext(const char *str, int char_len, int comp
ga_grow(&gap, IOSIZE);
*p = NUL;
STRCPY(gap.ga_data, IObuff);
- gap.ga_len = (int)strlen(IObuff);
+ gap.ga_len = (int)(p - IObuff);
} else {
p += utf_char2bytes(wca[i++], p);
}
@@ -737,7 +752,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// Find actual length of original text.
{
- const char *p = compl_orig_text;
+ const char *p = compl_orig_text.data;
compl_char_len = 0;
while (*p != NUL) {
MB_PTR_ADV(p);
@@ -747,7 +762,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
// "char_len" may be smaller than "compl_char_len" when using
// thesaurus, only use the minimum when comparing.
- int min_len = char_len < compl_char_len ? char_len : compl_char_len;
+ int min_len = MIN(char_len, compl_char_len);
str = ins_compl_infercase_gettext(str, char_len, compl_char_len, min_len, &tofree);
}
@@ -758,7 +773,7 @@ int ins_compl_add_infercase(char *str_arg, int len, bool icase, char *fname, Dir
flags |= CP_ICASE;
}
- int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, -1, -1);
+ int res = ins_compl_add(str, len, fname, NULL, false, NULL, dir, flags, false, NULL);
xfree(tofree);
return res;
}
@@ -789,6 +804,7 @@ static inline void free_cptext(char *const *const cptext)
/// @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.
+/// @param[in] user_hl list of extra highlight attributes for abbr kind.
///
/// If "cdir" is FORWARD, then the match is added after the current match.
/// Otherwise, it is added before the current match.
@@ -798,7 +814,7 @@ static inline void free_cptext(char *const *const cptext)
/// returned in case of error.
static int ins_compl_add(char *const str, int len, char *const fname, char *const *const cptext,
const bool cptext_allocated, typval_T *user_data, const Direction cdir,
- int flags_arg, const bool adup, int user_abbr_hlattr, int user_kind_hlattr)
+ int flags_arg, const bool adup, const int *user_hl)
FUNC_ATTR_NONNULL_ARG(1)
{
compl_T *match;
@@ -825,8 +841,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match = compl_first_match;
do {
if (!match_at_original_text(match)
- && strncmp(match->cp_str, str, (size_t)len) == 0
- && ((int)strlen(match->cp_str) <= len || match->cp_str[len] == NUL)) {
+ && strncmp(match->cp_str.data, str, (size_t)len) == 0
+ && ((int)match->cp_str.size <= len || match->cp_str.data[len] == NUL)) {
if (cptext_allocated) {
free_cptext(cptext);
}
@@ -846,7 +862,7 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
if (flags & CP_ORIGINAL_TEXT) {
match->cp_number = 0;
}
- match->cp_str = xstrnsave(str, (size_t)len);
+ match->cp_str = cbuf_to_string(str, (size_t)len);
// match-fname is:
// - compl_curr_match->cp_fname if it is a string equal to fname.
@@ -864,8 +880,8 @@ static int ins_compl_add(char *const str, int len, char *const fname, char *cons
match->cp_fname = NULL;
}
match->cp_flags = flags;
- match->cp_user_abbr_hlattr = user_abbr_hlattr;
- match->cp_user_kind_hlattr = user_kind_hlattr;
+ match->cp_user_abbr_hlattr = user_hl ? user_hl[0] : -1;
+ match->cp_user_kind_hlattr = user_hl ? user_hl[1] : -1;
if (cptext != NULL) {
int i;
@@ -928,27 +944,55 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len)
return true;
}
if (match->cp_flags & CP_ICASE) {
- return STRNICMP(match->cp_str, str, len) == 0;
+ return STRNICMP(match->cp_str.data, str, len) == 0;
+ }
+ return strncmp(match->cp_str.data, str, len) == 0;
+}
+
+/// when len is -1 mean use whole length of p otherwise part of p
+static void ins_compl_insert_bytes(char *p, int len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (len == -1) {
+ len = (int)strlen(p);
}
- return strncmp(match->cp_str, str, len) == 0;
+ assert(len >= 0);
+ ins_bytes_len(p, (size_t)len);
+ compl_ins_end_col = curwin->w_cursor.col;
+}
+
+/// Checks if the column is within the currently inserted completion text
+/// column range. If it is, it returns a special highlight attribute.
+/// -1 mean normal item.
+int ins_compl_col_range_attr(int col)
+{
+ if (get_cot_flags() & kOptCotFlagFuzzy) {
+ return -1;
+ }
+
+ if (col >= (compl_col + (int)ins_compl_leader_len()) && col < compl_ins_end_col) {
+ return syn_name2attr("ComplMatchIns");
+ }
+
+ return -1;
}
/// Reduce the longest common string for match "match".
static void ins_compl_longest_match(compl_T *match)
{
- if (compl_leader == NULL) {
+ if (compl_leader.data == NULL) {
// First match, use it as a whole.
- compl_leader = xstrdup(match->cp_str);
+ compl_leader = copy_string(match->cp_str, NULL);
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(false);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
compl_used_match = false;
@@ -956,8 +1000,8 @@ static void ins_compl_longest_match(compl_T *match)
}
// Reduce the text if this match differs from compl_leader.
- char *p = compl_leader;
- char *s = match->cp_str;
+ char *p = compl_leader.data;
+ char *s = match->cp_str.data;
while (*p != NUL) {
int c1 = utf_ptr2char(p);
int c2 = utf_ptr2char(s);
@@ -974,15 +1018,17 @@ static void ins_compl_longest_match(compl_T *match)
if (*p != NUL) {
// Leader was shortened, need to change the inserted text.
*p = NUL;
+ compl_leader.size = (size_t)(p - compl_leader.data);
+
bool had_match = (curwin->w_cursor.col > compl_col);
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(false);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
ins_redraw(false);
// When the match isn't there (to avoid matching itself) remove it
// again after redrawing.
if (!had_match) {
- ins_compl_delete();
+ ins_compl_delete(false);
}
}
@@ -997,9 +1043,9 @@ static void ins_compl_add_matches(int num_matches, char **matches, int icase)
Direction dir = compl_direction;
for (int i = 0; i < num_matches && add_r != FAIL; i++) {
- if ((add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir,
- CP_FAST | (icase ? CP_ICASE : 0),
- false, -1, -1)) == OK) {
+ add_r = ins_compl_add(matches[i], -1, NULL, NULL, false, NULL, dir,
+ CP_FAST | (icase ? CP_ICASE : 0), false, NULL);
+ if (add_r == OK) {
// If dir was BACKWARD then honor it just once.
dir = FORWARD;
}
@@ -1038,8 +1084,8 @@ bool ins_compl_has_shown_match(void)
/// Return whether the shown match is long enough.
bool ins_compl_long_shown_match(void)
{
- return compl_shown_match != NULL && compl_shown_match->cp_str != NULL
- && (colnr_T)strlen(compl_shown_match->cp_str) > curwin->w_cursor.col - compl_col;
+ return compl_shown_match != NULL && compl_shown_match->cp_str.data != NULL
+ && (colnr_T)compl_shown_match->cp_str.size > curwin->w_cursor.col - compl_col;
}
/// Get the local or global value of 'completeopt' flags.
@@ -1048,11 +1094,6 @@ unsigned get_cot_flags(void)
return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
}
-/// "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;
-
/// Remove any popup menu.
static void ins_compl_del_pum(void)
{
@@ -1069,7 +1110,7 @@ bool pum_wanted(void)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
// "completeopt" must contain "menu" or "menuone"
- return (get_cot_flags() & COT_ANY_MENU) != 0;
+ return (get_cot_flags() & (kOptCotFlagMenu | kOptCotFlagMenuone)) != 0;
}
/// Check that there are two or more matches to be shown in the popup menu.
@@ -1088,7 +1129,7 @@ static bool pum_enough_matches(void)
comp = comp->cp_next;
} while (!is_first_match(comp));
- if (get_cot_flags() & COT_MENUONE) {
+ if (get_cot_flags() & kOptCotFlagMenuone) {
return i >= 1;
}
return i >= 2;
@@ -1099,7 +1140,7 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc_lock(VAR_FIXED);
- tv_dict_add_str(dict, S_LEN("word"), match->cp_str);
+ tv_dict_add_str(dict, S_LEN("word"), match->cp_str.data);
tv_dict_add_str(dict, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
tv_dict_add_str(dict, S_LEN("menu"), match->cp_text[CPT_MENU]);
tv_dict_add_str(dict, S_LEN("kind"), match->cp_text[CPT_KIND]);
@@ -1168,54 +1209,47 @@ static int ins_compl_build_pum(void)
XFREE_CLEAR(compl_leader);
}
- const int lead_len = compl_leader != NULL ? (int)strlen(compl_leader) : 0;
int max_fuzzy_score = 0;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
-
- do {
- // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
- // set the cp_score for later comparisons.
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
- comp->cp_score = fuzzy_match_str(comp->cp_str, compl_leader);
- }
-
- if (!match_at_original_text(comp)
- && (compl_leader == NULL
- || ins_compl_equal(comp, compl_leader, (size_t)lead_len)
- || (compl_fuzzy_match && comp->cp_score > 0))) {
- compl_match_arraysize++;
- }
- comp = comp->cp_next;
- } while (comp != NULL && !is_first_match(comp));
-
- if (compl_match_arraysize == 0) {
- return -1;
- }
-
- assert(compl_match_arraysize >= 0);
- compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
+ bool fuzzy_filter = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
+ bool fuzzy_sort = fuzzy_filter && !(cur_cot_flags & kOptCotFlagNosort);
+ compl_T *match_head = NULL, *match_tail = NULL;
// 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);
- if (strequal(compl_leader, compl_orig_text) && !shown_match_ok) {
+ if (strequal(compl_leader.data, compl_orig_text.data) && !shown_match_ok) {
compl_shown_match = compl_no_select ? compl_first_match : compl_first_match->cp_next;
}
- compl_T *shown_compl = NULL;
bool did_find_shown_match = false;
- int cur = -1;
+ compl_T *shown_compl = NULL;
int i = 0;
- comp = compl_first_match;
+ int cur = -1;
+
do {
+ comp->cp_in_match_array = false;
+ // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
+ // set the cp_score for later comparisons.
+ if (fuzzy_filter && compl_leader.data != NULL && compl_leader.size > 0) {
+ comp->cp_score = fuzzy_match_str(comp->cp_str.data, compl_leader.data);
+ }
+
if (!match_at_original_text(comp)
- && (compl_leader == NULL
- || ins_compl_equal(comp, compl_leader, (size_t)lead_len)
- || (compl_fuzzy_match && comp->cp_score > 0))) {
- if (!shown_match_ok && !compl_fuzzy_match) {
+ && (compl_leader.data == NULL
+ || ins_compl_equal(comp, compl_leader.data, compl_leader.size)
+ || (fuzzy_filter && comp->cp_score > 0))) {
+ compl_match_arraysize++;
+ comp->cp_in_match_array = true;
+ if (match_head == NULL) {
+ match_head = comp;
+ } else {
+ match_tail->cp_match_next = comp;
+ }
+ match_tail = comp;
+ if (!shown_match_ok && !fuzzy_filter) {
if (comp == compl_shown_match || did_find_shown_match) {
// This item is the shown match or this is the
// first displayed item after the shown match.
@@ -1228,64 +1262,36 @@ static int ins_compl_build_pum(void)
shown_compl = comp;
}
cur = i;
- } else if (compl_fuzzy_match) {
+ } else if (fuzzy_filter) {
if (i == 0) {
shown_compl = comp;
}
// Update the maximum fuzzy score and the shown match
// if the current item's score is higher
- if (comp->cp_score > max_fuzzy_score) {
+ if (fuzzy_sort && comp->cp_score > max_fuzzy_score) {
did_find_shown_match = true;
max_fuzzy_score = comp->cp_score;
if (!compl_no_select) {
compl_shown_match = comp;
}
+ } else if (!fuzzy_sort && i == 0 && !compl_no_select) {
+ compl_shown_match = shown_compl;
}
-
if (!shown_match_ok && comp == compl_shown_match && !compl_no_select) {
cur = i;
shown_match_ok = true;
}
-
- // If there is no "no select" condition and the max fuzzy
- // score is positive, or there is no completion leader or the
- // leader length is zero, mark the shown match as valid and
- // reset the current index.
- if (!compl_no_select
- && (max_fuzzy_score > 0
- || (compl_leader == NULL || lead_len == 0))) {
- if (match_at_original_text(compl_shown_match)) {
- compl_shown_match = shown_compl;
- }
- }
- }
-
- if (comp->cp_text[CPT_ABBR] != NULL) {
- compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR];
- } else {
- compl_match_array[i].pum_text = comp->cp_str;
- }
- compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
- compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
- compl_match_array[i].pum_score = comp->cp_score;
- compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr;
- compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr;
- if (comp->cp_text[CPT_MENU] != NULL) {
- compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU];
- } else {
- compl_match_array[i++].pum_extra = comp->cp_fname;
}
+ i++;
}
- if (comp == compl_shown_match && !compl_fuzzy_match) {
+ if (comp == compl_shown_match && !fuzzy_filter) {
did_find_shown_match = true;
-
// When the original text is the shown match don't set
// compl_shown_match.
if (match_at_original_text(comp)) {
shown_match_ok = true;
}
-
if (!shown_match_ok && shown_compl != NULL) {
// The shown match isn't displayed, set it to the
// previously displayed match.
@@ -1296,7 +1302,31 @@ static int ins_compl_build_pum(void)
comp = comp->cp_next;
} while (comp != NULL && !is_first_match(comp));
- if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0) {
+ if (compl_match_arraysize == 0) {
+ return -1;
+ }
+
+ assert(compl_match_arraysize >= 0);
+ compl_match_array = xcalloc((size_t)compl_match_arraysize, sizeof(pumitem_T));
+
+ i = 0;
+ comp = match_head;
+ while (comp != NULL) {
+ compl_match_array[i].pum_text = comp->cp_text[CPT_ABBR] != NULL
+ ? comp->cp_text[CPT_ABBR] : comp->cp_str.data;
+ compl_match_array[i].pum_kind = comp->cp_text[CPT_KIND];
+ compl_match_array[i].pum_info = comp->cp_text[CPT_INFO];
+ compl_match_array[i].pum_score = comp->cp_score;
+ compl_match_array[i].pum_user_abbr_hlattr = comp->cp_user_abbr_hlattr;
+ compl_match_array[i].pum_user_kind_hlattr = comp->cp_user_kind_hlattr;
+ compl_match_array[i++].pum_extra = comp->cp_text[CPT_MENU] != NULL
+ ? comp->cp_text[CPT_MENU] : comp->cp_fname;
+ compl_T *match_next = comp->cp_match_next;
+ comp->cp_match_next = NULL;
+ comp = match_next;
+ }
+
+ if (fuzzy_sort && compl_leader.data != NULL && compl_leader.size > 0) {
for (i = 0; i < compl_match_arraysize; i++) {
compl_match_array[i].pum_idx = i;
}
@@ -1334,7 +1364,7 @@ void ins_compl_show_pum(void)
} else {
// popup menu already exists, only need to find the current item.
for (int i = 0; i < compl_match_arraysize; i++) {
- if (compl_match_array[i].pum_text == compl_shown_match->cp_str
+ if (compl_match_array[i].pum_text == compl_shown_match->cp_str.data
|| compl_match_array[i].pum_text == compl_shown_match->cp_text[CPT_ABBR]) {
cur = i;
break;
@@ -1403,7 +1433,13 @@ bool compl_match_curr_select(int selected)
/// Get current completion leader
char *ins_compl_leader(void)
{
- return compl_leader != NULL ? compl_leader : compl_orig_text;
+ return compl_leader.data != NULL ? compl_leader.data : compl_orig_text.data;
+}
+
+/// Get current completion leader length
+size_t ins_compl_leader_len(void)
+{
+ return compl_leader.data != NULL ? compl_leader.size : compl_orig_text.size;
}
/// Add any identifiers that match the given pattern "pat" in the list of
@@ -1562,6 +1598,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
FILE *fp = os_fopen(files[i], "r"); // open dictionary file
if (flags != DICT_EXACT && !shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc()
+ msg_ext_set_kind("completion");
vim_snprintf(IObuff, IOSIZE,
_("Scanning dictionary: %s"), files[i]);
msg_trunc(IObuff, true, HLF_R);
@@ -1577,11 +1614,7 @@ static void ins_compl_files(int count, char **files, bool thesaurus, int flags,
char *ptr = buf;
while (vim_regexec(regmatch, buf, (colnr_T)(ptr - buf))) {
ptr = regmatch->startp[0];
- if (ctrl_x_mode_line_or_eval()) {
- ptr = find_line_end(ptr);
- } else {
- ptr = find_word_end(ptr);
- }
+ ptr = ctrl_x_mode_line_or_eval() ? find_line_end(ptr) : find_word_end(ptr);
int add_r = ins_compl_add_infercase(regmatch->startp[0],
(int)(ptr - regmatch->startp[0]),
p_ic, files[i], *dir, false);
@@ -1652,9 +1685,8 @@ static char *find_line_end(char *ptr)
/// Free the list of completions
static void ins_compl_free(void)
{
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
if (compl_first_match == NULL) {
return;
@@ -1667,7 +1699,7 @@ static void ins_compl_free(void)
do {
compl_T *match = compl_curr_match;
compl_curr_match = compl_curr_match->cp_next;
- xfree(match->cp_str);
+ API_CLEAR_STRING(match->cp_str);
// several entries may use the same fname, free it just once.
if (match->cp_flags & CP_FREE_FNAME) {
xfree(match->cp_fname);
@@ -1687,12 +1719,12 @@ void ins_compl_clear(void)
compl_cont_status = 0;
compl_started = false;
compl_matches = 0;
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_leader);
+ compl_ins_end_col = 0;
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_leader);
edit_submode_extra = NULL;
kv_destroy(compl_orig_extmarks);
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc_lock(VAR_FIXED));
@@ -1705,6 +1737,12 @@ bool ins_compl_active(void)
return compl_started;
}
+/// Return true when wp is the actual completion window
+bool ins_compl_win_active(win_T *wp)
+{
+ return ins_compl_active() && !(wp->w_p_pvw || wp->w_float_is_info);
+}
+
/// Selected one of the matches. When false the match was edited or using the
/// longest common string.
bool ins_compl_used_match(void)
@@ -1743,12 +1781,33 @@ int ins_compl_len(void)
return compl_length;
}
+/// Return true when preinsert is set otherwise FALSE.
+static bool ins_compl_has_preinsert(void)
+{
+ return (get_cot_flags() & (kOptCotFlagFuzzy|kOptCotFlagPreinsert)) == kOptCotFlagPreinsert;
+}
+
+/// Returns true if the pre-insert effect is valid and the cursor is within
+/// the `compl_ins_end_col` range.
+bool ins_compl_preinsert_effect(void)
+{
+ if (!ins_compl_has_preinsert()) {
+ return false;
+ }
+
+ return curwin->w_cursor.col < compl_ins_end_col;
+}
+
/// 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
/// to be got from the user.
int ins_compl_bs(void)
{
+ if (ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
char *line = get_cursor_line_ptr();
char *p = line + curwin->w_cursor.col;
MB_PTR_BACK(line, p);
@@ -1776,8 +1835,9 @@ int ins_compl_bs(void)
// TODO(bfredl): get rid of random update_screen() calls deep inside completion logic
line = get_cursor_line_ptr();
- xfree(compl_leader);
- compl_leader = xstrnsave(line + compl_col, (size_t)(p_off - (ptrdiff_t)compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(line + compl_col,
+ (size_t)(p_off - (ptrdiff_t)compl_col));
ins_compl_new_leader();
if (compl_shown_match != NULL) {
@@ -1810,12 +1870,12 @@ static bool ins_compl_need_restart(void)
static void ins_compl_new_leader(void)
{
ins_compl_del_pum();
- ins_compl_delete();
- ins_bytes(compl_leader + get_compl_len());
+ ins_compl_delete(true);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
compl_used_match = false;
if (compl_started) {
- ins_compl_set_original_text(compl_leader);
+ ins_compl_set_original_text(compl_leader.data, compl_leader.size);
} else {
spell_bad_len = 0; // need to redetect bad word
// Matches were cleared, need to search for them now.
@@ -1833,8 +1893,13 @@ static void ins_compl_new_leader(void)
ins_compl_show_pum();
// Don't let Enter select the original text when there is no popup menu.
+ if (compl_match_array == NULL) {
+ compl_enter_selects = false;
+ } else if (ins_compl_has_preinsert() && compl_leader.size > 0) {
+ ins_compl_insert(false, true);
+ }
// Don't let Enter select when use user function and refresh_always is set
- if (compl_match_array == NULL || ins_compl_refresh_always()) {
+ if (ins_compl_refresh_always()) {
compl_enter_selects = false;
}
}
@@ -1857,6 +1922,10 @@ void ins_compl_addleader(int c)
{
int cc;
+ if (ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
if (stop_arrow() == FAIL) {
return;
}
@@ -1875,9 +1944,9 @@ void ins_compl_addleader(int c)
ins_compl_restart();
}
- xfree(compl_leader);
- compl_leader = xstrnsave(get_cursor_line_ptr() + compl_col,
- (size_t)(curwin->w_cursor.col - compl_col));
+ API_CLEAR_STRING(compl_leader);
+ compl_leader = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)(curwin->w_cursor.col - compl_col));
ins_compl_new_leader();
}
@@ -1897,19 +1966,19 @@ static void ins_compl_restart(void)
}
/// Set the first match, the original text.
-static void ins_compl_set_original_text(char *str)
+static void ins_compl_set_original_text(char *str, size_t len)
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 (match_at_original_text(compl_first_match)) { // safety check
- xfree(compl_first_match->cp_str);
- compl_first_match->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_str);
+ compl_first_match->cp_str = cbuf_to_string(str, len);
} else if (compl_first_match->cp_prev != NULL
&& match_at_original_text(compl_first_match->cp_prev)) {
- xfree(compl_first_match->cp_prev->cp_str);
- compl_first_match->cp_prev->cp_str = xstrdup(str);
+ API_CLEAR_STRING(compl_first_match->cp_prev->cp_str);
+ compl_first_match->cp_prev->cp_str = cbuf_to_string(str, len);
}
}
@@ -1919,8 +1988,8 @@ void ins_compl_addfrommatch(void)
{
int len = (int)curwin->w_cursor.col - (int)compl_col;
assert(compl_shown_match != NULL);
- char *p = compl_shown_match->cp_str;
- if ((int)strlen(p) <= len) { // the match is too short
+ char *p = compl_shown_match->cp_str.data;
+ if ((int)compl_shown_match->cp_str.size <= len) { // the match is too short
// When still at the original match use the first entry that matches
// the leader.
if (!match_at_original_text(compl_shown_match)) {
@@ -1928,15 +1997,17 @@ void ins_compl_addfrommatch(void)
}
p = NULL;
+ size_t plen = 0;
for (compl_T *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;
+ if (compl_leader.data == NULL
+ || ins_compl_equal(cp, compl_leader.data, compl_leader.size)) {
+ p = cp->cp_str.data;
+ plen = cp->cp_str.size;
break;
}
}
- if (p == NULL || (int)strlen(p) <= len) {
+ if (p == NULL || (int)plen <= len) {
return;
}
}
@@ -2076,7 +2147,7 @@ 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 (compl_curr_match != NULL || compl_leader.data != 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
@@ -2085,7 +2156,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
// CTRL-E then don't use the current match.
char *ptr;
if (compl_curr_match != NULL && compl_used_match && c != Ctrl_E) {
- ptr = compl_curr_match->cp_str;
+ ptr = compl_curr_match->cp_str.data;
} else {
ptr = NULL;
}
@@ -2121,36 +2192,46 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
}
+ char *word = NULL;
// 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()) {
+ word = xstrdup(compl_shown_match->cp_str.data);
retval = true;
+ // May need to remove ComplMatchIns highlight.
+ redrawWinline(curwin, curwin->w_cursor.lnum);
}
// 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();
+ ins_compl_delete(false);
char *p = NULL;
- if (compl_leader != NULL) {
- p = compl_leader;
+ size_t plen = 0;
+ if (compl_leader.data != NULL) {
+ p = compl_leader.data;
+ plen = compl_leader.size;
} else if (compl_first_match != NULL) {
- p = compl_orig_text;
+ p = compl_orig_text.data;
+ plen = compl_orig_text.size;
}
if (p != NULL) {
const int compl_len = get_compl_len();
- const int len = (int)strlen(p);
- if (len > compl_len) {
- ins_bytes_len(p + compl_len, (size_t)(len - compl_len));
+ if ((int)plen > compl_len) {
+ ins_compl_insert_bytes(p + compl_len, (int)plen - compl_len);
}
}
restore_orig_extmarks();
retval = true;
}
+ if ((c == Ctrl_W || c == Ctrl_U) && ins_compl_preinsert_effect()) {
+ ins_compl_delete(false);
+ }
+
auto_format(false, true);
// Trigger the CompleteDonePre event to give scripts a chance to
@@ -2184,7 +2265,8 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval)
}
// Trigger the CompleteDone event to give scripts a chance to act
// upon the end of completion.
- do_autocmd_completedone(c);
+ do_autocmd_completedone(c, prev_mode, word);
+ xfree(word);
return retval;
}
@@ -2236,7 +2318,7 @@ 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 = (get_cot_flags() & COT_LONGEST) != 0;
+ compl_get_longest = (get_cot_flags() & kOptCotFlagLongest) != 0;
compl_used_match = true;
}
@@ -2273,7 +2355,7 @@ bool ins_compl_prep(int c)
} else if (ctrl_x_mode == CTRL_X_LOCAL_MSG) {
// Trigger the CompleteDone event to give scripts a chance to act
// upon the (possibly failed) completion.
- do_autocmd_completedone(c);
+ do_autocmd_completedone(c, ctrl_x_mode, NULL);
}
may_trigger_modechanged();
@@ -2293,18 +2375,18 @@ bool ins_compl_prep(int c)
/// "ptr" is the known leader text or NUL.
static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
{
- int len;
+ int len = 0;
char *ptr = ptr_arg;
if (ptr == NULL) {
- if (compl_leader != NULL) {
- ptr = compl_leader;
+ if (compl_leader.data != NULL) {
+ ptr = compl_leader.data;
} else {
return; // nothing to do
}
}
- if (compl_orig_text != NULL) {
- char *p = compl_orig_text;
+ if (compl_orig_text.data != NULL) {
+ char *p = compl_orig_text.data;
for (len = 0; p[len] != NUL && p[len] == ptr[len]; len++) {}
if (len > 0) {
len -= utf_head_off(p, p + len);
@@ -2312,8 +2394,6 @@ static void ins_compl_fixRedoBufForLeader(char *ptr_arg)
for (p += len; *p != NUL; MB_PTR_ADV(p)) {
AppendCharToRedobuff(K_BS);
}
- } else {
- len = 0;
}
AppendToRedobuffLit(ptr + len, -1);
}
@@ -2573,9 +2653,8 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
int flags = fast ? CP_FAST : 0;
char *(cptext[CPT_COUNT]);
char *user_abbr_hlname = NULL;
- int user_abbr_hlattr = -1;
char *user_kind_hlname = NULL;
- int user_kind_hlattr = -1;
+ int user_hl[2] = { -1, -1 };
typval_T user_data;
user_data.v_type = VAR_UNKNOWN;
@@ -2587,10 +2666,10 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
cptext[CPT_INFO] = tv_dict_get_string(tv->vval.v_dict, "info", true);
user_abbr_hlname = tv_dict_get_string(tv->vval.v_dict, "abbr_hlgroup", false);
- user_abbr_hlattr = get_user_highlight_attr(user_abbr_hlname);
+ user_hl[0] = get_user_highlight_attr(user_abbr_hlname);
user_kind_hlname = tv_dict_get_string(tv->vval.v_dict, "kind_hlgroup", false);
- user_kind_hlattr = get_user_highlight_attr(user_kind_hlname);
+ user_hl[1] = get_user_highlight_attr(user_kind_hlname);
tv_dict_get_tv(tv->vval.v_dict, "user_data", &user_data);
@@ -2613,8 +2692,7 @@ static int ins_compl_add_tv(typval_T *const tv, const Direction dir, bool fast)
return FAIL;
}
int status = ins_compl_add((char *)word, -1, NULL, cptext, true,
- &user_data, dir, flags, dup,
- user_abbr_hlattr, user_kind_hlattr);
+ &user_data, dir, flags, dup, user_hl);
if (status != OK) {
tv_clear(&user_data);
}
@@ -2682,9 +2760,9 @@ static void set_completion(colnr_T startcol, list_T *list)
{
int flags = CP_ORIGINAL_TEXT;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_longest = (cur_cot_flags & COT_LONGEST) != 0;
- bool compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
+ bool compl_longest = (cur_cot_flags & kOptCotFlagLongest) != 0;
+ bool compl_no_insert = (cur_cot_flags & kOptCotFlagNoinsert) != 0;
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
// If already doing completions stop it.
if (ctrl_x_mode_not_default()) {
@@ -2701,13 +2779,15 @@ static void set_completion(colnr_T startcol, list_T *list)
compl_col = startcol;
compl_length = curwin->w_cursor.col - startcol;
// compl_pattern doesn't need to be set
- compl_orig_text = xstrnsave(get_cursor_line_ptr() + compl_col, (size_t)compl_length);
+ compl_orig_text = cbuf_to_string(get_cursor_line_ptr() + compl_col,
+ (size_t)compl_length);
save_orig_extmarks();
if (p_ic) {
flags |= CP_ICASE;
}
- if (ins_compl_add(compl_orig_text, -1, NULL, NULL, false, NULL, 0,
- flags | CP_FAST, false, -1, -1) != OK) {
+ if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size,
+ NULL, NULL, false, NULL, 0,
+ flags | CP_FAST, false, NULL) != OK) {
return;
}
@@ -2841,6 +2921,25 @@ static void ins_compl_update_sequence_numbers(void)
}
}
+/// Fill the dict of complete_info
+static void fill_complete_info_dict(dict_T *di, compl_T *match, bool add_match)
+{
+ tv_dict_add_str(di, S_LEN("word"), match->cp_str.data);
+ tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
+ tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
+ tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]);
+ tv_dict_add_str(di, S_LEN("info"), match->cp_text[CPT_INFO]);
+ if (add_match) {
+ tv_dict_add_bool(di, S_LEN("match"), match->cp_in_match_array);
+ }
+ if (match->cp_user_data.v_type == VAR_UNKNOWN) {
+ // Add an empty string for backwards compatibility
+ tv_dict_add_str(di, S_LEN("user_data"), "");
+ } else {
+ tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data);
+ }
+}
+
/// Get complete information
static void get_complete_info(list_T *what_list, dict_T *retdict)
{
@@ -2848,12 +2947,13 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
#define CI_WHAT_PUM_VISIBLE 0x02
#define CI_WHAT_ITEMS 0x04
#define CI_WHAT_SELECTED 0x08
-#define CI_WHAT_INSERTED 0x10
+#define CI_WHAT_COMPLETED 0x10
+#define CI_WHAT_MATCHES 0x20
#define CI_WHAT_ALL 0xff
int what_flag;
if (what_list == NULL) {
- what_flag = CI_WHAT_ALL;
+ what_flag = CI_WHAT_ALL & ~(CI_WHAT_MATCHES|CI_WHAT_COMPLETED);
} else {
what_flag = 0;
for (listitem_T *item = tv_list_first(what_list)
@@ -2869,8 +2969,10 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
what_flag |= CI_WHAT_ITEMS;
} else if (strcmp(what, "selected") == 0) {
what_flag |= CI_WHAT_SELECTED;
- } else if (strcmp(what, "inserted") == 0) {
- what_flag |= CI_WHAT_INSERTED;
+ } else if (strcmp(what, "completed") == 0) {
+ what_flag |= CI_WHAT_COMPLETED;
+ } else if (strcmp(what, "matches") == 0) {
+ what_flag |= CI_WHAT_MATCHES;
}
}
}
@@ -2884,12 +2986,17 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
ret = tv_dict_add_nr(retdict, S_LEN("pum_visible"), pum_visible());
}
- if (ret == OK && (what_flag & CI_WHAT_ITEMS || what_flag & CI_WHAT_SELECTED)) {
+ if (ret == OK && (what_flag & (CI_WHAT_ITEMS|CI_WHAT_SELECTED
+ |CI_WHAT_MATCHES|CI_WHAT_COMPLETED))) {
list_T *li = NULL;
int selected_idx = -1;
- if (what_flag & CI_WHAT_ITEMS) {
+ bool has_items = what_flag & CI_WHAT_ITEMS;
+ bool has_matches = what_flag & CI_WHAT_MATCHES;
+ bool has_completed = what_flag & CI_WHAT_COMPLETED;
+ if (has_items || has_matches) {
li = tv_list_alloc(kListLenMayKnow);
- ret = tv_dict_add_list(retdict, S_LEN("items"), li);
+ const char *key = (has_matches && !has_items) ? "matches" : "items";
+ ret = tv_dict_add_list(retdict, key, strlen(key), li);
}
if (ret == OK && what_flag & CI_WHAT_SELECTED) {
if (compl_curr_match != NULL && compl_curr_match->cp_number == -1) {
@@ -2901,20 +3008,10 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
compl_T *match = compl_first_match;
do {
if (!match_at_original_text(match)) {
- if (what_flag & CI_WHAT_ITEMS) {
+ if (has_items || (has_matches && match->cp_in_match_array)) {
dict_T *di = tv_dict_alloc();
tv_list_append_dict(li, di);
- tv_dict_add_str(di, S_LEN("word"), match->cp_str);
- tv_dict_add_str(di, S_LEN("abbr"), match->cp_text[CPT_ABBR]);
- tv_dict_add_str(di, S_LEN("menu"), match->cp_text[CPT_MENU]);
- tv_dict_add_str(di, S_LEN("kind"), match->cp_text[CPT_KIND]);
- tv_dict_add_str(di, S_LEN("info"), match->cp_text[CPT_INFO]);
- if (match->cp_user_data.v_type == VAR_UNKNOWN) {
- // Add an empty string for backwards compatibility
- tv_dict_add_str(di, S_LEN("user_data"), "");
- } else {
- tv_dict_add_tv(di, S_LEN("user_data"), &match->cp_user_data);
- }
+ fill_complete_info_dict(di, match, has_matches && has_items);
}
if (compl_curr_match != NULL
&& compl_curr_match->cp_number == match->cp_number) {
@@ -2933,11 +3030,14 @@ static void get_complete_info(list_T *what_list, dict_T *retdict)
tv_dict_add_nr(retdict, S_LEN("preview_bufnr"), wp->w_buffer->handle);
}
}
+ if (ret == OK && selected_idx != -1 && has_completed) {
+ dict_T *di = tv_dict_alloc();
+ fill_complete_info_dict(di, compl_curr_match, false);
+ ret = tv_dict_add_dict(retdict, S_LEN("completed"), di);
+ }
}
(void)ret;
- // TODO(vim):
- // if (ret == OK && (what_flag & CI_WHAT_INSERTED))
}
/// "complete_info()" function
@@ -3040,6 +3140,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
}
if (!shortmess(SHM_COMPLETIONSCAN)) {
msg_hist_off = true; // reset in msg_trunc()
+ msg_ext_set_kind("completion");
vim_snprintf(IObuff, IOSIZE, _("Scanning: %s"),
st->ins_buf->b_fname == NULL
? buf_spname(st->ins_buf)
@@ -3072,6 +3173,7 @@ static int process_next_cpt_value(ins_compl_next_state_T *st, int *compl_type_ar
} else if (*st->e_cpt == ']' || *st->e_cpt == 't') {
compl_type = CTRL_X_TAGS;
if (!shortmess(SHM_COMPLETIONSCAN)) {
+ msg_ext_set_kind("completion");
msg_hist_off = true; // reset in msg_trunc()
vim_snprintf(IObuff, IOSIZE, "%s", _("Scanning tags."));
msg_trunc(IObuff, true, HLF_R);
@@ -3096,8 +3198,8 @@ done:
/// included files.
static void get_next_include_file_completion(int compl_type)
{
- find_pattern_in_path(compl_pattern, compl_direction,
- compl_patternlen, false, false,
+ find_pattern_in_path(compl_pattern.data, compl_direction,
+ compl_pattern.size, false, false,
((compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY),
@@ -3109,14 +3211,14 @@ static void get_next_include_file_completion(int compl_type)
static void get_next_dict_tsr_completion(int compl_type, char *dict, int dict_f)
{
if (thesaurus_func_complete(compl_type)) {
- expand_by_function(compl_type, compl_pattern);
+ expand_by_function(compl_type, compl_pattern.data);
} else {
ins_compl_dictionaries(dict != NULL
? dict
: (compl_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)),
- compl_pattern,
+ compl_pattern.data,
dict != NULL ? dict_f : 0,
compl_type == CTRL_X_THESAURUS);
}
@@ -3127,14 +3229,14 @@ 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(compl_pattern);
+ p_ic = ignorecase(compl_pattern.data);
// 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,
+ if (find_tags(compl_pattern.data, &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) {
@@ -3149,13 +3251,13 @@ static void get_next_filename_completion(void)
{
char **matches;
int num_matches;
- if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
+ if (expand_wildcards(1, &compl_pattern.data, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) != OK) {
return;
}
// May change home directory back to "~".
- tilde_replace(compl_pattern, num_matches, matches);
+ tilde_replace(compl_pattern.data, num_matches, matches);
#ifdef BACKSLASH_IN_FILENAME
if (curbuf->b_p_csl[0] != NUL) {
for (int i = 0; i < num_matches; i++) {
@@ -3179,8 +3281,8 @@ static void get_next_cmdline_completion(void)
{
char **matches;
int num_matches;
- if (expand_cmdline(&compl_xp, compl_pattern,
- (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK) {
+ if (expand_cmdline(&compl_xp, compl_pattern.data,
+ (int)compl_pattern.size, &num_matches, &matches) == EXPAND_OK) {
ins_compl_add_matches(num_matches, matches, false);
}
}
@@ -3189,7 +3291,7 @@ static void get_next_cmdline_completion(void)
static void get_next_spell_completion(linenr_T lnum)
{
char **matches;
- int num_matches = expand_spelling(lnum, compl_pattern, &matches);
+ int num_matches = expand_spelling(lnum, compl_pattern.data, &matches);
if (num_matches > 0) {
ins_compl_add_matches(num_matches, matches, p_ic);
} else {
@@ -3208,22 +3310,24 @@ static char *ins_compl_get_next_word_or_line(buf_T *ins_buf, pos_T *cur_match_po
{
*match_len = 0;
char *ptr = ml_get_buf(ins_buf, cur_match_pos->lnum) + cur_match_pos->col;
- int len;
+ int len = ml_get_buf_len(ins_buf, cur_match_pos->lnum) - cur_match_pos->col;
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);
+ len = ml_get_buf_len(ins_buf, cur_match_pos->lnum + 1);
if (!p_paste) {
- ptr = skipwhite(ptr);
+ char *tmp_ptr = ptr;
+ ptr = skipwhite(tmp_ptr);
+ len -= (int)(ptr - tmp_ptr);
}
}
- len = (int)strlen(ptr);
} else {
char *tmp_ptr = ptr;
- if (compl_status_adding() && compl_length <= (int)strlen(tmp_ptr)) {
+ if (compl_status_adding() && compl_length <= len) {
tmp_ptr += compl_length;
// Skip if already inside a word.
if (vim_iswordp(tmp_ptr)) {
@@ -3319,10 +3423,11 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
// 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, compl_pattern);
+ compl_direction, compl_pattern.data);
} else {
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
- NULL, compl_direction, compl_pattern, compl_patternlen,
+ NULL, compl_direction, compl_pattern.data,
+ compl_pattern.size,
1, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
}
msg_silent--;
@@ -3368,7 +3473,8 @@ static int get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_
int len;
char *ptr = ins_compl_get_next_word_or_line(st->ins_buf, st->cur_match_pos,
&len, &cont_s_ipos);
- if (ptr == NULL) {
+ if (ptr == NULL
+ || (ins_compl_has_preinsert() && strcmp(ptr, compl_pattern.data) == 0)) {
continue;
}
if (ins_compl_add_infercase(ptr, len, p_ic,
@@ -3419,7 +3525,7 @@ static bool get_next_completion_match(int type, ins_compl_next_state_T *st, pos_
case CTRL_X_FUNCTION:
case CTRL_X_OMNI:
- expand_by_function(type, compl_pattern);
+ expand_by_function(type, compl_pattern.data);
break;
case CTRL_X_SPELL:
@@ -3450,9 +3556,9 @@ static void get_next_bufname_token(void)
FOR_ALL_BUFFERS(b) {
if (b->b_p_bl && b->b_sfname != NULL) {
char *tail = path_tail(b->b_sfname);
- if (strncmp(tail, compl_orig_text, strlen(compl_orig_text)) == 0) {
+ if (strncmp(tail, compl_orig_text.data, compl_orig_text.size) == 0) {
ins_compl_add(tail, (int)strlen(tail), NULL, NULL, false, NULL, 0,
- p_ic ? CP_ICASE : 0, false, -1, -1);
+ p_ic ? CP_ICASE : 0, false, NULL);
}
}
}
@@ -3518,7 +3624,7 @@ static int ins_compl_get_exp(pos_T *ini)
// If complete() was called then compl_pattern has been reset.
// The following won't work then, bail out.
- if (compl_pattern == NULL) {
+ if (compl_pattern.data == NULL) {
break;
}
@@ -3586,7 +3692,7 @@ static int ins_compl_get_exp(pos_T *ini)
static void ins_compl_update_shown_match(void)
{
while (!ins_compl_equal(compl_shown_match,
- compl_leader, strlen(compl_leader))
+ compl_leader.data, compl_leader.size)
&& compl_shown_match->cp_next != NULL
&& !is_first_match(compl_shown_match->cp_next)) {
compl_shown_match = compl_shown_match->cp_next;
@@ -3595,10 +3701,10 @@ static void ins_compl_update_shown_match(void)
// 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))
+ && !ins_compl_equal(compl_shown_match, compl_leader.data, compl_leader.size)
&& (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))
+ while (!ins_compl_equal(compl_shown_match, compl_leader.data, compl_leader.size)
&& compl_shown_match->cp_prev != NULL
&& !is_first_match(compl_shown_match->cp_prev)) {
compl_shown_match = compl_shown_match->cp_prev;
@@ -3607,16 +3713,36 @@ static void ins_compl_update_shown_match(void)
}
/// Delete the old text being completed.
-void ins_compl_delete(void)
+void ins_compl_delete(bool new_leader)
{
+ // Avoid deleting text that will be reinserted when changing leader. This
+ // allows marks present on the original text to shrink/grow appropriately.
+ int orig_col = 0;
+ if (new_leader) {
+ char *orig = compl_orig_text.data;
+ char *leader = ins_compl_leader();
+ while (*orig != NUL && utf_ptr2char(orig) == utf_ptr2char(leader)) {
+ leader += utf_ptr2len(leader);
+ orig += utf_ptr2len(orig);
+ }
+ orig_col = (int)(orig - compl_orig_text.data);
+ }
+
// In insert mode: Delete the typed part.
// In replace mode: Put the old characters back, if any.
- int col = compl_col + (compl_status_adding() ? compl_length : 0);
+ int col = compl_col + (compl_status_adding() ? compl_length : orig_col);
+ bool has_preinsert = ins_compl_preinsert_effect();
+ if (has_preinsert) {
+ col += (int)ins_compl_leader_len();
+ curwin->w_cursor.col = compl_ins_end_col;
+ }
+
if ((int)curwin->w_cursor.col > col) {
if (stop_arrow() == FAIL) {
return;
}
backspace_until_column(col);
+ compl_ins_end_col = curwin->w_cursor.col;
}
// TODO(vim): is this sufficient for redrawing? Redrawing everything
@@ -3628,15 +3754,25 @@ void ins_compl_delete(void)
/// Insert the new text being completed.
/// "in_compl_func" is true when called from complete_check().
-void ins_compl_insert(bool in_compl_func)
+/// "move_cursor" is used when 'completeopt' includes "preinsert" and when true
+/// cursor needs to move back from the inserted text to the compl_leader.
+void ins_compl_insert(bool in_compl_func, bool move_cursor)
{
int compl_len = get_compl_len();
+ bool preinsert = ins_compl_has_preinsert();
+ char *cp_str = compl_shown_match->cp_str.data;
+ size_t cp_str_len = compl_shown_match->cp_str.size;
+ size_t leader_len = ins_compl_leader_len();
+
// Make sure we don't go over the end of the string, this can happen with
// illegal bytes.
- if (compl_len < (int)strlen(compl_shown_match->cp_str)) {
- ins_bytes(compl_shown_match->cp_str + compl_len);
+ if (compl_len < (int)cp_str_len) {
+ ins_compl_insert_bytes(cp_str + compl_len, -1);
+ if (preinsert && move_cursor) {
+ curwin->w_cursor.col -= (colnr_T)(cp_str_len - leader_len);
+ }
}
- compl_used_match = !match_at_original_text(compl_shown_match);
+ compl_used_match = !(match_at_original_text(compl_shown_match) || preinsert);
dict_T *dict = ins_compl_dict_alloc(compl_shown_match);
set_vim_var_dict(VV_COMPLETED_ITEM, dict);
@@ -3704,7 +3840,7 @@ static compl_T *find_comp_when_fuzzy(void)
comp = compl_first_match;
do {
if (comp->cp_score == score
- && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR])) {
+ && (str == comp->cp_str.data || str == comp->cp_text[CPT_ABBR])) {
return comp;
}
comp = comp->cp_next;
@@ -3731,8 +3867,8 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
bool found_end = false;
compl_T *found_compl = NULL;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
+ bool compl_no_select = (cur_cot_flags & kOptCotFlagNoselect) != 0;
+ bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
while (--todo >= 0) {
if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL) {
@@ -3787,9 +3923,9 @@ static int find_next_completion_match(bool allow_get_expansion, int todo, bool a
found_end = false;
}
if (!match_at_original_text(compl_shown_match)
- && compl_leader != NULL
+ && compl_leader.data != NULL
&& !ins_compl_equal(compl_shown_match,
- compl_leader, strlen(compl_leader))
+ compl_leader.data, compl_leader.size)
&& !(compl_fuzzy_match && compl_shown_match->cp_score > 0)) {
todo++;
} else {
@@ -3836,8 +3972,9 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
const bool started = compl_started;
buf_T *const orig_curbuf = curbuf;
unsigned cur_cot_flags = get_cot_flags();
- bool compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
- bool compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
+ bool compl_no_insert = (cur_cot_flags & kOptCotFlagNoinsert) != 0;
+ bool compl_fuzzy_match = (cur_cot_flags & kOptCotFlagFuzzy) != 0;
+ bool compl_preinsert = ins_compl_has_preinsert();
// When user complete function return -1 for findstart which is next
// time of 'always', compl_shown_match become NULL.
@@ -3845,7 +3982,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
return -1;
}
- if (compl_leader != NULL
+ if (compl_leader.data != NULL
&& !match_at_original_text(compl_shown_match)
&& !compl_fuzzy_match) {
// Update "compl_shown_match" to the actually shown match
@@ -3855,7 +3992,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
if (allow_get_expansion && insert_match
&& (!compl_get_longest || compl_used_match)) {
// Delete old text to be replaced
- ins_compl_delete();
+ ins_compl_delete(false);
}
// When finding the longest common text we stick at the original text,
@@ -3882,17 +4019,18 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
}
// Insert the text of the new completion, or the compl_leader.
- if (compl_no_insert && !started) {
- ins_bytes(compl_orig_text + get_compl_len());
+ if (compl_no_insert && !started && !compl_preinsert) {
+ ins_compl_insert_bytes(compl_orig_text.data + get_compl_len(), -1);
compl_used_match = false;
restore_orig_extmarks();
} else if (insert_match) {
if (!compl_get_longest || compl_used_match) {
- ins_compl_insert(in_compl_func);
+ ins_compl_insert(in_compl_func, true);
} else {
- ins_bytes(compl_leader + get_compl_len());
+ assert(compl_leader.data != NULL);
+ ins_compl_insert_bytes(compl_leader.data + get_compl_len(), -1);
}
- if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) {
+ if (strequal(compl_curr_match->cp_str.data, compl_orig_text.data)) {
restore_orig_extmarks();
}
} else {
@@ -3908,7 +4046,7 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match
// Delete old text to be replaced, since we're still searching and
// don't want to match ourselves!
- ins_compl_delete();
+ ins_compl_delete(false);
}
// Enter will select a match when the match wasn't inserted and the popup
@@ -3976,7 +4114,7 @@ void ins_compl_check_keys(int frequency, bool in_compl_func)
}
}
}
- if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT)) {
+ if (compl_pending != 0 && !got_int && !(cot_flags & kOptCotFlagNoinsert)) {
int todo = compl_pending > 0 ? compl_pending : -compl_pending;
compl_pending = 0;
@@ -4056,8 +4194,7 @@ static bool ins_compl_use_match(int c)
/// Get the pattern, column and length for normal completion (CTRL-N CTRL-P
/// completion)
-/// Sets the global variables: compl_col, compl_length, compl_pattern and
-/// compl_patternlen.
+/// Sets the global variables: compl_col, compl_length and compl_pattern.
/// Uses the global variables: compl_cont_status and ctrl_x_mode
static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
{
@@ -4068,29 +4205,32 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
compl_length = curs_col - startcol;
}
if (p_ic) {
- compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = cstr_as_string(str_foldcase(line + compl_col,
+ compl_length, NULL, 0));
} else {
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
}
} else if (compl_status_adding()) {
char *prefix = "\\<";
size_t prefixlen = STRLEN_LITERAL("\\<");
- // we need up to 2 extra chars for the prefix
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col,
- compl_length) + prefixlen);
if (!vim_iswordp(line + compl_col)
|| (compl_col > 0
&& (vim_iswordp(mb_prevptr(line, line + compl_col))))) {
prefix = "";
prefixlen = 0;
}
- STRCPY(compl_pattern, prefix);
- quote_meta(compl_pattern + prefixlen, line + compl_col, compl_length);
+
+ // we need up to 2 extra chars for the prefix
+ size_t n = quote_meta(NULL, line + compl_col, compl_length) + prefixlen;
+ compl_pattern.data = xmalloc(n);
+ STRCPY(compl_pattern.data, prefix);
+ quote_meta(compl_pattern.data + prefixlen, line + compl_col, compl_length);
+ compl_pattern.size = n - 1;
} else if (--startcol < 0
|| !vim_iswordp(mb_prevptr(line, line + startcol + 1))) {
// Match any word of at least two chars
- compl_pattern = xstrnsave(S_LEN("\\<\\k\\k"));
+ compl_pattern = cbuf_to_string(S_LEN("\\<\\k\\k"));
compl_col += curs_col;
compl_length = 0;
} else {
@@ -4111,19 +4251,20 @@ static int get_normal_compl_info(char *line, int startcol, colnr_T curs_col)
// Only match word with at least two chars -- webb
// there's no need to call quote_meta,
// xmalloc(7) is enough -- Acevedo
- compl_pattern = xmalloc(7);
- STRCPY(compl_pattern, "\\<");
- quote_meta(compl_pattern + 2, line + compl_col, 1);
- strcat(compl_pattern, "\\k");
+ compl_pattern.data = xmalloc(7);
+ STRCPY(compl_pattern.data, "\\<");
+ quote_meta(compl_pattern.data + 2, line + compl_col, 1);
+ strcat(compl_pattern.data, "\\k");
+ compl_pattern.size = strlen(compl_pattern.data);
} else {
- compl_pattern = xmalloc(quote_meta(NULL, line + compl_col, compl_length) + 2);
- STRCPY(compl_pattern, "\\<");
- quote_meta(compl_pattern + 2, line + compl_col, compl_length);
+ size_t n = quote_meta(NULL, line + compl_col, compl_length) + 2;
+ compl_pattern.data = xmalloc(n);
+ STRCPY(compl_pattern.data, "\\<");
+ quote_meta(compl_pattern.data + 2, line + compl_col, compl_length);
+ compl_pattern.size = n - 1;
}
}
- compl_patternlen = strlen(compl_pattern);
-
return OK;
}
@@ -4138,13 +4279,12 @@ static int get_wholeline_compl_info(char *line, colnr_T curs_col)
compl_length = 0;
}
if (p_ic) {
- compl_pattern = str_foldcase(line + compl_col, compl_length, NULL, 0);
+ compl_pattern = cstr_as_string(str_foldcase(line + compl_col,
+ compl_length, NULL, 0));
} else {
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
}
- compl_patternlen = strlen(compl_pattern);
-
return OK;
}
@@ -4169,8 +4309,8 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
compl_col += startcol;
compl_length = (int)curs_col - startcol;
- compl_pattern = addstar(line + compl_col, (size_t)compl_length, EXPAND_FILES);
- compl_patternlen = strlen(compl_pattern);
+ compl_pattern = cstr_as_string(addstar(line + compl_col,
+ (size_t)compl_length, EXPAND_FILES));
return OK;
}
@@ -4179,9 +4319,9 @@ static int get_filename_compl_info(char *line, int startcol, colnr_T curs_col)
/// Sets the global variables: compl_col, compl_length and compl_pattern.
static int get_cmdline_compl_info(char *line, colnr_T curs_col)
{
- compl_pattern = xstrnsave(line, (size_t)curs_col);
- compl_patternlen = (size_t)curs_col;
- set_cmd_context(&compl_xp, compl_pattern, (int)compl_patternlen, curs_col, false);
+ compl_pattern = cbuf_to_string(line, (size_t)curs_col);
+ set_cmd_context(&compl_xp, compl_pattern.data,
+ (int)compl_pattern.size, curs_col, false);
if (compl_xp.xp_context == EXPAND_LUA) {
nlua_expand_pat(&compl_xp);
}
@@ -4191,7 +4331,7 @@ static int get_cmdline_compl_info(char *line, colnr_T curs_col)
// "pattern not found" message.
compl_col = curs_col;
} else {
- compl_col = (int)(compl_xp.xp_pattern - compl_pattern);
+ compl_col = (int)(compl_xp.xp_pattern - compl_pattern.data);
}
compl_length = curs_col - compl_col;
@@ -4269,8 +4409,7 @@ static int get_userdefined_compl_info(colnr_T curs_col)
// it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum);
compl_length = curs_col - compl_col;
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
- compl_patternlen = (size_t)compl_length;
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -4295,8 +4434,7 @@ static int get_spell_compl_info(int startcol, colnr_T curs_col)
}
// Need to obtain "line" again, it may have become invalid.
char *line = ml_get(curwin->w_cursor.lnum);
- compl_pattern = xstrnsave(line + compl_col, (size_t)compl_length);
- compl_patternlen = (size_t)compl_length;
+ compl_pattern = cbuf_to_string(line + compl_col, (size_t)compl_length);
return OK;
}
@@ -4481,19 +4619,19 @@ static int ins_compl_start(void)
ins_compl_fixRedoBufForLeader(NULL);
// Always add completion for the original text.
- xfree(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
- compl_orig_text = xstrnsave(line + compl_col, (size_t)compl_length);
+ compl_orig_text = cbuf_to_string(line + compl_col, (size_t)compl_length);
save_orig_extmarks();
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, -1, -1) != OK) {
- XFREE_CLEAR(compl_pattern);
- compl_patternlen = 0;
- XFREE_CLEAR(compl_orig_text);
+ if (ins_compl_add(compl_orig_text.data, (int)compl_orig_text.size,
+ NULL, NULL, false, NULL, 0,
+ flags, false, NULL) != OK) {
+ API_CLEAR_STRING(compl_pattern);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
return FAIL;
}
@@ -4567,6 +4705,7 @@ static void ins_compl_show_statusmsg(void)
if (edit_submode_extra != NULL) {
if (!p_smd) {
msg_hist_off = true;
+ msg_ext_set_kind("completion");
msg(edit_submode_extra, (edit_submode_highl < HLF_COUNT
? (int)edit_submode_highl + 1 : 0));
msg_hist_off = false;
@@ -4726,7 +4865,7 @@ static unsigned quote_meta(char *dest, char *src, int len)
#if defined(EXITFREE)
void free_insexpand_stuff(void)
{
- XFREE_CLEAR(compl_orig_text);
+ API_CLEAR_STRING(compl_orig_text);
kv_destroy(compl_orig_extmarks);
callback_free(&cfu_cb);
callback_free(&ofu_cb);
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index f7215d3d12..a9f8c9222a 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -917,7 +917,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
result[dlen++] = (char)K_SPECIAL;
result[dlen++] = (char)KS_EXTRA;
result[dlen++] = KE_SNR;
- snprintf(result + dlen, buf_len - dlen, "%" PRId64, (int64_t)sid);
+ snprintf(result + dlen, buf_len - dlen, "%" PRIdSCID, sid);
dlen += strlen(result + dlen);
result[dlen++] = '_';
continue;
diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c
index 39dfb8eeb9..79f4c0d692 100644
--- a/src/nvim/linematch.c
+++ b/src/nvim/linematch.c
@@ -3,9 +3,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-#include <string.h>
-#include "nvim/ascii_defs.h"
#include "nvim/linematch.h"
#include "nvim/macros_defs.h"
#include "nvim/memory.h"
@@ -34,12 +32,8 @@ struct diffcmppath_S {
static size_t line_len(const mmfile_t *m)
{
char *s = m->ptr;
- size_t n = (size_t)m->size;
- char *end = strnchr(s, &n, '\n');
- if (end) {
- return (size_t)(end - s);
- }
- return (size_t)m->size;
+ char *end = memchr(s, '\n', (size_t)m->size);
+ return end ? (size_t)(end - s) : (size_t)m->size;
}
#define MATCH_CHAR_MAX_LEN 800
@@ -150,9 +144,9 @@ static int count_n_matched_chars(mmfile_t **sp, const size_t n, bool iwhite)
mmfile_t fastforward_buf_to_lnum(mmfile_t s, linenr_T lnum)
{
for (int i = 0; i < lnum - 1; i++) {
- size_t n = (size_t)s.size;
- s.ptr = strnchr(s.ptr, &n, '\n');
- s.size = (int)n;
+ char *line_end = memchr(s.ptr, '\n', (size_t)s.size);
+ s.size = line_end ? (int)(s.size - (line_end - s.ptr)) : 0;
+ s.ptr = line_end;
if (!s.ptr) {
break;
}
diff --git a/src/nvim/linematch.h b/src/nvim/linematch.h
index 5f6667a7df..08daf0e16c 100644
--- a/src/nvim/linematch.h
+++ b/src/nvim/linematch.h
@@ -3,7 +3,7 @@
#include <stddef.h> // IWYU pragma: keep
#include "nvim/pos_defs.h" // IWYU pragma: keep
-#include "xdiff/xdiff.h"
+#include "xdiff/xdiff.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "linematch.h.generated.h"
diff --git a/src/nvim/lua/api_wrappers.c b/src/nvim/lua/api_wrappers.c
index 36847d1fc9..447ba846b4 100644
--- a/src/nvim/lua/api_wrappers.c
+++ b/src/nvim/lua/api_wrappers.c
@@ -1,19 +1,19 @@
-#include <lauxlib.h>
-#include <lua.h>
-#include <lualib.h>
+#include <lauxlib.h> // IWYU pragma: keep
+#include <lua.h> // IWYU pragma: keep
+#include <lualib.h> // IWYU pragma: keep
-#include "nvim/api/private/defs.h"
-#include "nvim/api/private/dispatch.h"
-#include "nvim/api/private/helpers.h"
-#include "nvim/errors.h"
-#include "nvim/ex_docmd.h"
-#include "nvim/ex_getln.h"
-#include "nvim/func_attr.h"
-#include "nvim/globals.h"
-#include "nvim/lua/converter.h"
-#include "nvim/lua/executor.h"
-#include "nvim/memory.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
+#include "nvim/api/private/dispatch.h" // IWYU pragma: keep
+#include "nvim/api/private/helpers.h" // IWYU pragma: keep
+#include "nvim/errors.h" // IWYU pragma: keep
+#include "nvim/ex_docmd.h" // IWYU pragma: keep
+#include "nvim/ex_getln.h" // IWYU pragma: keep
+#include "nvim/func_attr.h" // IWYU pragma: keep
+#include "nvim/globals.h" // IWYU pragma: keep
+#include "nvim/lua/converter.h" // IWYU pragma: keep
+#include "nvim/lua/executor.h" // IWYU pragma: keep
+#include "nvim/memory.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "lua_api_c_bindings.generated.h"
+# include "lua_api_c_bindings.generated.h" // IWYU pragma: keep
#endif
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
index 45ead154bc..292588ee64 100644
--- a/src/nvim/lua/converter.c
+++ b/src/nvim/lua/converter.c
@@ -22,6 +22,7 @@
#include "nvim/lua/executor.h"
#include "nvim/macros_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index c4fa8b0fff..71c5cd4585 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -23,7 +23,6 @@
#include "nvim/cursor.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
-#include "nvim/eval.h"
#include "nvim/eval/funcs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
@@ -47,10 +46,12 @@
#include "nvim/lua/treesitter.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/memory_defs.h"
#include "nvim/message.h"
+#include "nvim/message_defs.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/option_vars.h"
#include "nvim/os/fileio.h"
@@ -275,10 +276,9 @@ static int nlua_luv_thread_common_cfpcall(lua_State *lstate, int nargs, int nres
#endif
}
const char *error = lua_tostring(lstate, -1);
-
loop_schedule_deferred(&main_loop,
event_create(nlua_luv_error_event,
- xstrdup(error),
+ error != NULL ? xstrdup(error) : NULL,
(void *)(intptr_t)(is_callback
? kThreadCallback
: kThread)));
@@ -957,7 +957,7 @@ static void nlua_print_event(void **argv)
HlMessage msg = KV_INITIAL_VALUE;
HlMessageChunk chunk = { { .data = argv[0], .size = (size_t)(intptr_t)argv[1] - 1 }, 0 };
kv_push(msg, chunk);
- msg_multihl(msg, "lua_print", true);
+ msg_multihl(msg, "lua_print", true, false);
}
/// Print as a Vim message
@@ -1587,8 +1587,8 @@ Object nlua_call_ref_ctx(bool fast, LuaRef ref, const char *name, Array args, Lu
if (nlua_fast_cfpcall(lstate, nargs, 1, -1) < 0) {
// error is already scheduled, set anyways to convey failure.
api_set_error(err, kErrorTypeException, "fast context failure");
+ return NIL;
}
- return NIL;
} else if (nlua_pcall(lstate, nargs, 1)) {
// if err is passed, the caller will deal with the error.
if (err) {
diff --git a/src/nvim/lua/executor.h b/src/nvim/lua/executor.h
index 32fde3853b..3a0c03412f 100644
--- a/src/nvim/lua/executor.h
+++ b/src/nvim/lua/executor.h
@@ -4,10 +4,10 @@
#include <lua.h>
#include <stdbool.h>
+#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
-#include "nvim/func_attr.h"
#include "nvim/macros_defs.h"
#include "nvim/types_defs.h"
#include "nvim/usercmd.h" // IWYU pragma: keep
diff --git a/src/nvim/lua/secure.c b/src/nvim/lua/secure.c
index 61277949c4..1560881b55 100644
--- a/src/nvim/lua/secure.c
+++ b/src/nvim/lua/secure.c
@@ -2,11 +2,11 @@
#include <stdbool.h>
#include <string.h>
+#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
#include "nvim/errors.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/lua/executor.h"
#include "nvim/lua/secure.h"
#include "nvim/memory.h"
diff --git a/src/nvim/lua/spell.c b/src/nvim/lua/spell.c
index f4dacd7a55..eec5892307 100644
--- a/src/nvim/lua/spell.c
+++ b/src/nvim/lua/spell.c
@@ -16,7 +16,7 @@
#include "nvim/spell.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "lua/spell.c.generated.h"
+# include "lua/spell.c.generated.h" // IWYU pragma: keep
#endif
int nlua_spell_check(lua_State *lstate)
diff --git a/src/nvim/lua/stdlib.c b/src/nvim/lua/stdlib.c
index e719d99640..2fc9367ead 100644
--- a/src/nvim/lua/stdlib.c
+++ b/src/nvim/lua/stdlib.c
@@ -18,11 +18,13 @@
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
+#include "nvim/autocmd_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fold.h"
@@ -619,41 +621,36 @@ static int nlua_with(lua_State *L)
int rets = 0;
cmdmod_T save_cmdmod = cmdmod;
+ CLEAR_FIELD(cmdmod);
cmdmod.cmod_flags = flags;
apply_cmdmod(&cmdmod);
- if (buf || win) {
- try_start();
- }
-
- aco_save_T aco;
- win_execute_T win_execute_args;
Error err = ERROR_INIT;
+ TRY_WRAP(&err, {
+ aco_save_T aco;
+ win_execute_T win_execute_args;
- if (win) {
- tabpage_T *tabpage = win_find_tabpage(win);
- if (!win_execute_before(&win_execute_args, win, tabpage)) {
- goto end;
+ if (win) {
+ tabpage_T *tabpage = win_find_tabpage(win);
+ if (!win_execute_before(&win_execute_args, win, tabpage)) {
+ goto end;
+ }
+ } else if (buf) {
+ aucmd_prepbuf(&aco, buf);
}
- } else if (buf) {
- aucmd_prepbuf(&aco, buf);
- }
- int s = lua_gettop(L);
- lua_pushvalue(L, 2);
- status = lua_pcall(L, 0, LUA_MULTRET, 0);
- rets = lua_gettop(L) - s;
+ int s = lua_gettop(L);
+ lua_pushvalue(L, 2);
+ status = lua_pcall(L, 0, LUA_MULTRET, 0);
+ rets = lua_gettop(L) - s;
- if (win) {
- win_execute_after(&win_execute_args);
- } else if (buf) {
- aucmd_restbuf(&aco);
- }
-
-end:
- if (buf || win) {
- try_end(&err);
- }
+ if (win) {
+ win_execute_after(&win_execute_args);
+ } else if (buf) {
+ aucmd_restbuf(&aco);
+ }
+ end:;
+ });
undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod;
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index c80e7b7672..3e33fcd142 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -17,10 +17,12 @@
#ifdef HAVE_WASMTIME
# include <wasm.h>
+
+# include "nvim/os/fs.h"
#endif
-#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
+#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
#include "nvim/lua/treesitter.h"
@@ -28,7 +30,6 @@
#include "nvim/map_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
-#include "nvim/os/fs.h"
#include "nvim/pos_defs.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
@@ -127,9 +128,9 @@ static const TSLanguage *load_language_from_object(lua_State *L, const char *pat
{
uv_lib_t lib;
if (uv_dlopen(path, &lib)) {
+ xstrlcpy(IObuff, uv_dlerror(&lib), sizeof(IObuff));
uv_dlclose(&lib);
- luaL_error(L, "Failed to load parser for language '%s': uv_dlopen: %s",
- lang_name, uv_dlerror(&lib));
+ luaL_error(L, "Failed to load parser for language '%s': uv_dlopen: %s", lang_name, IObuff);
}
char symbol_buf[128];
@@ -137,8 +138,9 @@ static const TSLanguage *load_language_from_object(lua_State *L, const char *pat
TSLanguage *(*lang_parser)(void);
if (uv_dlsym(&lib, symbol_buf, (void **)&lang_parser)) {
+ xstrlcpy(IObuff, uv_dlerror(&lib), sizeof(IObuff));
uv_dlclose(&lib);
- luaL_error(L, "Failed to load parser: uv_dlsym: %s", uv_dlerror(&lib));
+ luaL_error(L, "Failed to load parser: uv_dlsym: %s", IObuff);
}
TSLanguage *lang = lang_parser();
@@ -216,7 +218,7 @@ static int add_language(lua_State *L, bool is_wasm)
? load_language_from_wasm(L, path, lang_name)
: load_language_from_object(L, path, lang_name, symbol_name);
- uint32_t lang_version = ts_language_version(lang);
+ uint32_t lang_version = ts_language_abi_version(lang);
if (lang_version < TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION
|| lang_version > TREE_SITTER_LANGUAGE_VERSION) {
return luaL_error(L,
@@ -298,7 +300,7 @@ int tslua_inspect_lang(lua_State *L)
lua_pushboolean(L, ts_language_is_wasm(lang));
lua_setfield(L, -2, "_wasm");
- lua_pushinteger(L, ts_language_version(lang)); // [retval, version]
+ lua_pushinteger(L, ts_language_abi_version(lang)); // [retval, version]
lua_setfield(L, -2, "_abi_version");
return 1;
@@ -474,7 +476,7 @@ static int parser_parse(lua_State *L)
#undef BUFSIZE
}
- input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8 };
+ input = (TSInput){ (void *)buf, input_cb, TSInputEncodingUTF8, NULL };
new_tree = ts_parser_parse(p, old_tree, input);
break;
@@ -488,13 +490,18 @@ static int parser_parse(lua_State *L)
// Sometimes parsing fails (timeout, or wrong parser ABI)
// In those case, just return an error.
if (!new_tree) {
- return luaL_error(L, "An error occurred when parsing.");
+ if (ts_parser_timeout_micros(p) == 0) {
+ // No timeout set, must have had an error
+ return luaL_error(L, "An error occurred when parsing.");
+ }
+ return 0;
}
// The new tree will be pushed to the stack, without copy, ownership is now to the lua GC.
// Old tree is owned by lua GC since before
uint32_t n_ranges = 0;
- TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges) : NULL;
+ TSRange *changed = old_tree ? ts_tree_get_changed_ranges(old_tree, new_tree, &n_ranges)
+ : ts_tree_included_ranges(new_tree, &n_ranges);
push_tree(L, new_tree); // [tree]
@@ -831,7 +838,6 @@ static struct luaL_Reg node_meta[] = {
{ "named_descendant_for_range", node_named_descendant_for_range },
{ "parent", node_parent },
{ "__has_ancestor", __has_ancestor },
- { "child_containing_descendant", node_child_containing_descendant },
{ "child_with_descendant", node_child_with_descendant },
{ "iter_children", node_iter_children },
{ "next_sibling", node_next_sibling },
@@ -1175,15 +1181,6 @@ static int __has_ancestor(lua_State *L)
return 1;
}
-static int node_child_containing_descendant(lua_State *L)
-{
- TSNode node = node_check(L, 1);
- TSNode descendant = node_check(L, 2);
- TSNode child = ts_node_child_containing_descendant(node, descendant);
- push_node(L, child, 1);
- return 1;
-}
-
static int node_child_with_descendant(lua_State *L)
{
TSNode node = node_check(L, 1);
diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c
index b9f96abf73..f51c1a05cd 100644
--- a/src/nvim/lua/xdiff.c
+++ b/src/nvim/lua/xdiff.c
@@ -1,4 +1,5 @@
#include <lauxlib.h>
+#include <limits.h>
#include <lua.h>
#include <stdbool.h>
#include <stdint.h>
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 695bd4c95a..0bd4277d19 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -7,6 +7,7 @@
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -19,6 +20,7 @@
#endif
#include "auto/config.h" // IWYU pragma: keep
+#include "klib/kvec.h"
#include "nvim/api/extmark.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
@@ -79,9 +81,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
-#include "nvim/os/fileio.h"
-#include "nvim/os/fileio_defs.h"
#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
@@ -111,6 +110,9 @@
#ifdef MSWIN
# include "nvim/os/os_win_console.h"
+# ifndef _UCRT
+# error UCRT is the only supported C runtime on windows
+# endif
#endif
#if defined(MSWIN) && !defined(MAKE_LIB)
@@ -241,22 +243,10 @@ void early_init(mparm_T *paramp)
#ifdef MAKE_LIB
int nvim_main(int argc, char **argv); // silence -Wmissing-prototypes
int nvim_main(int argc, char **argv)
-#elif defined(MSWIN)
-int wmain(int argc, wchar_t **argv_w) // multibyte args on Windows. #7060
#else
int main(int argc, char **argv)
#endif
{
-#if defined(MSWIN) && !defined(MAKE_LIB)
- char **argv = xmalloc((size_t)argc * sizeof(char *));
- for (int i = 0; i < argc; i++) {
- char *buf = NULL;
- utf16_to_utf8(argv_w[i], -1, &buf);
- assert(buf);
- argv[i] = buf;
- }
-#endif
-
argv0 = argv[0];
if (!appname_is_valid()) {
@@ -370,7 +360,7 @@ int main(int argc, char **argv)
setbuf(stdout, NULL); // NOLINT(bugprone-unsafe-functions)
- full_screen = !silent_mode || exmode_active;
+ full_screen = !silent_mode;
// Set the default values for the options that use Rows and Columns.
win_init_size();
@@ -637,13 +627,14 @@ int main(int argc, char **argv)
}
// WORKAROUND(mhi): #3023
- if (cb_flags & CB_UNNAMEDMASK) {
+ if (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus)) {
eval_has_provider("clipboard", false);
}
if (params.luaf != NULL) {
// Like "--cmd", "+", "-c" and "-S", don't truncate messages.
msg_scroll = true;
+ DLOG("executing Lua -l script");
bool lua_ok = nlua_exec_file(params.luaf);
TIME_MSG("executing Lua -l script");
if (msg_didout) {
@@ -1237,6 +1228,9 @@ static void command_line_scan(mparm_T *parmp)
if (exmode_active) { // "-es" silent (batch) Ex-mode
silent_mode = true;
parmp->no_swap_file = true;
+ if (p_shadafile == NULL || *p_shadafile == NUL) {
+ set_option_value_give_err(kOptShadafile, STATIC_CSTR_AS_OPTVAL("NONE"), 0);
+ }
} else { // "-s {scriptin}" read from script file
want_argument = true;
}
@@ -2094,8 +2088,7 @@ static void source_startup_scripts(const mparm_T *const parmp)
{
// If -u given, use only the initializations from that file and nothing else.
if (parmp->use_vimrc != NULL) {
- if (strequal(parmp->use_vimrc, "NONE")
- || strequal(parmp->use_vimrc, "NORC")) {
+ if (strequal(parmp->use_vimrc, "NONE") || strequal(parmp->use_vimrc, "NORC")) {
// Do nothing.
} else {
if (do_source(parmp->use_vimrc, false, DOSO_NONE, NULL) != OK) {
diff --git a/src/nvim/main.h b/src/nvim/main.h
index dedfadf270..5ae5d98de4 100644
--- a/src/nvim/main.h
+++ b/src/nvim/main.h
@@ -2,7 +2,6 @@
#include <stdbool.h>
-#include "nvim/event/loop.h"
#include "nvim/types_defs.h"
// Maximum number of commands from + or -c arguments.
diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c
index 1a6b2c3581..ca0349d1f6 100644
--- a/src/nvim/mapping.c
+++ b/src/nvim/mapping.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/converter.h"
#include "nvim/api/private/defs.h"
@@ -32,7 +33,6 @@
#include "nvim/getchar_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
@@ -42,6 +42,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
@@ -581,6 +582,9 @@ static int buf_do_map(int maptype, MapArguments *args, int mode, bool is_abbrev,
const bool has_lhs = (args->lhs[0] != NUL);
const bool has_rhs = args->rhs_lua != LUA_NOREF || (args->rhs[0] != NUL) || args->rhs_is_noop;
const bool do_print = !has_lhs || (maptype != MAPTYPE_UNMAP && !has_rhs);
+ if (do_print) {
+ msg_ext_set_kind("list_cmd");
+ }
// check for :unmap without argument
if (maptype == MAPTYPE_UNMAP && !has_lhs) {
@@ -1897,7 +1901,7 @@ int makemap(FILE *fd, buf_T *buf)
// "what": 0 for :map lhs, 1 for :map rhs, 2 for :set
//
// return FAIL for failure, OK otherwise
-int put_escstr(FILE *fd, char *strstart, int what)
+int put_escstr(FILE *fd, const char *strstart, int what)
{
uint8_t *str = (uint8_t *)strstart;
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 3dbcbbd47b..d1ad920537 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -23,7 +23,6 @@
#include "nvim/fold.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
@@ -234,7 +233,7 @@ void setpcmark(void)
curwin->w_pcmark.lnum = 1;
}
- if (jop_flags & JOP_STACK) {
+ if (jop_flags & kOptJopFlagStack) {
// jumpoptions=stack: if we're somewhere in the middle of the jumplist
// discard everything after the current index.
if (curwin->w_jumplistidx < curwin->w_jumplistlen - 1) {
@@ -1473,7 +1472,7 @@ void cleanup_jumplist(win_T *wp, bool loadfiles)
mustfree = false;
} else if (i > from + 1) { // non-adjacent duplicate
// jumpoptions=stack: remove duplicates only when adjacent.
- mustfree = !(jop_flags & JOP_STACK);
+ mustfree = !(jop_flags & kOptJopFlagStack);
} else { // adjacent duplicate
mustfree = true;
}
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 555fef5bbd..5ccd4fd45d 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -2330,7 +2330,6 @@ void marktree_check(MarkTree *b)
#endif
}
-#ifndef NDEBUG
size_t marktree_check_node(MarkTree *b, MTNode *x, MTPos *last, bool *last_right,
const uint32_t *meta_node_ref)
{
@@ -2485,8 +2484,6 @@ bool mt_recurse_nodes_compare(MTNode *x, PMap(ptr_t) *checked)
return true;
}
-#endif
-
// TODO(bfredl): kv_print
#define GA_PUT(x) ga_concat(ga, (char *)(x))
#define GA_PRINT(fmt, ...) snprintf(buf, sizeof(buf), fmt, __VA_ARGS__); \
diff --git a/src/nvim/marktree.h b/src/nvim/marktree.h
index 15df57ef63..1fb1a74487 100644
--- a/src/nvim/marktree.h
+++ b/src/nvim/marktree.h
@@ -4,7 +4,6 @@
#include <stddef.h> // IWYU pragma: keep
#include <stdint.h>
-#include "nvim/buffer_defs.h"
#include "nvim/decoration_defs.h"
#include "nvim/marktree_defs.h" // IWYU pragma: keep
#include "nvim/pos_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/math.c b/src/nvim/math.c
index 4ca212413b..0b5886d36c 100644
--- a/src/nvim/math.c
+++ b/src/nvim/math.c
@@ -106,3 +106,9 @@ int vim_append_digit_int(int *value, int digit)
*value = x * 10 + digit;
return OK;
}
+
+/// Return something that fits into an int.
+int trim_to_int(int64_t x)
+{
+ return x > INT_MAX ? INT_MAX : x < INT_MIN ? INT_MIN : (int)x;
+}
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index 65f718f925..ffd4472b8a 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -26,6 +26,7 @@
#include <ctype.h>
#include <errno.h>
#include <iconv.h>
+#include <limits.h>
#include <locale.h>
#include <stdbool.h>
#include <stddef.h>
@@ -51,7 +52,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
#include "nvim/iconv_defs.h"
#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
@@ -1119,7 +1119,7 @@ int utf_char2bytes(const int c, char *const buf)
/// stateful algorithm to determine grapheme clusters. Still available
/// to support some legacy code which hasn't been refactored yet.
///
-/// To check if a char would combine with a preceeding space, use
+/// To check if a char would combine with a preceding space, use
/// utf_iscomposing_first() instead.
///
/// Based on code from Markus Kuhn.
@@ -1400,11 +1400,11 @@ int utf_fold(int a)
int mb_toupper(int a)
{
// If 'casemap' contains "keepascii" use ASCII style toupper().
- if (a < 128 && (cmp_flags & CMP_KEEPASCII)) {
+ if (a < 128 && (cmp_flags & kOptCmpFlagKeepascii)) {
return TOUPPER_ASC(a);
}
- if (!(cmp_flags & CMP_INTERNAL)) {
+ if (!(cmp_flags & kOptCmpFlagInternal)) {
return (int)towupper((wint_t)a);
}
@@ -1426,11 +1426,11 @@ bool mb_islower(int a)
int mb_tolower(int a)
{
// If 'casemap' contains "keepascii" use ASCII style tolower().
- if (a < 128 && (cmp_flags & CMP_KEEPASCII)) {
+ if (a < 128 && (cmp_flags & kOptCmpFlagKeepascii)) {
return TOLOWER_ASC(a);
}
- if (!(cmp_flags & CMP_INTERNAL)) {
+ if (!(cmp_flags & kOptCmpFlagInternal)) {
return (int)towlower((wint_t)a);
}
diff --git a/src/nvim/mbyte.h b/src/nvim/mbyte.h
index 2da051fca2..674e0a2638 100644
--- a/src/nvim/mbyte.h
+++ b/src/nvim/mbyte.h
@@ -12,7 +12,6 @@
#include "nvim/mbyte_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
-typedef utf8proc_int32_t GraphemeState;
#define GRAPHEME_STATE_INIT 0
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/mbyte_defs.h b/src/nvim/mbyte_defs.h
index 7f2d1ba6ce..8d5ff2a8c1 100644
--- a/src/nvim/mbyte_defs.h
+++ b/src/nvim/mbyte_defs.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include <utf8proc.h>
#include "nvim/iconv_defs.h"
@@ -71,3 +72,7 @@ typedef struct {
int8_t begin_off; ///< Offset to the first byte of the codepoint.
int8_t end_off; ///< Offset to one past the end byte of the codepoint.
} CharBoundsOff;
+
+typedef utf8proc_int32_t GraphemeState;
+
+enum { UNICODE_INVALID = 0xFFFD, };
diff --git a/src/nvim/memline.c b/src/nvim/memline.c
index bfe90bb680..fb7fdfb8b2 100644
--- a/src/nvim/memline.c
+++ b/src/nvim/memline.c
@@ -63,7 +63,6 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/macros_defs.h"
@@ -805,8 +804,7 @@ void ml_recover(bool checkext)
// list the names of the swapfiles
recover_names(fname, true, NULL, 0, NULL);
msg_putchar('\n');
- msg_puts(_("Enter number of swap file to use (0 to quit): "));
- i = get_number(false, NULL);
+ i = prompt_for_input(_("Enter number of swap file to use (0 to quit): "), 0, false, NULL);
if (i < 1 || i > len) {
goto theend;
}
@@ -1862,7 +1860,7 @@ int gchar_pos(pos_T *pos)
FUNC_ATTR_NONNULL_ARG(1)
{
// When searching columns is sometimes put at the end of a line.
- if (pos->col == MAXCOL) {
+ if (pos->col == MAXCOL || pos->col > ml_get_len(pos->lnum)) {
return NUL;
}
return utf_ptr2char(ml_get_pos(pos));
diff --git a/src/nvim/memory.c b/src/nvim/memory.c
index 3aa37c047c..fa102f4e12 100644
--- a/src/nvim/memory.c
+++ b/src/nvim/memory.c
@@ -34,7 +34,6 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
-#include "nvim/os/input.h"
#include "nvim/sign.h"
#include "nvim/state_defs.h"
#include "nvim/statusline.h"
diff --git a/src/nvim/memory.h b/src/nvim/memory.h
index a6ff82e39d..0955c5306c 100644
--- a/src/nvim/memory.h
+++ b/src/nvim/memory.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdint.h> // IWYU pragma: keep
+#include <string.h>
#include <time.h> // IWYU pragma: keep
#include "auto/config.h"
diff --git a/src/nvim/menu.c b/src/nvim/menu.c
index 4d3058ee44..5f14ea1eed 100644
--- a/src/nvim/menu.c
+++ b/src/nvim/menu.c
@@ -25,7 +25,6 @@
#include "nvim/getchar_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 47f33c8967..6fc102e4ff 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -2,6 +2,7 @@
#include <assert.h>
#include <inttypes.h>
+#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
@@ -26,6 +27,7 @@
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
#include "nvim/ex_cmds_defs.h"
+#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/fileio.h"
#include "nvim/garray.h"
@@ -45,6 +47,7 @@
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/ops.h"
@@ -89,11 +92,21 @@ static int confirm_msg_used = false; // displaying confirm_msg
# include "message.c.generated.h"
#endif
static char *confirm_msg = NULL; // ":confirm" message
-static char *confirm_msg_tail; // tail of confirm_msg
+static char *confirm_buttons; // ":confirm" buttons sent to cmdline as prompt
MessageHistoryEntry *first_msg_hist = NULL;
MessageHistoryEntry *last_msg_hist = NULL;
static int msg_hist_len = 0;
+static int msg_hist_max = 500; // The default max value is 500
+
+// args in 'messagesopt' option
+#define MESSAGES_OPT_HIT_ENTER "hit-enter"
+#define MESSAGES_OPT_WAIT "wait:"
+#define MESSAGES_OPT_HISTORY "history:"
+
+// The default is "hit-enter,history:500"
+static int msg_flags = kOptMoptFlagHitEnter | kOptMoptFlagHistory;
+static int msg_wait = 0;
static FILE *verbose_fd = NULL;
static bool verbose_did_open = false;
@@ -119,7 +132,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_hl_id()
+// msg_scrolled_ign true when msg_scrolled is non-zero and msg_puts_hl()
// 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.
@@ -140,8 +153,8 @@ static Array *msg_ext_chunks = NULL;
static garray_T msg_ext_last_chunk = GA_INIT(sizeof(char), 40);
static sattr_T msg_ext_last_attr = -1;
static int msg_ext_last_hl_id;
-static size_t msg_ext_cur_len = 0;
+static bool msg_ext_history = false; ///< message was added to history
static bool msg_ext_overwrite = false; ///< will overwrite last message
static int msg_ext_visible = 0; ///< number of messages currently visible
@@ -233,7 +246,7 @@ void msg_grid_validate(void)
int verb_msg(const char *s)
{
verbose_enter();
- int n = msg_hl_keep(s, 0, false, false);
+ int n = msg_keep(s, 0, false, false);
verbose_leave();
return n;
@@ -246,7 +259,7 @@ int verb_msg(const char *s)
bool msg(const char *s, const int hl_id)
FUNC_ATTR_NONNULL_ARG(1)
{
- return msg_hl_keep(s, hl_id, false, false);
+ return msg_keep(s, hl_id, false, false);
}
/// Similar to msg_outtrans_len, but support newlines and tabs.
@@ -279,26 +292,45 @@ void msg_multiline(String str, int hl_id, bool check_int, bool hist, bool *need_
}
}
-void msg_multihl(HlMessage hl_msg, const char *kind, bool history)
+// Avoid starting a new message for each chunk and adding message to history in msg_keep().
+static bool is_multihl = false;
+
+/// Print message chunks, each with their own highlight ID.
+///
+/// @param hl_msg Message chunks
+/// @param kind Message kind (can be NULL to avoid setting kind)
+/// @param history Whether to add message to history
+/// @param err Whether to print message as an error
+void msg_multihl(HlMessage hl_msg, const char *kind, bool history, bool err)
{
no_wait_return++;
msg_start();
msg_clr_eos();
bool need_clear = false;
- msg_ext_set_kind(kind);
+ msg_ext_history = history;
+ if (kind != NULL) {
+ msg_ext_set_kind(kind);
+ }
+ is_multihl = true;
for (uint32_t i = 0; i < kv_size(hl_msg); i++) {
HlMessageChunk chunk = kv_A(hl_msg, i);
- msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear);
+ if (err) {
+ emsg_multiline(chunk.text.data, kind, chunk.hl_id, true);
+ } else {
+ msg_multiline(chunk.text, chunk.hl_id, true, false, &need_clear);
+ }
+ assert(!ui_has(kUIMessages) || kind == NULL || msg_ext_kind == kind);
}
if (history && kv_size(hl_msg)) {
add_msg_hist_multihl(NULL, 0, 0, true, hl_msg);
}
+ is_multihl = false;
no_wait_return--;
msg_end();
}
/// @param keep set keep_msg if it doesn't scroll
-bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
+bool msg_keep(const char *s, int hl_id, bool keep, bool multiline)
FUNC_ATTR_NONNULL_ALL
{
static int entered = 0;
@@ -328,18 +360,19 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
}
entered++;
- // Add message to history (unless it's a repeated kept message or a
- // truncated message)
- if (s != keep_msg
- || (*s != '<'
- && last_msg_hist != NULL
- && last_msg_hist->msg != NULL
- && strcmp(s, last_msg_hist->msg) != 0)) {
+ // Add message to history (unless it's a truncated, repeated kept or multihl message).
+ if ((s != keep_msg
+ || (*s != '<'
+ && last_msg_hist != NULL
+ && last_msg_hist->msg != NULL
+ && strcmp(s, last_msg_hist->msg) != 0)) && !is_multihl) {
add_msg_hist(s, -1, hl_id, multiline);
}
+ if (!is_multihl) {
+ msg_start();
+ }
// Truncate the message if needed.
- msg_start();
char *buf = msg_strtrunc(s, false);
if (buf != NULL) {
s = buf;
@@ -354,7 +387,10 @@ bool msg_hl_keep(const char *s, int hl_id, bool keep, bool multiline)
if (need_clear) {
msg_clr_eos();
}
- bool retval = msg_end();
+ bool retval = true;
+ if (!is_multihl) {
+ retval = msg_end();
+ }
if (keep && retval && vim_strsize(s) < (Rows - cmdline_row - 1) * Columns + sc_col) {
set_keep_msg(s, 0);
@@ -503,7 +539,7 @@ int smsg(int hl_id, const char *s, ...)
return msg(IObuff, hl_id);
}
-int smsg_hl_keep(int hl_id, const char *s, ...)
+int smsg_keep(int hl_id, const char *s, ...)
FUNC_ATTR_PRINTF(2, 3)
{
va_list arglist;
@@ -511,7 +547,7 @@ int smsg_hl_keep(int hl_id, const char *s, ...)
va_start(arglist, s);
vim_vsnprintf(IObuff, IOSIZE, s, arglist);
va_end(arglist);
- return msg_hl_keep(IObuff, hl_id, true, false);
+ return msg_keep(IObuff, hl_id, true, false);
}
// Remember the last sourcing name/lnum used in an error message, so that it
@@ -604,6 +640,9 @@ void msg_source(int hl_id)
msg_scroll = true; // this will take more than one line
msg(p, hl_id);
xfree(p);
+ if (is_multihl) {
+ msg_start(); // avoided in msg_keep() but need the "msg_didout" newline here
+ }
}
p = get_emsg_lnum();
if (p != NULL) {
@@ -638,7 +677,7 @@ int emsg_not_now(void)
return false;
}
-bool emsg_multiline(const char *s, bool multiline)
+bool emsg_multiline(const char *s, const char *kind, int hl_id, bool multiline)
{
bool ignore = false;
@@ -736,23 +775,26 @@ bool emsg_multiline(const char *s, bool multiline)
}
emsg_on_display = true; // remember there is an error message
- int hl_id = 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
// and a redraw is expected because
// msg_scrolled is non-zero
if (msg_ext_kind == NULL) {
- msg_ext_set_kind("emsg");
+ msg_ext_set_kind(kind);
}
// Display name and line number for the source of the error.
msg_scroll = true;
msg_source(hl_id);
+ if (msg_ext_kind == NULL) {
+ msg_ext_set_kind(kind);
+ }
+
// Display the error message itself.
msg_nowait = false; // Wait for this msg.
- return msg_hl_keep(s, hl_id, false, multiline);
+ return msg_keep(s, hl_id, false, multiline);
}
/// emsg() - display an error message
@@ -763,7 +805,7 @@ bool emsg_multiline(const char *s, bool multiline)
/// @return true if wait_return() not called
bool emsg(const char *s)
{
- return emsg_multiline(s, false);
+ return emsg_multiline(s, "emsg", HLF_E, false);
}
void emsg_invreg(int name)
@@ -803,7 +845,7 @@ bool semsg_multiline(const char *const fmt, ...)
vim_vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
va_end(ap);
- ret = emsg_multiline(errbuf, true);
+ ret = emsg_multiline(errbuf, "emsg", HLF_E, true);
return ret;
}
@@ -887,7 +929,7 @@ void msg_schedule_semsg(const char *const fmt, ...)
static void msg_semsg_multiline_event(void **argv)
{
char *s = argv[0];
- emsg_multiline(s, true);
+ emsg_multiline(s, "emsg", HLF_E, true);
xfree(s);
}
@@ -973,28 +1015,24 @@ static void add_msg_hist(const char *s, int len, int hl_id, bool multiline)
static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multiline,
HlMessage multihl)
{
- if (msg_hist_off || msg_silent != 0) {
+ if (msg_hist_off || msg_silent != 0 || (s != NULL && *s == NUL)) {
hl_msg_free(multihl);
return;
}
- // Don't let the message history get too big
- while (msg_hist_len > p_mhi) {
- delete_first_msg();
- }
-
// allocate an entry and add the message at the end of the history
struct msg_hist *p = xmalloc(sizeof(struct msg_hist));
if (s) {
if (len < 0) {
len = (int)strlen(s);
}
+ assert(len > 0);
// remove leading and trailing newlines
- while (len > 0 && *s == '\n') {
+ while (*s == '\n') {
s++;
len--;
}
- while (len > 0 && s[len - 1] == '\n') {
+ while (s[len - 1] == '\n') {
len--;
}
p->msg = xmemdupz(s, (size_t)len);
@@ -1014,6 +1052,9 @@ static void add_msg_hist_multihl(const char *s, int len, int hl_id, bool multili
first_msg_hist = last_msg_hist;
}
msg_hist_len++;
+ msg_ext_history = true;
+
+ check_msg_hist();
}
/// Delete the first (oldest) message from the history.
@@ -1037,6 +1078,76 @@ int delete_first_msg(void)
return OK;
}
+static void check_msg_hist(void)
+{
+ // Don't let the message history get too big
+ while (msg_hist_len > 0 && msg_hist_len > msg_hist_max) {
+ (void)delete_first_msg();
+ }
+}
+
+int messagesopt_changed(void)
+{
+ int messages_flags_new = 0;
+ int messages_wait_new = 0;
+ int messages_history_new = 0;
+
+ char *p = p_mopt;
+ while (*p != NUL) {
+ if (strnequal(p, S_LEN(MESSAGES_OPT_HIT_ENTER))) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_HIT_ENTER);
+ messages_flags_new |= kOptMoptFlagHitEnter;
+ } else if (strnequal(p, S_LEN(MESSAGES_OPT_WAIT))
+ && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_WAIT)])) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_WAIT);
+ messages_wait_new = getdigits_int(&p, false, INT_MAX);
+ messages_flags_new |= kOptMoptFlagWait;
+ } else if (strnequal(p, S_LEN(MESSAGES_OPT_HISTORY))
+ && ascii_isdigit(p[STRLEN_LITERAL(MESSAGES_OPT_HISTORY)])) {
+ p += STRLEN_LITERAL(MESSAGES_OPT_HISTORY);
+ messages_history_new = getdigits_int(&p, false, INT_MAX);
+ messages_flags_new |= kOptMoptFlagHistory;
+ }
+
+ if (*p != ',' && *p != NUL) {
+ return FAIL;
+ }
+ if (*p == ',') {
+ p++;
+ }
+ }
+
+ // Either "wait" or "hit-enter" is required
+ if (!(messages_flags_new & (kOptMoptFlagHitEnter | kOptMoptFlagWait))) {
+ return FAIL;
+ }
+
+ // "history" must be set
+ if (!(messages_flags_new & kOptMoptFlagHistory)) {
+ return FAIL;
+ }
+
+ assert(messages_history_new >= 0);
+ // "history" must be <= 10000
+ if (messages_history_new > 10000) {
+ return FAIL;
+ }
+
+ assert(messages_wait_new >= 0);
+ // "wait" must be <= 10000
+ if (messages_wait_new > 10000) {
+ return FAIL;
+ }
+
+ msg_flags = messages_flags_new;
+ msg_wait = messages_wait_new;
+
+ msg_hist_max = messages_history_new;
+ check_msg_hist();
+
+ return OK;
+}
+
/// :messages command implementation
void ex_messages(exarg_T *eap)
FUNC_ATTR_NONNULL_ALL
@@ -1109,9 +1220,9 @@ void ex_messages(exarg_T *eap)
msg_hist_off = true;
for (; p != NULL && !got_int; p = p->next) {
if (kv_size(p->multihl)) {
- msg_multihl(p->multihl, p->kind, false);
+ msg_multihl(p->multihl, p->kind, false, false);
} else if (p->msg != NULL) {
- msg_hl_keep(p->msg, p->hl_id, false, p->multiline);
+ msg_keep(p->msg, p->hl_id, false, p->multiline);
}
}
msg_hist_off = false;
@@ -1200,83 +1311,88 @@ void wait_return(int redraw)
cmdline_row = Rows - 1;
}
- hit_return_msg(true);
-
- do {
- // Remember "got_int", if it is set vgetc() probably returns a
- // CTRL-C, but we need to loop then.
- had_got_int = got_int;
-
- // Don't do mappings here, we put the character back in the
- // typeahead buffer.
- no_mapping++;
- allow_keys++;
-
- // Temporarily disable Recording. If Recording is active, the
- // character will be recorded later, since it will be added to the
- // typebuf after the loop
- const int save_reg_recording = reg_recording;
- save_scriptout = scriptout;
- reg_recording = 0;
- scriptout = NULL;
- c = safe_vgetc();
- if (had_got_int && !global_busy) {
- got_int = false;
- }
- no_mapping--;
- allow_keys--;
- reg_recording = save_reg_recording;
- scriptout = save_scriptout;
-
- // Allow scrolling back in the messages.
- // Also accept scroll-down commands when messages fill the screen,
- // to avoid that typing one 'j' too many makes the messages
- // disappear.
- if (p_more) {
- if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
- || c == K_UP || c == K_PAGEUP) {
- if (msg_scrolled > Rows) {
- // scroll back to show older messages
- do_more_prompt(c);
- } else {
- msg_didout = false;
- c = K_IGNORE;
- msg_col = 0;
- }
- if (quit_more) {
- c = CAR; // just pretend CR was hit
- quit_more = false;
- got_int = false;
- } else if (c != K_IGNORE) {
+ if (msg_flags & kOptMoptFlagHitEnter) {
+ hit_return_msg(true);
+
+ do {
+ // Remember "got_int", if it is set vgetc() probably returns a
+ // CTRL-C, but we need to loop then.
+ had_got_int = got_int;
+
+ // Don't do mappings here, we put the character back in the
+ // typeahead buffer.
+ no_mapping++;
+ allow_keys++;
+
+ // Temporarily disable Recording. If Recording is active, the
+ // character will be recorded later, since it will be added to the
+ // typebuf after the loop
+ const int save_reg_recording = reg_recording;
+ save_scriptout = scriptout;
+ reg_recording = 0;
+ scriptout = NULL;
+ c = safe_vgetc();
+ if (had_got_int && !global_busy) {
+ got_int = false;
+ }
+ no_mapping--;
+ allow_keys--;
+ reg_recording = save_reg_recording;
+ scriptout = save_scriptout;
+
+ // Allow scrolling back in the messages.
+ // Also accept scroll-down commands when messages fill the screen,
+ // to avoid that typing one 'j' too many makes the messages
+ // disappear.
+ if (p_more) {
+ if (c == 'b' || c == 'k' || c == 'u' || c == 'g'
+ || c == K_UP || c == K_PAGEUP) {
+ if (msg_scrolled > Rows) {
+ // scroll back to show older messages
+ do_more_prompt(c);
+ } else {
+ msg_didout = false;
+ c = K_IGNORE;
+ msg_col = 0;
+ }
+ if (quit_more) {
+ c = CAR; // just pretend CR was hit
+ quit_more = false;
+ got_int = false;
+ } else if (c != K_IGNORE) {
+ c = K_IGNORE;
+ hit_return_msg(false);
+ }
+ } else if (msg_scrolled > Rows - 2
+ && (c == 'j' || c == 'd' || c == 'f'
+ || c == K_DOWN || c == K_PAGEDOWN)) {
c = K_IGNORE;
- hit_return_msg(false);
}
- } else if (msg_scrolled > Rows - 2
- && (c == 'j' || c == 'd' || c == 'f'
- || c == K_DOWN || c == K_PAGEDOWN)) {
- c = K_IGNORE;
}
+ } while ((had_got_int && c == Ctrl_C)
+ || c == K_IGNORE
+ || c == K_LEFTDRAG || c == K_LEFTRELEASE
+ || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
+ || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
+ || c == K_MOUSELEFT || c == K_MOUSERIGHT
+ || c == K_MOUSEDOWN || c == K_MOUSEUP
+ || c == K_MOUSEMOVE);
+ os_breakcheck();
+
+ // Avoid that the mouse-up event causes visual mode to start.
+ if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
+ || c == K_X1MOUSE || c == K_X2MOUSE) {
+ jump_to_mouse(MOUSE_SETPOS, NULL, 0);
+ } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
+ // Put the character back in the typeahead buffer. Don't use the
+ // stuff buffer, because lmaps wouldn't work.
+ ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
+ do_redraw = true; // need a redraw even though there is typeahead
}
- } while ((had_got_int && c == Ctrl_C)
- || c == K_IGNORE
- || c == K_LEFTDRAG || c == K_LEFTRELEASE
- || c == K_MIDDLEDRAG || c == K_MIDDLERELEASE
- || c == K_RIGHTDRAG || c == K_RIGHTRELEASE
- || c == K_MOUSELEFT || c == K_MOUSERIGHT
- || c == K_MOUSEDOWN || c == K_MOUSEUP
- || c == K_MOUSEMOVE);
- os_breakcheck();
-
- // Avoid that the mouse-up event causes visual mode to start.
- if (c == K_LEFTMOUSE || c == K_MIDDLEMOUSE || c == K_RIGHTMOUSE
- || c == K_X1MOUSE || c == K_X2MOUSE) {
- jump_to_mouse(MOUSE_SETPOS, NULL, 0);
- } else if (vim_strchr("\r\n ", c) == NULL && c != Ctrl_C) {
- // Put the character back in the typeahead buffer. Don't use the
- // stuff buffer, because lmaps wouldn't work.
- ins_char_typebuf(vgetc_char, vgetc_mod_mask, true);
- do_redraw = true; // need a redraw even though there is
- // typeahead
+ } else {
+ c = CAR;
+ // Wait to allow the user to verify the output.
+ do_sleep(msg_wait, true);
}
}
redir_off = false;
@@ -2016,12 +2132,12 @@ void msg_outtrans_long(const char *longstr, int hl_id)
int len = (int)strlen(longstr);
int slen = len;
int room = Columns - msg_col;
- if (len > room && room >= 20) {
+ if (!ui_has(kUIMessages) && len > room && room >= 20) {
slen = (room - 3) / 2;
msg_outtrans_len(longstr, slen, hl_id, false);
msg_puts_hl("...", HLF_8, false);
}
- msg_outtrans_len(longstr + len - slen, slen, hl_id, len);
+ msg_outtrans_len(longstr + len - slen, slen, hl_id, false);
}
/// Basic function for writing a message with highlight id.
@@ -2042,8 +2158,8 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
// If redirection is on, also write to the redirection file.
redir_write(str, len);
- // Don't print anything when using ":silent cmd".
- if (msg_silent != 0) {
+ // Don't print anything when using ":silent cmd" or empty message.
+ if (msg_silent != 0 || *str == NUL) {
return;
}
@@ -2091,27 +2207,6 @@ void msg_puts_len(const char *const str, const ptrdiff_t len, int hl_id, bool hi
need_fileinfo = false;
}
-/// Print a formatted message
-///
-/// Message printed is limited by #IOSIZE. Must not be used from inside
-/// msg_puts_hl_id().
-///
-/// @param[in] hl_id Highlight id.
-/// @param[in] fmt Format string.
-void msg_printf_hl(const int hl_id, const char *const fmt, ...)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PRINTF(2, 3)
-{
- static char msgbuf[IOSIZE];
-
- va_list ap;
- va_start(ap, fmt);
- const size_t len = (size_t)vim_vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
- va_end(ap);
-
- msg_scroll = true;
- msg_puts_len(msgbuf, (ptrdiff_t)len, hl_id, true);
-}
-
static void msg_ext_emit_chunk(void)
{
if (msg_ext_chunks == NULL) {
@@ -2150,7 +2245,13 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
// Concat pieces with the same highlight
size_t len = maxlen < 0 ? strlen(str) : strnlen(str, (size_t)maxlen);
ga_concat_len(&msg_ext_last_chunk, str, len);
- msg_ext_cur_len += len;
+
+ // Find last newline in the message and calculate the current message column
+ const char *lastline = strrchr(str, '\n');
+ maxlen -= (int)(lastline ? (lastline - str) : 0);
+ const char *p = lastline ? lastline + 1 : str;
+ int col = (int)(maxlen < 0 ? mb_string2cells(p) : mb_string2cells_len(p, (size_t)(maxlen)));
+ msg_col = (lastline ? 0 : msg_col) + col;
return;
}
@@ -2209,7 +2310,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
if (p_more && lines_left == 0 && State != MODE_HITRETURN
&& !msg_no_more && !exmode_active) {
if (do_more_prompt(NUL)) {
- s = confirm_msg_tail;
+ s = confirm_buttons;
}
if (quit_more) {
return;
@@ -2273,7 +2374,7 @@ static void msg_puts_display(const char *str, int maxlen, int hl_id, int recurse
}
} while (msg_col & 7);
} else if (c == BELL) { // beep (from ":sh")
- vim_beep(BO_SH);
+ vim_beep(kOptBoFlagShell);
}
}
}
@@ -2328,7 +2429,7 @@ int msg_scrollsize(void)
bool msg_do_throttle(void)
{
- return msg_use_grid() && !(rdb_flags & RDB_NOTHROTTLE);
+ return msg_use_grid() && !(rdb_flags & kOptRdbFlagNothrottle);
}
/// Scroll the screen up one line for displaying the next message line.
@@ -2593,7 +2694,7 @@ void show_sb_text(void)
// weird, typing a command without output results in one line.
msgchunk_T *mp = msg_sb_start(last_msgchunk);
if (mp == NULL || mp->sb_prev == NULL) {
- vim_beep(BO_MESS);
+ vim_beep(kOptBoFlagMess);
} else {
do_more_prompt('G');
wait_return(false);
@@ -2641,7 +2742,7 @@ static msgchunk_T *disp_sb_line(int row, msgchunk_T *smp)
}
/// @return true when messages should be printed to stdout/stderr:
-/// - "batch mode" ("silent mode", -es/-Es)
+/// - "batch mode" ("silent mode", -es/-Es/-l)
/// - no UI and not embedded
int msg_use_printf(void)
{
@@ -2701,7 +2802,7 @@ 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_buttons".
static bool do_more_prompt(int typed_char)
{
static bool entered = false;
@@ -3052,7 +3153,7 @@ static Array *msg_ext_init_chunks(void)
{
Array *tofree = msg_ext_chunks;
msg_ext_chunks = xcalloc(1, sizeof(*msg_ext_chunks));
- msg_ext_cur_len = 0;
+ msg_col = 0;
return tofree;
}
@@ -3066,13 +3167,14 @@ void msg_ext_ui_flush(void)
msg_ext_emit_chunk();
if (msg_ext_chunks->size > 0) {
Array *tofree = msg_ext_init_chunks();
- ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite);
+ ui_call_msg_show(cstr_as_string(msg_ext_kind), *tofree, msg_ext_overwrite, msg_ext_history);
api_free_array(*tofree);
xfree(tofree);
if (!msg_ext_overwrite) {
msg_ext_visible++;
}
msg_ext_overwrite = false;
+ msg_ext_history = false;
msg_ext_kind = NULL;
}
}
@@ -3081,7 +3183,11 @@ void msg_ext_flush_showmode(void)
{
// Showmode messages doesn't interrupt normal message flow, so we use
// separate event. Still reuse the same chunking logic, for simplicity.
- if (ui_has(kUIMessages)) {
+ // This is called unconditionally; check if we are emitting, or have
+ // emitted non-empty "content".
+ static bool clear = false;
+ if (ui_has(kUIMessages) && (msg_ext_last_attr != -1 || clear)) {
+ clear = msg_ext_last_attr != -1;
msg_ext_emit_chunk();
Array *tofree = msg_ext_init_chunks();
ui_call_msg_showmode(*tofree);
@@ -3228,6 +3334,10 @@ int redirecting(void)
|| redir_reg || redir_vname || capture_ga != NULL;
}
+// Save and restore message kind when emitting a verbose message.
+static const char *pre_verbose_kind = NULL;
+static const char *verbose_kind = "verbose";
+
/// Before giving verbose message.
/// Must always be called paired with verbose_leave()!
void verbose_enter(void)
@@ -3235,6 +3345,10 @@ void verbose_enter(void)
if (*p_vfile != NUL) {
msg_silent++;
}
+ if (msg_ext_kind != verbose_kind) {
+ pre_verbose_kind = msg_ext_kind;
+ msg_ext_set_kind("verbose");
+ }
}
/// After giving verbose message.
@@ -3246,14 +3360,17 @@ void verbose_leave(void)
msg_silent = 0;
}
}
+ if (pre_verbose_kind != NULL) {
+ msg_ext_set_kind(pre_verbose_kind);
+ pre_verbose_kind = NULL;
+ }
}
/// Like verbose_enter() and set msg_scroll when displaying the message.
void verbose_enter_scroll(void)
{
- if (*p_vfile != NUL) {
- msg_silent++;
- } else {
+ verbose_enter();
+ if (*p_vfile == NUL) {
// always scroll up, don't overwrite
msg_scroll = true;
}
@@ -3262,11 +3379,8 @@ void verbose_enter_scroll(void)
/// Like verbose_leave() and set cmdline_row when displaying the message.
void verbose_leave_scroll(void)
{
- if (*p_vfile != NUL) {
- if (--msg_silent < 0) {
- msg_silent = 0;
- }
- } else {
+ verbose_leave();
+ if (*p_vfile == NUL) {
cmdline_row = msg_row;
}
}
@@ -3360,14 +3474,6 @@ void msg_advance(int col)
msg_col = col; // for redirection, may fill it up later
return;
}
- if (ui_has(kUIMessages)) {
- // TODO(bfredl): use byte count as a basic proxy.
- // later on we might add proper support for formatted messages.
- while (msg_ext_cur_len < (size_t)col) {
- msg_putchar(' ');
- }
- return;
- }
col = MIN(col, Columns - 1); // not enough room
while (msg_col < col) {
msg_putchar(' ');
@@ -3424,10 +3530,10 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
}
// Get a typed character directly from the user.
- int c = get_keystroke(NULL);
+ int c = prompt_for_input(confirm_buttons, HLF_M, true, NULL);
switch (c) {
case CAR: // User accepts default option
- case NL:
+ case NUL:
retval = dfltbutton;
break;
case Ctrl_C: // User aborts/cancels
@@ -3436,6 +3542,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
break;
default: // Could be a hotkey?
if (c < 0) { // special keys are ignored here
+ msg_didout = msg_didany = false;
continue;
}
if (c == ':' && ex_cmd) {
@@ -3458,6 +3565,7 @@ int do_dialog(int type, const char *title, const char *message, const char *butt
break;
}
// No hotkey match, so keep waiting
+ msg_didout = msg_didany = false;
continue;
}
break;
@@ -3511,19 +3619,20 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
has_hotkey[0] = false;
// Compute the size of memory to allocate.
- int len = 0;
+ int msg_len = 0;
+ int button_len = 0;
int idx = 0;
const char *r = buttons;
while (*r) {
if (*r == DLG_BUTTON_SEP) {
- len += 3; // '\n' -> ', '; 'x' -> '(x)'
+ button_len += 3; // '\n' -> ', '; 'x' -> '(x)'
lenhotkey += HOTK_LEN; // each button needs a hotkey
if (idx < HAS_HOTKEY_LEN - 1) {
has_hotkey[++idx] = false;
}
} else if (*r == DLG_HOTKEY_CHAR) {
r++;
- len++; // '&a' -> '[a]'
+ button_len++; // '&a' -> '[a]'
if (idx < HAS_HOTKEY_LEN - 1) {
has_hotkey[idx] = true;
}
@@ -3533,21 +3642,22 @@ static char *console_dialog_alloc(const char *message, const char *buttons, bool
MB_PTR_ADV(r);
}
- len += (int)(strlen(message)
- + 2 // for the NL's
- + strlen(buttons)
- + 3); // for the ": " and NUL
- lenhotkey++; // for the NUL
+ msg_len += (int)strlen(message) + 3; // for the NL's and NUL
+ button_len += (int)strlen(buttons) + 3; // for the ": " and NUL
+ lenhotkey++; // for the NUL
// If no hotkey is specified, first char is used.
if (!has_hotkey[0]) {
- len += 2; // "x" -> "[x]"
+ button_len += 2; // "x" -> "[x]"
}
// Now allocate space for the strings
xfree(confirm_msg);
- confirm_msg = xmalloc((size_t)len);
- *confirm_msg = NUL;
+ confirm_msg = xmalloc((size_t)msg_len);
+ snprintf(confirm_msg, (size_t)msg_len, "\n%s\n", message);
+
+ xfree(confirm_buttons);
+ confirm_buttons = xmalloc((size_t)button_len);
return xmalloc((size_t)lenhotkey);
}
@@ -3565,42 +3675,34 @@ static char *msg_show_console_dialog(const char *message, const char *buttons, i
bool has_hotkey[HAS_HOTKEY_LEN] = { false };
char *hotk = console_dialog_alloc(message, buttons, has_hotkey);
- copy_hotkeys_and_msg(message, buttons, dfltbutton, has_hotkey, hotk);
+ copy_confirm_hotkeys(buttons, dfltbutton, has_hotkey, hotk);
display_confirm_msg();
return hotk;
}
-/// Copies hotkeys & dialog message into the memory allocated for it
+/// Copies hotkeys into the memory allocated for it
///
-/// @param message Message which will be part of the confirm_msg
/// @param buttons String containing button names
/// @param default_button_idx Number of default button
/// @param has_hotkey An element in this array is true if corresponding button
/// has a hotkey
/// @param[out] hotkeys_ptr Pointer to the memory location where hotkeys will be copied
-static void copy_hotkeys_and_msg(const char *message, const char *buttons, int default_button_idx,
+static void copy_confirm_hotkeys(const char *buttons, int default_button_idx,
const bool has_hotkey[], char *hotkeys_ptr)
{
- *confirm_msg = '\n';
- STRCPY(confirm_msg + 1, message);
-
- char *msgp = confirm_msg + 1 + strlen(message);
-
// Define first default hotkey. Keep the hotkey string NUL
// terminated to avoid reading past the end.
hotkeys_ptr[copy_char(buttons, hotkeys_ptr, true)] = NUL;
- // Remember where the choices start, displaying starts here when
- // "hotkeys_ptr" typed at the more prompt.
- confirm_msg_tail = msgp;
- *msgp++ = '\n';
-
bool first_hotkey = false; // Is the first char of button a hotkey
if (!has_hotkey[0]) {
first_hotkey = true; // If no hotkey is specified, first char is used
}
+ // Remember where the choices start, sent as prompt to cmdline.
+ char *msgp = confirm_buttons;
+
int idx = 0;
const char *r = buttons;
while (*r) {
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index 1289adfabb..b0ab235f6b 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -280,6 +280,54 @@ static int get_fpos_of_mouse(pos_T *mpos)
return IN_BUFFER;
}
+static int do_popup(int which_button, int m_pos_flag, pos_T m_pos)
+{
+ int jump_flags = 0;
+ if (strcmp(p_mousem, "popup_setpos") == 0) {
+ // First set the cursor position before showing the popup menu.
+ if (VIsual_active) {
+ // set MOUSE_MAY_STOP_VIS if we are outside the selection
+ // or the current window (might have false negative here)
+ if (m_pos_flag != IN_BUFFER) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else {
+ if (VIsual_mode == 'V') {
+ if ((curwin->w_cursor.lnum <= VIsual.lnum
+ && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
+ || (VIsual.lnum < curwin->w_cursor.lnum
+ && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ } else if ((ltoreq(curwin->w_cursor, VIsual)
+ && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
+ || (lt(VIsual, curwin->w_cursor)
+ && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ } else if (VIsual_mode == Ctrl_V) {
+ colnr_T leftcol, rightcol;
+ getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
+ getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
+ if (m_pos.col < leftcol || m_pos.col > rightcol) {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ }
+ } else {
+ jump_flags = MOUSE_MAY_STOP_VIS;
+ }
+ }
+ if (jump_flags) {
+ jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
+ redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
+ update_screen();
+ setcursor();
+ ui_flush(); // Update before showing popup menu
+ }
+ show_popupmenu();
+ got_click = false; // ignore release events
+ return jump_flags;
+}
+
/// Do the appropriate action for the current mouse click in the current mode.
/// Not used for Command-line mode.
///
@@ -324,24 +372,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
int which_button; // MOUSE_LEFT, _MIDDLE or _RIGHT
bool is_click; // If false it's a drag or release event
bool is_drag; // If true it's a drag event
- int jump_flags = 0; // flags for jump_to_mouse()
- pos_T start_visual;
- bool moved; // Has cursor moved?
- bool in_winbar; // mouse in window bar
- bool in_statuscol; // mouse in status column
- bool in_status_line; // mouse in status line
static bool in_tab_line = false; // mouse clicked in tab line
- bool in_sep_line; // mouse in vertical separator line
- int c1;
- win_T *old_curwin = curwin;
static pos_T orig_cursor;
- colnr_T leftcol, rightcol;
- pos_T end_visual;
- int old_active = VIsual_active;
- int old_mode = VIsual_mode;
- int regname;
-
- pos_T save_cursor = curwin->w_cursor;
while (true) {
which_button = get_mouse_button(KEY2TERMCAP1(c), &is_click, &is_drag);
@@ -434,12 +466,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
return false;
}
- if (oap != NULL) {
- regname = oap->regname;
- } else {
- regname = 0;
- }
-
+ int regname = oap != NULL ? oap->regname : 0;
// Middle mouse button does a 'put' of the selected text
if (which_button == MOUSE_MIDDLE) {
if (State == MODE_NORMAL) {
@@ -496,12 +523,10 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
}
}
+ // flags for jump_to_mouse()
// When dragging or button-up stay in the same window.
- if (!is_click) {
- jump_flags |= MOUSE_FOCUS | MOUSE_DID_MOVE;
- }
-
- start_visual.lnum = 0;
+ int jump_flags = is_click ? 0 : (MOUSE_FOCUS|MOUSE_DID_MOVE);
+ win_T *old_curwin = curwin;
if (tab_page_click_defs != NULL) { // only when initialized
// Check for clicking in the tab page line.
@@ -515,8 +540,8 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
// click in a tab selects that tab page
if (is_click && cmdwin_type == 0 && mouse_col < Columns) {
+ int tabnr = tab_page_click_defs[mouse_col].tabnr;
in_tab_line = true;
- c1 = tab_page_click_defs[mouse_col].tabnr;
switch (tab_page_click_defs[mouse_col].type) {
case kStlClickDisabled:
@@ -527,11 +552,11 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
// double click opens new page
end_visual_mode();
tabpage_new();
- tabpage_move(c1 == 0 ? 9999 : c1 - 1);
+ tabpage_move(tabnr == 0 ? 9999 : tabnr - 1);
} else {
// Go to specified tab page, or next one if not clicking
// on a label.
- goto_tabpage(c1);
+ goto_tabpage(tabnr);
// It's like clicking on the status line of a window.
if (curwin != old_curwin) {
@@ -542,7 +567,7 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
}
FALLTHROUGH;
case kStlClickTabClose:
- mouse_tab_close(c1);
+ mouse_tab_close(tabnr);
break;
case kStlClickFuncRun:
call_click_def_func(tab_page_click_defs, mouse_col, which_button);
@@ -556,75 +581,33 @@ bool do_mouse(oparg_T *oap, int c, int dir, int count, bool fixindent)
}
}
+ int m_pos_flag = 0;
+ pos_T m_pos = { 0 };
// When 'mousemodel' is "popup" or "popup_setpos", translate mouse events:
// right button up -> pop-up menu
// shift-left button -> right button
// alt-left button -> alt-right button
if (mouse_model_popup()) {
- pos_T m_pos;
- int m_pos_flag = get_fpos_of_mouse(&m_pos);
- if (m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL)) {
- goto popupexit;
- }
- if (which_button == MOUSE_RIGHT
- && !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
+ m_pos_flag = get_fpos_of_mouse(&m_pos);
+ if (!(m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL))
+ && which_button == MOUSE_RIGHT && !(mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) {
if (!is_click) {
// Ignore right button release events, only shows the popup
// menu on the button down event.
return false;
}
- jump_flags = 0;
- if (strcmp(p_mousem, "popup_setpos") == 0) {
- // First set the cursor position before showing the popup menu.
- if (VIsual_active) {
- // set MOUSE_MAY_STOP_VIS if we are outside the selection
- // or the current window (might have false negative here)
- if (m_pos_flag != IN_BUFFER) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- } else {
- if (VIsual_mode == 'V') {
- if ((curwin->w_cursor.lnum <= VIsual.lnum
- && (m_pos.lnum < curwin->w_cursor.lnum || VIsual.lnum < m_pos.lnum))
- || (VIsual.lnum < curwin->w_cursor.lnum
- && (m_pos.lnum < VIsual.lnum || curwin->w_cursor.lnum < m_pos.lnum))) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- }
- } else if ((ltoreq(curwin->w_cursor, VIsual)
- && (lt(m_pos, curwin->w_cursor) || lt(VIsual, m_pos)))
- || (lt(VIsual, curwin->w_cursor)
- && (lt(m_pos, VIsual) || lt(curwin->w_cursor, m_pos)))) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- } else if (VIsual_mode == Ctrl_V) {
- getvcols(curwin, &curwin->w_cursor, &VIsual, &leftcol, &rightcol);
- getvcol(curwin, &m_pos, NULL, &m_pos.col, NULL);
- if (m_pos.col < leftcol || m_pos.col > rightcol) {
- jump_flags = MOUSE_MAY_STOP_VIS;
- }
- }
- }
- } else {
- jump_flags = MOUSE_MAY_STOP_VIS;
- }
- }
- if (jump_flags) {
- jump_flags = jump_to_mouse(jump_flags, NULL, which_button);
- redraw_curbuf_later(VIsual_active ? UPD_INVERTED : UPD_VALID);
- update_screen();
- setcursor();
- ui_flush(); // Update before showing popup menu
- }
- show_popupmenu();
- got_click = false; // ignore release events
- return (jump_flags & CURSOR_MOVED) != 0;
+ return (do_popup(which_button, m_pos_flag, m_pos) & CURSOR_MOVED);
}
- if (which_button == MOUSE_LEFT
- && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT))) {
+ // Only do this translation when mouse is over the buffer text
+ if (!(m_pos_flag & (IN_STATUS_LINE|MOUSE_WINBAR|MOUSE_STATUSCOL))
+ && (which_button == MOUSE_LEFT && (mod_mask & (MOD_MASK_SHIFT|MOD_MASK_ALT)))) {
which_button = MOUSE_RIGHT;
mod_mask &= ~MOD_MASK_SHIFT;
}
}
-popupexit:
+ pos_T end_visual = { 0 };
+ pos_T start_visual = { 0 };
if ((State & (MODE_NORMAL | MODE_INSERT))
&& !(mod_mask & (MOD_MASK_SHIFT | MOD_MASK_CTRL))) {
if (which_button == MOUSE_LEFT) {
@@ -664,15 +647,15 @@ popupexit:
}
// JUMP!
- jump_flags = jump_to_mouse(jump_flags,
- oap == NULL ? NULL : &(oap->inclusive),
- which_button);
+ int old_active = VIsual_active;
+ pos_T save_cursor = curwin->w_cursor;
+ jump_flags = jump_to_mouse(jump_flags, oap == NULL ? NULL : &(oap->inclusive), which_button);
- moved = (jump_flags & CURSOR_MOVED);
- in_winbar = (jump_flags & MOUSE_WINBAR);
- in_statuscol = (jump_flags & MOUSE_STATUSCOL);
- in_status_line = (jump_flags & IN_STATUS_LINE);
- in_sep_line = (jump_flags & IN_SEP_LINE);
+ bool moved = (jump_flags & CURSOR_MOVED);
+ bool in_winbar = (jump_flags & MOUSE_WINBAR);
+ bool in_statuscol = (jump_flags & MOUSE_STATUSCOL);
+ bool in_status_line = (jump_flags & IN_STATUS_LINE);
+ bool in_sep_line = (jump_flags & IN_SEP_LINE);
if ((in_winbar || in_status_line || in_statuscol) && is_click) {
// Handle click event on window bar, status line or status column
@@ -705,6 +688,12 @@ popupexit:
if (click_defs != NULL) {
switch (click_defs[click_col].type) {
case kStlClickDisabled:
+ // If there is no click definition, still open the popupmenu for a
+ // statuscolumn click like a click in the sign/number column does.
+ if (in_statuscol && mouse_model_popup()
+ && which_button == MOUSE_RIGHT && !(mod_mask & (MOD_MASK_SHIFT|MOD_MASK_CTRL))) {
+ do_popup(which_button, m_pos_flag, m_pos);
+ }
break;
case kStlClickFuncRun:
call_click_def_func(click_defs, click_col, which_button);
@@ -715,7 +704,9 @@ popupexit:
}
}
- return false;
+ if (!(in_statuscol && (jump_flags & (MOUSE_FOLD_CLOSE|MOUSE_FOLD_OPEN)))) {
+ return false;
+ }
} else if (in_winbar || in_statuscol) {
// A drag or release event in the window bar and status column has no side effects.
return false;
@@ -760,6 +751,7 @@ popupexit:
mouse_row = 0;
}
+ int old_mode = VIsual_mode;
if (start_visual.lnum) { // right click in visual mode
linenr_T diff;
// When ALT is pressed make Visual mode blockwise.
@@ -770,6 +762,7 @@ popupexit:
// In Visual-block mode, divide the area in four, pick up the corner
// that is in the quarter that the cursor is in.
if (VIsual_mode == Ctrl_V) {
+ colnr_T leftcol, rightcol;
getvcols(curwin, &start_visual, &end_visual, &leftcol, &rightcol);
if (curwin->w_curswant > (leftcol + rightcol) / 2) {
end_visual.col = leftcol;
@@ -831,7 +824,6 @@ popupexit:
// Middle mouse click: Put text before cursor.
if (which_button == MOUSE_MIDDLE) {
- int c2;
if (regname == 0 && eval_has_provider("clipboard", false)) {
regname = '*';
}
@@ -843,6 +835,7 @@ popupexit:
dir = FORWARD;
}
+ int c1, c2;
if (fixindent) {
c1 = (dir == BACKWARD) ? '[' : ']';
c2 = 'p';
@@ -1258,8 +1251,6 @@ retnomove:
if (flags & MOUSE_SETPOS) {
goto retnomove; // ugly goto...
}
- win_T *old_curwin = curwin;
- pos_T old_cursor = curwin->w_cursor;
if (row < 0 || col < 0) { // check if it makes sense
return IN_UNKNOWN;
@@ -1299,13 +1290,15 @@ retnomove:
grid = mouse_grid;
}
+ win_T *old_curwin = curwin;
+ pos_T old_cursor = curwin->w_cursor;
if (!keep_focus) {
if (on_winbar) {
return IN_OTHER_WIN | MOUSE_WINBAR;
}
if (on_statuscol) {
- return IN_OTHER_WIN | MOUSE_STATUSCOL;
+ goto foldclick;
}
fdc = win_fdccol_count(wp);
@@ -1497,6 +1490,7 @@ retnomove:
}
}
+foldclick:;
colnr_T col_from_screen = -1;
int mouse_fold_flags = 0;
mouse_check_grid(&col_from_screen, &mouse_fold_flags);
@@ -1535,7 +1529,7 @@ retnomove:
*inclusive = false;
}
- count = IN_BUFFER;
+ count = on_statuscol ? (IN_OTHER_WIN|MOUSE_STATUSCOL) : IN_BUFFER;
if (curwin != old_curwin || curwin->w_cursor.lnum != old_cursor.lnum
|| curwin->w_cursor.col != old_cursor.col) {
count |= CURSOR_MOVED; // Cursor has moved
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 6324466dcc..afd569ba7d 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -10,8 +10,8 @@
#include <assert.h>
#include <limits.h>
#include <stdbool.h>
-#include <stddef.h>
#include <stdint.h>
+#include <stdlib.h>
#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
@@ -28,8 +28,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/highlight.h"
-#include "nvim/highlight_defs.h"
+#include "nvim/grid_defs.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
@@ -38,12 +37,12 @@
#include "nvim/mouse.h"
#include "nvim/move.h"
#include "nvim/normal.h"
+#include "nvim/normal_defs.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
-#include "nvim/search.h"
#include "nvim/strings.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
@@ -150,32 +149,27 @@ static void redraw_for_cursorline(win_T *wp)
}
}
-/// Redraw when w_virtcol changes and
+/// Redraw when 'concealcursor' is active, or when w_virtcol changes and:
/// - 'cursorcolumn' is set, or
/// - 'cursorlineopt' contains "screenline", or
-/// - 'concealcursor' is active, or
/// - Visual mode is active.
static void redraw_for_cursorcolumn(win_T *wp)
FUNC_ATTR_NONNULL_ALL
{
- if (wp->w_valid & VALID_VIRTCOL) {
- return;
- }
-
// If the cursor moves horizontally when 'concealcursor' is active, then the
// current line needs to be redrawn to calculate the correct cursor position.
if (wp->w_p_cole > 0 && conceal_cursor_line(wp)) {
redrawWinline(wp, wp->w_cursor.lnum);
}
- if (pum_visible()) {
+ if ((wp->w_valid & VALID_VIRTCOL) || pum_visible()) {
return;
}
if (wp->w_p_cuc) {
// When 'cursorcolumn' is set 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)) {
+ } else if (wp->w_p_cul && (wp->w_p_culopt_flags & kOptCuloptFlagScreenline)) {
// When 'cursorlineopt' contains "screenline" need to redraw with UPD_VALID.
redraw_later(wp, UPD_VALID);
}
@@ -2496,7 +2490,10 @@ int pagescroll(Direction dir, int count, bool half)
if (!nochange) {
// Place cursor at top or bottom of window.
validate_botline(curwin);
- curwin->w_cursor.lnum = (dir == FORWARD ? curwin->w_topline : curwin->w_botline - 1);
+ linenr_T lnum = (dir == FORWARD ? curwin->w_topline : curwin->w_botline - 1);
+ // In silent Ex mode the value of w_botline - 1 may be 0,
+ // but cursor lnum needs to be at least 1.
+ curwin->w_cursor.lnum = MAX(lnum, 1);
}
}
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index 626312b666..e38bc1896d 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -26,6 +26,7 @@
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/channel_defs.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/os/input.h"
#include "nvim/types_defs.h"
@@ -43,17 +44,20 @@
static void log_request(char *dir, uint64_t channel_id, uint32_t req_id, const char *name)
{
- DLOGN("RPC %s %" PRIu64 ": %s id=%u: %s\n", dir, channel_id, REQ, req_id, name);
+ logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, false, "%s %" PRIu64 ": %s id=%u: %s\n", dir, channel_id,
+ REQ, req_id, name);
}
static void log_response(char *dir, uint64_t channel_id, char *kind, uint32_t req_id)
{
- DLOGN("RPC %s %" PRIu64 ": %s id=%u\n", dir, channel_id, kind, req_id);
+ logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, false, "%s %" PRIu64 ": %s id=%u\n", dir, channel_id, kind,
+ req_id);
}
static void log_notify(char *dir, uint64_t channel_id, const char *name)
{
- DLOGN("RPC %s %" PRIu64 ": %s %s\n", dir, channel_id, NOT, name);
+ logmsg(LOGLVL_DBG, "RPC: ", NULL, -1, false, "%s %" PRIu64 ": %s %s\n", dir, channel_id, NOT,
+ name);
}
#else
diff --git a/src/nvim/msgpack_rpc/packer.c b/src/nvim/msgpack_rpc/packer.c
index b739f7ba28..e5eab91b34 100644
--- a/src/nvim/msgpack_rpc/packer.c
+++ b/src/nvim/msgpack_rpc/packer.c
@@ -1,8 +1,17 @@
#include <assert.h>
+#include <lauxlib.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros_defs.h"
+#include "nvim/memory.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "msgpack_rpc/packer.c.generated.h"
diff --git a/src/nvim/msgpack_rpc/packer.h b/src/nvim/msgpack_rpc/packer.h
index 299962bab4..da86f8e969 100644
--- a/src/nvim/msgpack_rpc/packer.h
+++ b/src/nvim/msgpack_rpc/packer.h
@@ -1,11 +1,11 @@
#pragma once
#include <stdbool.h>
-#include <stddef.h>
+#include <stddef.h> // IWYU pragma: keep
#include <stdint.h>
-#include <string.h>
+#include <string.h> // IWYU pragma: keep
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/msgpack_rpc/packer_defs.h"
#define mpack_w(b, byte) *(*(b))++ = (char)(byte);
diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c
index 462f8397f4..b2c6d0d007 100644
--- a/src/nvim/msgpack_rpc/server.c
+++ b/src/nvim/msgpack_rpc/server.c
@@ -17,6 +17,7 @@
#include "nvim/memory.h"
#include "nvim/msgpack_rpc/server.h"
#include "nvim/os/os.h"
+#include "nvim/os/os_defs.h"
#include "nvim/os/stdpaths_defs.h"
#include "nvim/types_defs.h"
diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c
index 4ddc41e596..185a1abccb 100644
--- a/src/nvim/msgpack_rpc/unpacker.c
+++ b/src/nvim/msgpack_rpc/unpacker.c
@@ -1,6 +1,7 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
+#include <uv.h>
#include "klib/kvec.h"
#include "mpack/conv.h"
diff --git a/src/nvim/msgpack_rpc/unpacker.h b/src/nvim/msgpack_rpc/unpacker.h
index c29462292f..a9cd7e4652 100644
--- a/src/nvim/msgpack_rpc/unpacker.h
+++ b/src/nvim/msgpack_rpc/unpacker.h
@@ -1,8 +1,10 @@
#pragma once
#include <inttypes.h>
+#include <stdbool.h>
#include <string.h>
+#include "klib/kvec.h"
#include "mpack/mpack_core.h"
#include "mpack/object.h"
#include "nvim/api/private/defs.h"
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 55aa385b33..7d0080622d 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -52,6 +52,7 @@
#include "nvim/mark_defs.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memline_defs.h"
#include "nvim/memory.h"
@@ -96,7 +97,7 @@ typedef struct {
bool previous_got_int; // `got_int` was true
bool cmdwin; // command-line window normal mode
bool noexmode; // true if the normal mode was pushed from
- // ex mode(:global or :visual for example)
+ // ex mode (:global or :visual for example)
bool toplevel; // top-level normal mode
oparg_T oa; // operator arguments
cmdarg_T ca; // command arguments
@@ -503,9 +504,9 @@ bool op_pending(void)
/// Normal state entry point. This is called on:
///
/// - Startup, In this case the function never returns.
-/// - The command-line window is opened(`q:`). Returns when `cmdwin_result` != 0.
+/// - The command-line window is opened (`q:`). Returns when `cmdwin_result` != 0.
/// - The :visual command is called from :global in ex mode, `:global/PAT/visual`
-/// for example. Returns when re-entering ex mode(because ex mode recursion is
+/// for example. Returns when re-entering ex mode (because ex mode recursion is
/// not allowed)
///
/// This used to be called main_loop() on main.c
@@ -641,8 +642,7 @@ static bool normal_need_redraw_mode_message(NormalState *s)
return (
// 'showmode' is set and messages can be printed
((p_smd && msg_silent == 0
- // must restart insert mode(ctrl+o or ctrl+l) or we just entered visual
- // mode
+ // must restart insert mode (ctrl+o or ctrl+l) or just entered visual mode
&& (restart_edit != 0 || (VIsual_active
&& s->old_pos.lnum == curwin->w_cursor.lnum
&& s->old_pos.col == curwin->w_cursor.col))
@@ -812,7 +812,7 @@ static void normal_get_additional_char(NormalState *s)
// There is a busy wait here when typing "f<C-\>" and then
// something different from CTRL-N. Can't be avoided.
while ((s->c = vpeekc()) <= 0 && towait > 0) {
- do_sleep(towait > 50 ? 50 : towait);
+ do_sleep(towait > 50 ? 50 : towait, false);
towait -= 50;
}
if (s->c > 0) {
@@ -1348,7 +1348,7 @@ static void normal_check_folds(NormalState *s)
if (hasAnyFolding(curwin) && !char_avail()) {
foldCheckClose();
- if (fdo_flags & FDO_ALL) {
+ if (fdo_flags & kOptFdoFlagAll) {
foldOpenCursor();
}
}
@@ -1372,10 +1372,6 @@ static void normal_redraw(NormalState *s)
}
}
- if (need_maketitle) {
- maketitle();
- }
-
curbuf->b_last_used = time(NULL);
// Display message after redraw. If an external message is still visible,
@@ -2310,7 +2306,7 @@ static void nv_gd(oparg_T *oap, int nchar, int thisblock)
return;
}
- if ((fdo_flags & FDO_SEARCH) && KeyTyped && oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagSearch) && KeyTyped && oap->op_type == OP_NOP) {
foldOpenCursor();
}
// clear any search statistics
@@ -3752,7 +3748,7 @@ static void nv_right(cmdarg_T *cap)
}
}
}
- if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
+ if (n != cap->count1 && (fdo_flags & kOptFdoFlagHor) && KeyTyped
&& cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
@@ -3811,7 +3807,7 @@ static void nv_left(cmdarg_T *cap)
break;
}
}
- if (n != cap->count1 && (fdo_flags & FDO_HOR) && KeyTyped
+ if (n != cap->count1 && (fdo_flags & kOptFdoFlagHor) && KeyTyped
&& cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
@@ -3929,7 +3925,7 @@ static void nv_dollar(cmdarg_T *cap)
if (cursor_down(cap->count1 - 1,
cap->oap->op_type == OP_NOP) == false) {
clearopbeep(cap->oap);
- } else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ } else if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4016,7 +4012,7 @@ static int normal_search(cmdarg_T *cap, int dir, char *pat, size_t patlen, int o
cap->oap->motion_type = kMTLineWise;
}
curwin->w_cursor.coladd = 0;
- if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
+ if (cap->oap->op_type == OP_NOP && (fdo_flags & kOptFdoFlagSearch) && KeyTyped) {
foldOpenCursor();
}
}
@@ -4065,7 +4061,7 @@ static void nv_csearch(cmdarg_T *cap)
curwin->w_cursor.coladd = 0;
}
adjust_for_sel(cap);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4181,7 +4177,7 @@ static void nv_bracket_block(cmdarg_T *cap, const pos_T *old_pos)
setpcmark();
curwin->w_cursor = *pos;
curwin->w_set_curswant = true;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped
&& cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
@@ -4261,7 +4257,7 @@ static void nv_brackets(cmdarg_T *cap)
if (cap->oap->op_type == OP_NOP) {
beginline(BL_WHITE | BL_FIX);
}
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4319,7 +4315,7 @@ static void nv_brackets(cmdarg_T *cap)
}
curwin->w_set_curswant = true;
}
- if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) {
+ if (cap->oap->op_type == OP_NOP && (fdo_flags & kOptFdoFlagSearch) && KeyTyped) {
foldOpenCursor();
}
} else {
@@ -4371,7 +4367,7 @@ static void nv_percent(cmdarg_T *cap)
}
if (cap->oap->op_type == OP_NOP
&& lnum != curwin->w_cursor.lnum
- && (fdo_flags & FDO_PERCENT)
+ && (fdo_flags & kOptFdoFlagPercent)
&& KeyTyped) {
foldOpenCursor();
}
@@ -4395,7 +4391,7 @@ static void nv_brace(cmdarg_T *cap)
// Don't leave the cursor on the NUL past end of line.
adjust_cursor(cap->oap);
curwin->w_cursor.coladd = 0;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4426,7 +4422,7 @@ static void nv_findpar(cmdarg_T *cap)
}
curwin->w_cursor.coladd = 0;
- if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagBlock) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -4864,7 +4860,7 @@ static void nv_optrans(cmdarg_T *cap)
static void nv_gomark(cmdarg_T *cap)
{
int name;
- MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMove flags = jop_flags & kOptJopFlagView ? kMarkSetView : 0; // flags for moving to the mark
if (cap->oap->op_type != OP_NOP) {
// When there is a pending operator, do not restore the view as this is usually unexpected.
flags = 0;
@@ -4893,7 +4889,7 @@ static void nv_gomark(cmdarg_T *cap)
if (cap->oap->op_type == OP_NOP
&& move_res & kMarkMoveSuccess
&& (move_res & kMarkSwitchedBuf || move_res & kMarkChangedCursor)
- && (fdo_flags & FDO_MARK)
+ && (fdo_flags & kOptFdoFlagMark)
&& old_KeyTyped) {
foldOpenCursor();
}
@@ -4904,7 +4900,7 @@ static void nv_gomark(cmdarg_T *cap)
static void nv_pcmark(cmdarg_T *cap)
{
fmark_T *fm = NULL;
- MarkMove flags = jop_flags & JOP_VIEW ? kMarkSetView : 0; // flags for moving to the mark
+ MarkMove flags = jop_flags & kOptJopFlagView ? kMarkSetView : 0; // flags for moving to the mark
MarkMoveRes move_res = 0; // Result from moving to the mark
const bool old_KeyTyped = KeyTyped; // getting file may reset it.
@@ -4943,7 +4939,7 @@ static void nv_pcmark(cmdarg_T *cap)
}
if (cap->oap->op_type == OP_NOP
&& (move_res & kMarkSwitchedBuf || move_res & kMarkChangedLine)
- && (fdo_flags & FDO_MARK)
+ && (fdo_flags & kOptFdoFlagMark)
&& old_KeyTyped) {
foldOpenCursor();
}
@@ -5042,6 +5038,9 @@ static void nv_visual(cmdarg_T *cap)
assert(cap->count0 >= INT_MIN && cap->count0 <= INT_MAX);
curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1;
curwin->w_cursor.lnum = lnum;
+ if (*p_sel == 'e') {
+ curwin->w_curswant++;
+ }
coladvance(curwin, curwin->w_curswant);
} else {
curwin->w_set_curswant = true;
@@ -5094,7 +5093,7 @@ static void n_start_visual_mode(int c)
// Corner case: the 0 position in a tab may change when going into
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
//
- if (c == Ctrl_V && (get_ve_flags(curwin) & VE_BLOCK) && gchar_cursor() == TAB) {
+ if (c == Ctrl_V && (get_ve_flags(curwin) & kOptVeFlagBlock) && gchar_cursor() == TAB) {
validate_virtcol(curwin);
coladvance(curwin, curwin->w_virtcol);
}
@@ -5561,7 +5560,7 @@ static void nv_g_cmd(cmdarg_T *cap)
// "gs": Goto sleep.
case 's':
- do_sleep(cap->count1 * 1000);
+ do_sleep(cap->count1 * 1000, false);
break;
// "ga": Display the ascii value of the character under the
@@ -5915,7 +5914,7 @@ static void nv_bck_word(cmdarg_T *cap)
curwin->w_set_curswant = true;
if (bck_word(cap->count1, cap->arg, false) == false) {
clearopbeep(cap->oap);
- } else if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ } else if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -5975,7 +5974,7 @@ static void nv_wordcmd(cmdarg_T *cap)
clearopbeep(cap->oap);
} else {
adjust_for_sel(cap);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -5993,7 +5992,7 @@ static void adjust_cursor(oparg_T *oap)
if (curwin->w_cursor.col > 0 && gchar_cursor() == NUL
&& (!VIsual_active || *p_sel == 'o')
&& !virtual_active(curwin)
- && (get_ve_flags(curwin) & VE_ONEMORE) == 0) {
+ && (get_ve_flags(curwin) & kOptVeFlagOnemore) == 0) {
curwin->w_cursor.col--;
// prevent cursor from moving on the trail byte
mb_adjust_cursor();
@@ -6008,7 +6007,7 @@ static void nv_beginline(cmdarg_T *cap)
cap->oap->motion_type = kMTCharWise;
cap->oap->inclusive = false;
beginline(cap->arg);
- if ((fdo_flags & FDO_HOR) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagHor) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
ins_at_eol = false; // Don't move cursor past eol (only necessary in a
@@ -6097,7 +6096,7 @@ static void nv_goto(cmdarg_T *cap)
lnum = MIN(MAX(lnum, 1), curbuf->b_ml.ml_line_count);
curwin->w_cursor.lnum = lnum;
beginline(BL_SOL | BL_FIX);
- if ((fdo_flags & FDO_JUMP) && KeyTyped && cap->oap->op_type == OP_NOP) {
+ if ((fdo_flags & kOptFdoFlagJump) && KeyTyped && cap->oap->op_type == OP_NOP) {
foldOpenCursor();
}
}
@@ -6167,7 +6166,7 @@ static void nv_esc(cmdarg_T *cap)
curwin->w_set_curswant = true;
redraw_curbuf_later(UPD_INVERTED);
} else if (no_reason) {
- vim_beep(BO_ESC);
+ vim_beep(kOptBoFlagEsc);
}
clearop(cap->oap);
}
@@ -6176,7 +6175,7 @@ static void nv_esc(cmdarg_T *cap)
void set_cursor_for_append_to_line(void)
{
curwin->w_set_curswant = true;
- if (get_ve_flags(curwin) == VE_ALL) {
+ if (get_ve_flags(curwin) == kOptVeFlagAll) {
const int save_State = State;
// Pretend Insert mode here to allow the cursor on the
// character past the end of the line
@@ -6501,7 +6500,8 @@ static void nv_put_opt(cmdarg_T *cap, bool fix_indent)
int regname = cap->oap->regname;
bool keep_registers = cap->cmdchar == 'P';
// '+' and '*' could be the same selection
- bool clipoverwrite = (regname == '+' || regname == '*') && (cb_flags & CB_UNNAMEDMASK);
+ bool clipoverwrite = (regname == '+' || regname == '*')
+ && (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus));
if (regname == 0 || regname == '"' || clipoverwrite
|| ascii_isdigit(regname) || regname == '-') {
// The delete might overwrite the register we want to put, save it first
@@ -6624,8 +6624,8 @@ static void nv_event(cmdarg_T *cap)
// `input_get` branch was not executed (!multiqueue_empty(loop.events), which
// could have `may_garbage_collect` set to true in `normal_check`).
//
- // That is because here we may run code that calls `input_get`
- // later(`f_confirm` or `get_keystroke` for example), but in these cases it is
+ // That is because here we may run code that calls `input_get` later
+ // (`f_confirm` or `get_keystroke` for example), but in these cases it is
// not safe to perform garbage collection because there could be unreferenced
// lists or dicts being used.
may_garbage_collect = false;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 7dd3f665ba..2f45d862c3 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -29,6 +29,7 @@
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_getln.h"
@@ -41,7 +42,6 @@
#include "nvim/getchar_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -49,6 +49,7 @@
#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
+#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
@@ -78,6 +79,7 @@
#include "nvim/vim_defs.h"
#include "nvim/window.h"
#include "nvim/yankmap.h"
+#include "nvim/window.h"
struct yank_registers {
yankmap_T inner;
@@ -274,7 +276,7 @@ void op_shift(oparg_T *oap, bool curs_top, int amount)
vim_snprintf(IObuff, IOSIZE,
NGETTEXT(msg_line_single, msg_line_plural, oap->line_count),
(int64_t)oap->line_count, op, amount);
- msg_hl_keep(IObuff, 0, true, false);
+ msg_keep(IObuff, 0, true, false);
}
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
@@ -290,21 +292,53 @@ void op_shift(oparg_T *oap, bool curs_top, int amount)
changed_lines(curbuf, oap->start.lnum, 0, oap->end.lnum + 1, 0, true);
}
-/// Shift the current line one shiftwidth left (if left != 0) or right
-/// leaves cursor on first blank in the line.
-///
-/// @param call_changed_bytes call changed_bytes()
-void shift_line(bool left, bool round, int amount, int call_changed_bytes)
+/// Return the tabstop width at the index of the variable tabstop array. If an
+/// index greater than the length of the array is given, the last tabstop width
+/// in the array is returned.
+static int get_vts(const int *vts_array, int index)
{
- int sw_val = get_sw_value_indent(curbuf, left);
- if (sw_val == 0) {
- sw_val = 1; // shouldn't happen, just in case
+ int ts;
+
+ if (index < 1) {
+ ts = 0;
+ } else if (index <= vts_array[0]) {
+ ts = vts_array[index];
+ } else {
+ ts = vts_array[vts_array[0]];
+ }
+
+ return ts;
+}
+
+/// Return the sum of all the tabstops through the index-th.
+static int get_vts_sum(const int *vts_array, int index)
+{
+ int sum = 0;
+ int i;
+
+ // Perform the summation for indeces within the actual array.
+ for (i = 1; i <= index && i <= vts_array[0]; i++) {
+ sum += vts_array[i];
}
- int count = get_indent(); // get current indent
+
+ // Add topstops whose indeces exceed the actual array.
+ if (i <= index) {
+ sum += vts_array[vts_array[0]] * (index - vts_array[0]);
+ }
+
+ return sum;
+}
+
+/// @param left true if shift is to the left
+/// @param count true if new indent is to be to a tabstop
+/// @param amount number of shifts
+static int64_t get_new_sw_indent(bool left, bool round, int64_t amount, int64_t sw_val)
+{
+ int64_t count = get_indent(); // get current indent
if (round) { // round off indent
- int i = count / sw_val; // number of 'shiftwidth' rounded down
- int j = count % sw_val; // extra spaces
+ int64_t i = trim_to_int(count / sw_val); // number of 'shiftwidth' rounded down
+ int64_t j = trim_to_int(count % sw_val); // extra spaces
if (j && left) { // first remove extra spaces
amount--;
}
@@ -322,11 +356,94 @@ void shift_line(bool left, bool round, int amount, int call_changed_bytes)
}
}
+ return count;
+}
+
+/// @param left true if shift is to the left
+/// @param count true if new indent is to be to a tabstop
+/// @param amount number of shifts
+static int64_t get_new_vts_indent(bool left, bool round, int amount, int *vts_array)
+{
+ int64_t indent = get_indent();
+ int vtsi = 0;
+ int vts_indent = 0;
+ int ts = 0; // Silence uninitialized variable warning.
+
+ // Find the tabstop at or to the left of the current indent.
+ while (vts_indent <= indent) {
+ vtsi++;
+ ts = get_vts(vts_array, vtsi);
+ vts_indent += ts;
+ }
+ vts_indent -= ts;
+ vtsi--;
+
+ // Extra indent spaces to the right of the tabstop
+ int64_t offset = indent - vts_indent;
+
+ if (round) {
+ if (left) {
+ if (offset == 0) {
+ indent = get_vts_sum(vts_array, vtsi - amount);
+ } else {
+ indent = get_vts_sum(vts_array, vtsi - (amount - 1));
+ }
+ } else {
+ indent = get_vts_sum(vts_array, vtsi + amount);
+ }
+ } else {
+ if (left) {
+ if (amount > vtsi) {
+ indent = 0;
+ } else {
+ indent = get_vts_sum(vts_array, vtsi - amount) + offset;
+ }
+ } else {
+ indent = get_vts_sum(vts_array, vtsi + amount) + offset;
+ }
+ }
+
+ return indent;
+}
+
+/// Shift the current line 'amount' shiftwidth(s) left (if 'left' is true) or
+/// right.
+///
+/// The rules for choosing a shiftwidth are: If 'shiftwidth' is non-zero, use
+/// 'shiftwidth'; else if 'vartabstop' is not empty, use 'vartabstop'; else use
+/// 'tabstop'. The Vim documentation says nothing about 'softtabstop' or
+/// 'varsofttabstop' affecting the shiftwidth, and neither affects the
+/// shiftwidth in current versions of Vim, so they are not considered here.
+///
+/// @param left true if shift is to the left
+/// @param count true if new indent is to be to a tabstop
+/// @param amount number of shifts
+/// @param call_changed_bytes call changed_bytes()
+void shift_line(bool left, bool round, int amount, int call_changed_bytes)
+{
+ int64_t count;
+ int64_t sw_val = curbuf->b_p_sw;
+ int64_t ts_val = curbuf->b_p_ts;
+ int *vts_array = curbuf->b_p_vts_array;
+
+ if (sw_val != 0) {
+ // 'shiftwidth' is not zero; use it as the shift size.
+ count = get_new_sw_indent(left, round, amount, sw_val);
+ } else if ((vts_array == NULL) || (vts_array[0] == 0)) {
+ // 'shiftwidth' is zero and 'vartabstop' is empty; use 'tabstop' as the
+ // shift size.
+ count = get_new_sw_indent(left, round, amount, ts_val);
+ } else {
+ // 'shiftwidth' is zero and 'vartabstop' is defined; use 'vartabstop'
+ // to determine the new indent.
+ count = get_new_vts_indent(left, round, amount, vts_array);
+ }
+
// Set new indent
if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, count, false, call_changed_bytes);
+ change_indent(INDENT_SET, trim_to_int(count), false, call_changed_bytes);
} else {
- set_indent(count, call_changed_bytes ? SIN_CHANGED : 0);
+ set_indent(trim_to_int(count), call_changed_bytes ? SIN_CHANGED : 0);
}
}
@@ -2468,7 +2585,7 @@ void op_insert(oparg_T *oap, int count1)
if (u_save_cursor() == FAIL) {
return;
}
- curwin->w_ve_flags = VE_ALL;
+ curwin->w_ve_flags = kOptVeFlagAll;
coladvance_force(oap->op_type == OP_APPEND
? oap->end_vcol + 1 : getviscol());
if (oap->op_type == OP_APPEND) {
@@ -3231,7 +3348,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
eol = (*(cursor_pos + utfc_ptr2len(cursor_pos)) == NUL);
}
- bool ve_allows = (cur_ve_flags == VE_ALL || cur_ve_flags == VE_ONEMORE);
+ bool ve_allows = (cur_ve_flags == kOptVeFlagAll || cur_ve_flags == kOptVeFlagOnemore);
bool eof = curbuf->b_ml.ml_line_count == curwin->w_cursor.lnum
&& one_past_line;
if (ve_allows || !(eol || eof)) {
@@ -3419,7 +3536,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
goto end;
}
- if (cur_ve_flags == VE_ALL && y_type == kMTCharWise) {
+ if (cur_ve_flags == kOptVeFlagAll && y_type == kMTCharWise) {
if (gchar_cursor() == TAB) {
int viscol = getviscol();
OptInt ts = curbuf->b_p_ts;
@@ -3448,7 +3565,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
colnr_T endcol2 = 0;
if (dir == FORWARD && c != NUL) {
- if (cur_ve_flags == VE_ALL) {
+ if (cur_ve_flags == kOptVeFlagAll) {
getvcol(curwin, &curwin->w_cursor, &col, NULL, &endcol2);
} else {
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
@@ -3462,7 +3579,7 @@ void do_put(int regname, yankreg_T *reg, int dir, int count, int flags)
}
col += curwin->w_cursor.coladd;
- if (cur_ve_flags == VE_ALL
+ if (cur_ve_flags == kOptVeFlagAll
&& (curwin->w_cursor.coladd > 0 || endcol2 == curwin->w_cursor.col)) {
if (dir == FORWARD && c == NUL) {
col++;
@@ -3922,7 +4039,7 @@ error:
// Make sure the cursor is not after the NUL.
int len = get_cursor_line_len();
if (curwin->w_cursor.col > len) {
- if (cur_ve_flags == VE_ALL) {
+ if (cur_ve_flags == kOptVeFlagAll) {
curwin->w_cursor.coladd = curwin->w_cursor.col - len;
}
curwin->w_cursor.col = len;
@@ -3958,7 +4075,7 @@ void adjust_cursor_eol(void)
const bool adj_cursor = (curwin->w_cursor.col > 0
&& gchar_cursor() == NUL
- && (cur_ve_flags & VE_ONEMORE) == 0
+ && (cur_ve_flags & kOptVeFlagOnemore) == 0
&& !(restart_edit || (State & MODE_INSERT)));
if (!adj_cursor) {
return;
@@ -3967,7 +4084,7 @@ void adjust_cursor_eol(void)
// Put the cursor on the last character in the line.
dec_cursor();
- if (cur_ve_flags == VE_ALL) {
+ if (cur_ve_flags == kOptVeFlagAll) {
colnr_T scol, ecol;
// Coladd is set to the width of the last character.
@@ -4596,6 +4713,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
colnr_T endcol = MAXCOL;
colnr_T cs, ce;
char *p = ml_get(lnum);
+ int plen = ml_get_len(lnum);
bdp->startspaces = 0;
bdp->endspaces = 0;
@@ -4645,7 +4763,7 @@ void charwise_block_prep(pos_T start, pos_T end, struct block_def *bdp, linenr_T
bdp->textlen = endcol - startcol + inclusive;
}
bdp->textcol = startcol;
- bdp->textstart = p + startcol;
+ bdp->textstart = startcol <= plen ? p + startcol : p;
}
/// Handle the add/subtract operator.
@@ -6446,7 +6564,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_DELETE:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
op_delete(oap);
@@ -6462,7 +6580,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_YANK:
if (empty_region_error) {
if (!gui_yank) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
}
} else {
@@ -6476,7 +6594,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_CHANGE:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
// This is a new edit command, not a restart. Need to
@@ -6539,7 +6657,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_LOWER:
case OP_ROT13:
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
op_tilde(oap);
@@ -6581,7 +6699,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_APPEND:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
// This is a new edit command, not a restart. Need to
@@ -6616,7 +6734,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_REPLACE:
VIsual_reselect = false; // don't reselect now
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
// Restore linebreak, so that when the user edits it looks as before.
@@ -6654,7 +6772,7 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
case OP_NR_ADD:
case OP_NR_SUB:
if (empty_region_error) {
- vim_beep(BO_OPER);
+ vim_beep(kOptBoFlagOperator);
CancelRedo();
} else {
VIsual_active = true;
@@ -6714,7 +6832,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
yankreg_T *target = NULL;
bool explicit_cb_reg = (*name == '*' || *name == '+');
- bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK);
+ bool implicit_cb_reg = (*name == NUL) && (cb_flags & (kOptCbFlagUnnamed | kOptCbFlagUnnamedplus));
if (!explicit_cb_reg && !implicit_cb_reg) {
goto end;
}
@@ -6733,7 +6851,7 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
if (explicit_cb_reg) {
target = get_global_reg(*name == '*' ? STAR_REGISTER : PLUS_REGISTER);
- if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) {
+ if (writing && (cb_flags & (*name == '*' ? kOptCbFlagUnnamed : kOptCbFlagUnnamedplus))) {
clipboard_needs_update = false;
}
goto end;
@@ -6747,8 +6865,8 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)
goto end;
}
- if (cb_flags & CB_UNNAMEDPLUS) {
- *name = (cb_flags & CB_UNNAMED && writing) ? '"' : '+';
+ if (cb_flags & kOptCbFlagUnnamedplus) {
+ *name = (cb_flags & kOptCbFlagUnnamed && writing) ? '"' : '+';
target = get_global_reg(PLUS_REGISTER);
} else {
*name = '*';
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
index 35ee1e76dd..05dd0454dc 100644
--- a/src/nvim/ops.h
+++ b/src/nvim/ops.h
@@ -3,8 +3,8 @@
#include <stdbool.h>
#include <stddef.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/extmark_defs.h" // IWYU pragma: keep
#include "nvim/macros_defs.h"
diff --git a/src/nvim/option.c b/src/nvim/option.c
index bb86d10425..3d805dc319 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <uv.h>
#include "auto/config.h"
#include "klib/kvec.h"
@@ -47,6 +48,7 @@
#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/typval.h"
+#include "nvim/eval/typval_defs.h"
#include "nvim/eval/vars.h"
#include "nvim/eval/window.h"
#include "nvim/ex_cmds_defs.h"
@@ -58,6 +60,7 @@
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
@@ -74,6 +77,7 @@
#include "nvim/memfile.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
#include "nvim/move.h"
@@ -88,7 +92,6 @@
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/path.h"
-#include "nvim/plines.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
#include "nvim/regexp.h"
@@ -104,7 +107,6 @@
#include "nvim/terminal.h"
#include "nvim/types_defs.h"
#include "nvim/ui.h"
-#include "nvim/ui_defs.h"
#include "nvim/undo.h"
#include "nvim/undo_defs.h"
#include "nvim/vim_defs.h"
@@ -228,7 +230,7 @@ static void set_init_default_backupskip(void)
#endif
{
p = vim_getenv(names[i]);
- plen = 0; // will be calcuated below
+ plen = 0; // will be calculated below
}
if (p != NULL && *p != NUL) {
bool has_trailing_path_sep = false;
@@ -429,7 +431,7 @@ void set_init_1(bool clean_arg)
/// Get default value for option, based on the option's type and scope.
///
/// @param opt_idx Option index in options[] table.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// @return Default value of option for the scope specified in opt_flags.
static OptVal get_option_default(const OptIndex opt_idx, int opt_flags)
@@ -480,7 +482,7 @@ static void change_option_default(const OptIndex opt_idx, OptVal value)
/// This does not take care of side effects!
///
/// @param opt_idx Option index in options[] table.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void set_option_default(const OptIndex opt_idx, int opt_flags)
{
OptVal def_val = get_option_default(opt_idx, opt_flags);
@@ -497,7 +499,7 @@ static void set_option_default(const OptIndex opt_idx, int opt_flags)
/// Set all options (except terminal options) to their default value.
///
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void set_options_default(int opt_flags)
{
for (OptIndex opt_idx = 0; opt_idx < kOptCount; opt_idx++) {
@@ -531,8 +533,8 @@ static void set_string_default(OptIndex opt_idx, char *val, bool allocated)
/// For an option value that contains comma separated items, find "newval" in
/// "origval". Return NULL if not found.
-static char *find_dup_item(char *origval, const char *newval, const size_t newvallen,
- uint32_t flags)
+static const char *find_dup_item(const char *origval, const char *newval, const size_t newvallen,
+ uint32_t flags)
FUNC_ATTR_NONNULL_ARG(2)
{
if (origval == NULL) {
@@ -541,7 +543,7 @@ static char *find_dup_item(char *origval, const char *newval, const size_t newva
int bs = 0;
- for (char *s = origval; *s != NUL; s++) {
+ for (const char *s = origval; *s != NUL; s++) {
if ((!(flags & kOptFlagComma) || s == origval || (s[-1] == ',' && !(bs & 1)))
&& strncmp(s, newval, newvallen) == 0
&& (!(flags & kOptFlagComma) || s[newvallen] == ',' || s[newvallen] == NUL)) {
@@ -725,7 +727,7 @@ void ex_set(exarg_T *eap)
/// Copy the new string value into allocated memory for the option.
/// Can't use set_option_direct(), because we need to remove the backslashes.
-static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
+static char *stropt_copy_value(const char *origval, char **argp, set_op_T op,
uint32_t flags FUNC_ATTR_UNUSED)
{
char *arg = *argp;
@@ -772,7 +774,7 @@ static char *stropt_copy_value(char *origval, char **argp, set_op_T op,
}
/// Expand environment variables and ~ in string option value 'newval'.
-static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval, set_op_T op)
+static char *stropt_expand_envvar(OptIndex opt_idx, const char *origval, char *newval, set_op_T op)
{
char *s = option_expand(opt_idx, newval);
if (s == NULL) {
@@ -792,7 +794,7 @@ static char *stropt_expand_envvar(OptIndex opt_idx, char *origval, char *newval,
/// Concatenate the original and new values of a string option, adding a "," if
/// needed.
-static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, uint32_t flags)
+static void stropt_concat_with_comma(const char *origval, char *newval, set_op_T op, uint32_t flags)
{
int len = 0;
int comma = ((flags & kOptFlagComma) && *origval != NUL && *newval != NUL);
@@ -818,7 +820,8 @@ static void stropt_concat_with_comma(char *origval, char *newval, set_op_T op, u
/// Remove a value from a string option. Copy string option value in "origval"
/// to "newval" and then remove the string "strval" of length "len".
-static void stropt_remove_val(char *origval, char *newval, uint32_t flags, char *strval, int len)
+static void stropt_remove_val(const char *origval, char *newval, uint32_t flags, const char *strval,
+ int len)
{
// Remove newval[] from origval[]. (Note: "len" has been set above
// and is used here).
@@ -870,13 +873,13 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags)
/// set {opt}={val}
/// set {opt}:{val}
static char *stropt_get_newval(int nextchar, OptIndex opt_idx, char **argp, void *varp,
- char *origval, set_op_T *op_arg, uint32_t flags)
+ const char *origval, set_op_T *op_arg, uint32_t flags)
{
char *arg = *argp;
set_op_T op = *op_arg;
char *save_arg = NULL;
char *newval;
- char *s = NULL;
+ const char *s = NULL;
arg++; // jump to after the '=' or ':'
@@ -977,12 +980,12 @@ static int validate_opt_idx(win_T *win, OptIndex opt_idx, int opt_flags, uint32_
// Skip all options that are not window-local (used when showing
// an already loaded buffer in a window).
- if ((opt_flags & OPT_WINONLY) && (opt_idx == kOptInvalid || !option_is_window_local(opt_idx))) {
+ if ((opt_flags & OPT_WINONLY) && !option_is_window_local(opt_idx)) {
return FAIL;
}
// Skip all options that are window-local (used for :vimgrep).
- if ((opt_flags & OPT_NOWIN) && opt_idx != kOptInvalid && option_is_window_local(opt_idx)) {
+ if ((opt_flags & OPT_NOWIN) && option_is_window_local(opt_idx)) {
return FAIL;
}
@@ -1192,9 +1195,10 @@ static OptVal get_option_newval(OptIndex opt_idx, int opt_flags, set_prefix_T pr
break;
}
case kOptValTypeString: {
- char *oldval_str = oldval.data.string.data;
+ const char *oldval_str = oldval.data.string.data;
// Get the new value for the option
- char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op, flags);
+ const char *newval_str = stropt_get_newval(nextchar, opt_idx, argp, varp, oldval_str, &op,
+ flags);
newval = CSTR_AS_OPTVAL(newval_str);
break;
}
@@ -1276,6 +1280,7 @@ static void do_one_set_option(int opt_flags, char **argp, bool *did_show, char *
gotocmdline(true); // cursor at status line
*did_show = true; // remember that we did a line
}
+ msg_ext_set_kind("list_cmd");
showoneopt(&options[opt_idx], opt_flags);
if (p_verbose > 0) {
@@ -1474,7 +1479,7 @@ void did_set_title(void)
/// set_options_bin - called when 'bin' changes value.
///
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_options_bin(int oldval, int newval, int opt_flags)
{
// The option values that are changed when 'bin' changes are
@@ -1565,7 +1570,7 @@ char *find_shada_parameter(int type)
/// These string options cannot be indirect!
/// If "val" is NULL expand the current value of the option.
/// Return pointer to NameBuff, or NULL when not expanded.
-static char *option_expand(OptIndex opt_idx, char *val)
+static char *option_expand(OptIndex opt_idx, const char *val)
{
// if option doesn't need expansion nothing to do
if (!(options[opt_idx].flags & kOptFlagExpand) || is_option_hidden(opt_idx)) {
@@ -1651,7 +1656,7 @@ void check_options(void)
///
/// @param wp Window.
/// @param opt_idx Option index in options[] table.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// @return True if option was set from a modeline or in secure mode, false if it wasn't.
int was_set_insecurely(win_T *const wp, OptIndex opt_idx, int opt_flags)
@@ -1735,8 +1740,14 @@ bool parse_winhl_opt(const char *winhl, win_T *wp)
p = wp->w_p_winhl;
}
+ if (wp != NULL && wp->w_ns_hl_winhl < 0) {
+ // 'winhighlight' shouldn't be used for this window.
+ // Only check that the value is valid.
+ wp = NULL;
+ }
+
if (!*p) {
- if (wp != NULL && wp->w_ns_hl_winhl && wp->w_ns_hl == wp->w_ns_hl_winhl) {
+ if (wp != NULL && wp->w_ns_hl_winhl > 0 && wp->w_ns_hl == wp->w_ns_hl_winhl) {
wp->w_ns_hl = 0;
wp->w_hl_needs_update = true;
}
@@ -1970,8 +1981,8 @@ static const char *did_set_cmdheight(optset_T *args)
{
OptInt old_value = args->os_oldval.number;
- if (p_ch > Rows - min_rows() + 1) {
- p_ch = Rows - min_rows() + 1;
+ if (p_ch > Rows - min_rows(curtab) + 1) {
+ p_ch = Rows - min_rows(curtab) + 1;
}
// if p_ch changed value, change the command line height
@@ -2109,14 +2120,14 @@ static const char *did_set_laststatus(optset_T *args)
// When switching to global statusline, decrease topframe height
// Also clear the cmdline to remove the ruler if there is one
if (value == 3 && old_value != 3) {
- frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false);
+ frame_new_height(topframe, topframe->fr_height - STATUS_HEIGHT, false, false, false);
win_comp_pos();
clear_cmdline = true;
}
// When switching from global statusline, increase height of topframe by STATUS_HEIGHT
// in order to to re-add the space that was previously taken by the global statusline
if (old_value == 3 && value != 3) {
- frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false);
+ frame_new_height(topframe, topframe->fr_height + STATUS_HEIGHT, false, false, false);
win_comp_pos();
}
@@ -2750,10 +2761,11 @@ static const char *check_num_option_bounds(OptIndex opt_idx, OptInt *newval, cha
switch (opt_idx) {
case kOptLines:
- if (*newval < min_rows() && full_screen) {
- vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"), min_rows());
+ if (*newval < min_rows_for_all_tabpages() && full_screen) {
+ vim_snprintf(errbuf, errbuflen, _("E593: Need at least %d lines"),
+ min_rows_for_all_tabpages());
errmsg = errbuf;
- *newval = min_rows();
+ *newval = min_rows_for_all_tabpages();
}
// True max size is defined by check_screensize().
*newval = MIN(*newval, INT_MAX);
@@ -2860,8 +2872,6 @@ static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *e
case kOptCmdheight:
if (value < 0) {
return e_positive;
- } else {
- p_ch_was_zero = value == 0;
}
break;
case kOptHistory:
@@ -2871,13 +2881,6 @@ static const char *validate_num_option(OptIndex opt_idx, OptInt *newval, char *e
return e_invarg;
}
break;
- case kOptMsghistory:
- if (value < 0) {
- return e_positive;
- } else if (value > 10000) {
- return e_invarg;
- }
- break;
case kOptPyxversion:
if (value == 0) {
*newval = 3;
@@ -3118,17 +3121,10 @@ bool optval_equal(OptVal o1, OptVal o2)
UNREACHABLE;
}
-/// Get type of option. Does not support multitype options.
+/// Get type of option.
static OptValType option_get_type(const OptIndex opt_idx)
{
- assert(!option_is_multitype(opt_idx));
-
- // If the option only supports a single type, it means that the index of the option's type flag
- // corresponds to the value of the type enum. So get the index of the type flag using xctz() and
- // use that as the option's type.
- OptValType type = xctz(options[opt_idx].type_flags);
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
- return type;
+ return options[opt_idx].type;
}
/// Create OptVal from var pointer.
@@ -3146,11 +3142,6 @@ OptVal optval_from_varp(OptIndex opt_idx, void *varp)
return BOOLEAN_OPTVAL(curbufIsChanged());
}
- if (option_is_multitype(opt_idx)) {
- // Multitype options are stored as OptVal.
- return *(OptVal *)varp;
- }
-
OptValType type = option_get_type(opt_idx);
switch (type) {
@@ -3261,33 +3252,6 @@ OptVal object_as_optval(Object o, bool *error)
UNREACHABLE;
}
-/// Get an allocated string containing a list of valid types for an option.
-/// For options with a singular type, it returns the name of the type. For options with multiple
-/// possible types, it returns a slash separated list of types. For example, if an option can be a
-/// number, boolean or string, the function returns "number/boolean/string"
-static char *option_get_valid_types(OptIndex opt_idx)
-{
- StringBuilder str = KV_INITIAL_VALUE;
- kv_resize(str, 32);
-
- // Iterate through every valid option value type and check if the option supports that type
- for (OptValType type = 0; type < kOptValTypeSize; type++) {
- if (option_has_type(opt_idx, type)) {
- const char *typename = optval_type_get_name(type);
-
- if (str.size == 0) {
- kv_concat(str, typename);
- } else {
- kv_printf(str, "/%s", typename);
- }
- }
- }
-
- // Ensure that the string is NUL-terminated.
- kv_push(str, NUL);
- return str.items;
-}
-
/// Check if option is hidden.
///
/// @param opt_idx Option index in options[] table.
@@ -3300,25 +3264,10 @@ bool is_option_hidden(OptIndex opt_idx)
&& options[opt_idx].var == &options[opt_idx].def_val.data;
}
-/// Check if option is multitype (supports multiple types).
-static bool option_is_multitype(OptIndex opt_idx)
-{
- const OptTypeFlags type_flags = get_option(opt_idx)->type_flags;
- assert(type_flags != 0);
- return !is_power_of_two(type_flags);
-}
-
/// Check if option supports a specific type.
bool option_has_type(OptIndex opt_idx, OptValType type)
{
- // Ensure that type flags variable can hold all types.
- STATIC_ASSERT(kOptValTypeSize <= sizeof(OptTypeFlags) * 8,
- "Option type_flags cannot fit all option types");
- // Ensure that the type is valid before accessing type_flags.
- assert(type > kOptValTypeNil && type < kOptValTypeSize);
- // Bitshift 1 by the value of type to get the type's corresponding flag, and check if it's set in
- // the type_flags bit field.
- return get_option(opt_idx)->type_flags & (1 << type);
+ return opt_idx != kOptInvalid && options[opt_idx].type == type;
}
/// Check if option supports a specific scope.
@@ -3377,18 +3326,18 @@ uint32_t get_option_flags(OptIndex opt_idx)
/// Gets the value for an option.
///
-/// @param opt_idx Option index in options[] table.
-/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
+/// @param opt_idx Option index in options[] table.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// @return [allocated] Option value. Returns NIL_OPTVAL for invalid option index.
-OptVal get_option_value(OptIndex opt_idx, int scope)
+OptVal get_option_value(OptIndex opt_idx, int opt_flags)
{
if (opt_idx == kOptInvalid) { // option not in the options[] table.
return NIL_OPTVAL;
}
vimoption_T *opt = &options[opt_idx];
- void *varp = get_varp_scope(opt, scope);
+ void *varp = get_varp_scope(opt, opt_flags);
return optval_copy(optval_from_varp(opt_idx, varp));
}
@@ -3461,7 +3410,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx)
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
/// @param[in] varp Option variable pointer, cannot be NULL.
/// @param old_value Old option value.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
@@ -3473,8 +3422,7 @@ static bool is_option_local_value_unset(OptIndex opt_idx)
/// @return NULL on success, an untranslated error message on error.
static const char *did_set_option(OptIndex opt_idx, void *varp, OptVal old_value, OptVal new_value,
int opt_flags, scid_T set_sid, const bool direct,
- const bool value_replaced, char *errbuf, // NOLINT(readability-non-const-parameter)
- size_t errbuflen)
+ const bool value_replaced, char *errbuf, size_t errbuflen)
{
vimoption_T *opt = &options[opt_idx];
const char *errmsg = NULL;
@@ -3656,11 +3604,10 @@ static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval,
}
} else if (!option_has_type(opt_idx, newval->type)) {
char *rep = optval_to_cstr(*newval);
- char *valid_types = option_get_valid_types(opt_idx);
+ const char *type_str = optval_type_get_name(opt->type);
snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"),
- opt->fullname, valid_types, optval_type_get_name(newval->type), rep);
+ opt->fullname, type_str, optval_type_get_name(newval->type), rep);
xfree(rep);
- xfree(valid_types);
errmsg = errbuf;
} else if (newval->type == kOptValTypeNumber) {
// Validate and bound check num option values.
@@ -3674,7 +3621,7 @@ static const char *validate_option_value(const OptIndex opt_idx, OptVal *newval,
///
/// @param opt_idx Index in options[] table. Must not be kOptInvalid.
/// @param value New option value. Might get freed.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
@@ -3778,7 +3725,7 @@ static const char *set_option(const OptIndex opt_idx, OptVal value, int opt_flag
///
/// @param opt_idx Option index in options[] table.
/// @param value Option value.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
@@ -3800,14 +3747,14 @@ void set_option_direct(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set
///
/// @param opt_idx Option index in options[] table.
/// @param value Option value.
-/// @param opt_flags Option flags.
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param set_sid Script ID. Special values:
/// 0: Use current script ID.
/// SID_NONE: Don't set script ID.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T set_sid,
- OptScope req_scope, void *const from)
+ OptScope scope, void *const from)
{
buf_T *save_curbuf = curbuf;
win_T *save_curwin = curwin;
@@ -3815,7 +3762,7 @@ void set_option_direct_for(OptIndex opt_idx, OptVal value, int opt_flags, scid_T
// Don't use switch_option_context(), as that calls aucmd_prepbuf(), which may have unintended
// side-effects when setting an option directly. Just change the values of curbuf and curwin if
// needed, no need to properly switch the window / buffer.
- switch (req_scope) {
+ switch (scope) {
case kOptScopeGlobal:
break;
case kOptScopeWin:
@@ -3898,7 +3845,7 @@ const char *set_option_value_handle_tty(const char *name, OptIndex opt_idx, cons
///
/// @param opt_idx Option index in options[] table.
/// @param value Option value. If NIL_OPTVAL, the option value is cleared.
-/// @param opt_flags OPT_LOCAL or 0 (both)
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_flags)
{
const char *errmsg = set_option_value(opt_idx, value, opt_flags);
@@ -3911,14 +3858,14 @@ void set_option_value_give_err(const OptIndex opt_idx, OptVal value, int opt_fla
/// Switch current context to get/set option value for window/buffer.
///
/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
///
/// @return true if context was switched, false otherwise.
-static bool switch_option_context(void *const ctx, OptScope req_scope, void *const from, Error *err)
+static bool switch_option_context(void *const ctx, OptScope scope, void *const from, Error *err)
{
- switch (req_scope) {
+ switch (scope) {
case kOptScopeGlobal:
return false;
case kOptScopeWin: {
@@ -3933,7 +3880,7 @@ static bool switch_option_context(void *const ctx, OptScope req_scope, void *con
== FAIL) {
restore_win_noblock(switchwin, true);
- if (try_end(err)) {
+ if (ERROR_SET(err)) {
return false;
}
api_set_error(err, kErrorTypeException, "Problem while switching windows");
@@ -3957,9 +3904,9 @@ static bool switch_option_context(void *const ctx, OptScope req_scope, void *con
/// Restore context after getting/setting option for window/buffer. See switch_option_context() for
/// params.
-static void restore_option_context(void *const ctx, OptScope req_scope)
+static void restore_option_context(void *const ctx, OptScope scope)
{
- switch (req_scope) {
+ switch (scope) {
case kOptScopeGlobal:
break;
case kOptScopeWin:
@@ -3977,28 +3924,28 @@ static void restore_option_context(void *const ctx, OptScope req_scope)
/// @param[out] flagsp Set to the option flags (see OptFlags) (if not NULL).
/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination).
/// @param[out] hidden Whether option is hidden.
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
///
/// @return Option value. Must be freed by caller.
-OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scope, void *const from,
+OptVal get_option_value_for(OptIndex opt_idx, int opt_flags, const OptScope scope, void *const from,
Error *err)
{
switchwin_T switchwin;
aco_save_T aco;
- void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin
- : (req_scope == kOptScopeBuf ? (void *)&aco : NULL);
+ void *ctx = scope == kOptScopeWin ? (void *)&switchwin
+ : (scope == kOptScopeBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
+ bool switched = switch_option_context(ctx, scope, from, err);
if (ERROR_SET(err)) {
return NIL_OPTVAL;
}
- OptVal retv = get_option_value(opt_idx, scope);
+ OptVal retv = get_option_value(opt_idx, opt_flags);
if (switched) {
- restore_option_context(ctx, req_scope);
+ restore_option_context(ctx, scope);
}
return retv;
@@ -4010,19 +3957,19 @@ OptVal get_option_value_for(OptIndex opt_idx, int scope, const OptScope req_scop
/// @param opt_idx Option index in options[] table.
/// @param[in] value Option value.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// @param req_scope Requested option scope. See OptScope in option.h.
+/// @param scope Option scope. See OptScope in option.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, const int opt_flags,
- const OptScope req_scope, void *const from, Error *err)
+ const OptScope scope, void *const from, Error *err)
FUNC_ATTR_NONNULL_ARG(1)
{
switchwin_T switchwin;
aco_save_T aco;
- void *ctx = req_scope == kOptScopeWin ? (void *)&switchwin
- : (req_scope == kOptScopeBuf ? (void *)&aco : NULL);
+ void *ctx = scope == kOptScopeWin ? (void *)&switchwin
+ : (scope == kOptScopeBuf ? (void *)&aco : NULL);
- bool switched = switch_option_context(ctx, req_scope, from, err);
+ bool switched = switch_option_context(ctx, scope, from, err);
if (ERROR_SET(err)) {
return;
}
@@ -4033,14 +3980,14 @@ void set_option_value_for(const char *name, OptIndex opt_idx, OptVal value, cons
}
if (switched) {
- restore_option_context(ctx, req_scope);
+ restore_option_context(ctx, scope);
}
}
/// if 'all' == false: show changed options
/// if 'all' == true: show all normal options
///
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void showoptions(bool all, int opt_flags)
{
#define INC 20
@@ -4048,6 +3995,7 @@ static void showoptions(bool all, int opt_flags)
vimoption_T **items = xmalloc(sizeof(vimoption_T *) * OPTION_COUNT);
+ msg_ext_set_kind("list_cmd");
// Highlight title
if (opt_flags & OPT_GLOBAL) {
msg_puts_title(_("\n--- Global option values ---"));
@@ -4166,7 +4114,7 @@ void ui_refresh_options(void)
/// showoneopt: show the value of one option
/// must not be called with a hidden option!
///
-/// @param opt_flags OPT_LOCAL or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
static void showoneopt(vimoption_T *opt, int opt_flags)
{
int save_silent = silent_mode;
@@ -4389,7 +4337,7 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
return FAIL;
}
- char *value_str = value.data.string.data;
+ const char *value_str = value.data.string.data;
char *buf = NULL;
char *part = NULL;
@@ -4451,18 +4399,18 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
return OK;
}
-void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
+void *get_varp_scope_from(vimoption_T *p, int opt_flags, buf_T *buf, win_T *win)
{
OptIndex opt_idx = get_opt_idx(p);
- if ((scope & OPT_GLOBAL) && !option_is_global_only(opt_idx)) {
+ if ((opt_flags & OPT_GLOBAL) && !option_is_global_only(opt_idx)) {
if (option_is_window_local(opt_idx)) {
return GLOBAL_WO(get_varp_from(p, buf, win));
}
return p->var;
}
- if ((scope & OPT_LOCAL) && option_is_global_local(opt_idx)) {
+ if ((opt_flags & OPT_LOCAL) && option_is_global_local(opt_idx)) {
switch (opt_idx) {
case kOptFormatprg:
return &(buf->b_p_fp);
@@ -4533,17 +4481,17 @@ void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win)
/// Get pointer to option variable, depending on local or global scope.
///
-/// @param scope can be OPT_LOCAL, OPT_GLOBAL or a combination.
-void *get_varp_scope(vimoption_T *p, int scope)
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
+void *get_varp_scope(vimoption_T *p, int opt_flags)
{
- return get_varp_scope_from(p, scope, curbuf, curwin);
+ return get_varp_scope_from(p, opt_flags, curbuf, curwin);
}
/// Get pointer to option variable at 'opt_idx', depending on local or global
/// scope.
-void *get_option_varp_scope_from(OptIndex opt_idx, int scope, buf_T *buf, win_T *win)
+void *get_option_varp_scope_from(OptIndex opt_idx, int opt_flags, buf_T *buf, win_T *win)
{
- return get_varp_scope_from(&(options[opt_idx]), scope, buf, win);
+ return get_varp_scope_from(&(options[opt_idx]), opt_flags, buf, win);
}
void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
@@ -5282,7 +5230,7 @@ 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;
- if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) {
+ if (p_vts && *p_vts != NUL && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
@@ -5295,7 +5243,7 @@ void buf_copy_options(buf_T *buf, int flags)
COPY_OPT_SCTX(buf, kBufOptTabstop);
buf->b_p_vts = xstrdup(p_vts);
COPY_OPT_SCTX(buf, kBufOptVartabstop);
- if (p_vts && p_vts != empty_string_option && !buf->b_p_vts_array) {
+ if (p_vts && *p_vts != NUL && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
@@ -5348,7 +5296,7 @@ static char expand_option_name[5] = { 't', '_', NUL, NUL, NUL };
static int expand_option_flags = 0;
static bool expand_option_append = false;
-/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags)
{
expand_option_flags = opt_flags;
@@ -5771,6 +5719,7 @@ int ExpandStringSetting(expand_T *xp, regmatch_T *regmatch, int *numMatches, cha
optexpand_T args = {
.oe_varp = get_varp_scope(&options[expand_option_idx], expand_option_flags),
+ .oe_idx = expand_option_idx,
.oe_append = expand_option_append,
.oe_regmatch = regmatch,
.oe_xp = xp,
@@ -5900,12 +5849,12 @@ int ExpandSettingSubtract(expand_T *xp, regmatch_T *regmatch, int *numMatches, c
/// Get the value for the numeric or string option///opp in a nice format into
/// NameBuff[]. Must not be called with a hidden option!
///
-/// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
///
/// TODO(famiu): Replace this with optval_to_cstr() if possible.
-static void option_value2string(vimoption_T *opt, int scope)
+static void option_value2string(vimoption_T *opt, int opt_flags)
{
- void *varp = get_varp_scope(opt, scope);
+ void *varp = get_varp_scope(opt, opt_flags);
assert(varp != NULL);
if (option_has_type(get_opt_idx(opt), kOptValTypeNumber)) {
@@ -6009,19 +5958,19 @@ int fill_culopt_flags(char *val, win_T *wp)
p = val;
}
while (*p != NUL) {
- // Note: Keep this in sync with p_culopt_values.
+ // Note: Keep this in sync with opt_culopt_values.
if (strncmp(p, "line", 4) == 0) {
p += 4;
- culopt_flags_new |= CULOPT_LINE;
+ culopt_flags_new |= kOptCuloptFlagLine;
} else if (strncmp(p, "both", 4) == 0) {
p += 4;
- culopt_flags_new |= CULOPT_LINE | CULOPT_NBR;
+ culopt_flags_new |= kOptCuloptFlagLine | kOptCuloptFlagNumber;
} else if (strncmp(p, "number", 6) == 0) {
p += 6;
- culopt_flags_new |= CULOPT_NBR;
+ culopt_flags_new |= kOptCuloptFlagNumber;
} else if (strncmp(p, "screenline", 10) == 0) {
p += 10;
- culopt_flags_new |= CULOPT_SCRLINE;
+ culopt_flags_new |= kOptCuloptFlagScreenline;
}
if (*p != ',' && *p != NUL) {
@@ -6033,7 +5982,7 @@ int fill_culopt_flags(char *val, win_T *wp)
}
// Can't have both "line" and "screenline".
- if ((culopt_flags_new & CULOPT_LINE) && (culopt_flags_new & CULOPT_SCRLINE)) {
+ if ((culopt_flags_new & kOptCuloptFlagLine) && (culopt_flags_new & kOptCuloptFlagScreenline)) {
return FAIL;
}
wp->w_p_culopt_flags = culopt_flags_new;
@@ -6143,7 +6092,8 @@ char *get_flp_value(buf_T *buf)
/// Get the local or global value of 'virtualedit' flags.
unsigned get_ve_flags(win_T *wp)
{
- return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags) & ~(VE_NONE | VE_NONEU);
+ return (wp->w_ve_flags ? wp->w_ve_flags : ve_flags)
+ & ~(unsigned)(kOptVeFlagNone | kOptVeFlagNoneU);
}
/// Get the local or global value of 'showbreak'.
@@ -6221,7 +6171,7 @@ int default_fileformat(void)
/// Sets 'fileformat'.
///
/// @param eol_style End-of-line style.
-/// @param opt_flags OPT_LOCAL and/or OPT_GLOBAL
+/// @param opt_flags Option flags (can be OPT_LOCAL, OPT_GLOBAL or a combination).
void set_fileformat(int eol_style, int opt_flags)
{
char *p = NULL;
@@ -6352,14 +6302,14 @@ int get_sidescrolloff_value(win_T *wp)
return (int)(wp->w_p_siso < 0 ? p_siso : wp->w_p_siso);
}
-Dict get_vimoption(String name, int scope, buf_T *buf, win_T *win, Arena *arena, Error *err)
+Dict get_vimoption(String name, int opt_flags, buf_T *buf, win_T *win, Arena *arena, Error *err)
{
OptIndex opt_idx = find_option_len(name.data, name.size);
VALIDATE_S(opt_idx != kOptInvalid, "option (not found)", name.data, {
return (Dict)ARRAY_DICT_INIT;
});
- return vimoption2dict(&options[opt_idx], scope, buf, win, arena);
+ return vimoption2dict(&options[opt_idx], opt_flags, buf, win, arena);
}
Dict get_all_vimoptions(Arena *arena)
@@ -6372,7 +6322,7 @@ Dict get_all_vimoptions(Arena *arena)
return retval;
}
-static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *win, Arena *arena)
+static Dict vimoption2dict(vimoption_T *opt, int opt_flags, buf_T *buf, win_T *win, Arena *arena)
{
OptIndex opt_idx = get_opt_idx(opt);
Dict dict = arena_dict(arena, 13);
@@ -6399,7 +6349,7 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
PUT_C(dict, "was_set", BOOLEAN_OBJ(opt->flags & kOptFlagWasSet));
LastSet last_set = { .channel_id = 0 };
- if (req_scope == OPT_GLOBAL) {
+ if (opt_flags == OPT_GLOBAL) {
last_set = opt->last_set;
} else {
// Scope is either OPT_LOCAL or a fallback mode was requested.
@@ -6409,7 +6359,7 @@ static Dict vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, win_T *w
if (option_has_scope(opt_idx, kOptScopeWin)) {
last_set = win->w_p_script_ctx[opt->scope_idx[kOptScopeWin]];
}
- if (req_scope != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) {
+ if (opt_flags != OPT_LOCAL && last_set.script_ctx.sc_sid == 0) {
last_set = opt->last_set;
}
}
diff --git a/src/nvim/option.h b/src/nvim/option.h
index cba5b00d95..2f71990150 100644
--- a/src/nvim/option.h
+++ b/src/nvim/option.h
@@ -1,13 +1,11 @@
#pragma once
-#include <stdbool.h>
-#include <stdint.h>
#include <stdio.h> // IWYU pragma: keep
#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/api/private/helpers.h"
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/macros_defs.h"
#include "nvim/option_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 832e03148a..7dd4bd2bc7 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -54,9 +54,6 @@ typedef enum {
kOptValTypeNumber,
kOptValTypeString,
} OptValType;
-/// Always update this whenever a new option type is added.
-#define kOptValTypeSize (kOptValTypeString + 1)
-typedef uint32_t OptTypeFlags;
/// Scopes that an option can support.
typedef enum {
@@ -99,7 +96,6 @@ typedef struct {
int os_flags;
/// Old value of the option.
- /// TODO(famiu): Convert `os_oldval` and `os_newval` to `OptVal` to accommodate multitype options.
OptValData os_oldval;
/// New value of the option.
OptValData os_newval;
@@ -138,6 +134,7 @@ typedef const char *(*opt_did_set_cb_T)(optset_T *args);
typedef struct {
/// Pointer to the option variable. It's always a string.
char *oe_varp;
+ OptIndex oe_idx;
/// The original option value, escaped.
char *oe_opt_value;
@@ -173,14 +170,18 @@ typedef struct {
char *fullname; ///< full option name
char *shortname; ///< permissible abbreviation
uint32_t flags; ///< see above
- OptTypeFlags type_flags; ///< option type flags, see OptValType
+ OptValType type; ///< option type
OptScopeFlags scope_flags; ///< option scope flags, see OptScope
void *var; ///< global option: pointer to variable;
///< window-local option: NULL;
///< buffer-local option: global value
+ unsigned *flags_var;
ssize_t scope_idx[kOptScopeSize]; ///< index of option at every scope.
bool immutable; ///< option is immutable, trying to set it will give an error.
+ const char **values; ///< possible values for string options
+ const size_t values_len; ///< length of values array
+
/// callback function to invoke after an option is modified to validate and
/// apply the new value.
opt_did_set_cb_T opt_did_set_cb;
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index 9ed24c4c9c..d31e76aff8 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -3,12 +3,17 @@
#include "nvim/macros_defs.h"
#include "nvim/os/os_defs.h"
#include "nvim/sign_defs.h"
+#include "nvim/statusline_defs.h"
#include "nvim/types_defs.h"
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "option_vars.generated.h" // NOLINT(build/include_defs)
+#endif
+
// option_vars.h: definition of global variables for settable options
#define HIGHLIGHT_INIT \
- "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText,d:Directory,e:ErrorMsg," \
+ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,@: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," \
@@ -16,7 +21,7 @@
"R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind," \
"]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel," \
"_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm," \
- "Z:StatusLineTermNC,g:MsgArea,0:Whitespace,I:NormalNC"
+ "Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,0:Whitespace,I:NormalNC"
// Default values for 'errorformat'.
// The "%f|%l| %m" one is used for when the contents of the quickfix window is
@@ -208,49 +213,6 @@ enum {
#define COM_ALL "nbsmexflrO" // all flags for 'comments' option
#define COM_MAX_LEN 50 // maximum length of a part
-/// 'statusline' option flags
-enum {
- STL_FILEPATH = 'f', ///< Path of file in buffer.
- STL_FULLPATH = 'F', ///< Full path of file in buffer.
- STL_FILENAME = 't', ///< Last part (tail) of file path.
- STL_COLUMN = 'c', ///< Column og cursor.
- STL_VIRTCOL = 'v', ///< Virtual column.
- STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display.
- STL_LINE = 'l', ///< Line number of cursor.
- STL_NUMLINES = 'L', ///< Number of lines in buffer.
- STL_BUFNO = 'n', ///< Current buffer number.
- STL_KEYMAP = 'k', ///< 'keymap' when active.
- STL_OFFSET = 'o', ///< Offset of character under cursor.
- STL_OFFSET_X = 'O', ///< - in hexadecimal.
- STL_BYTEVAL = 'b', ///< Byte value of character.
- STL_BYTEVAL_X = 'B', ///< - in hexadecimal.
- STL_ROFLAG = 'r', ///< Readonly flag.
- STL_ROFLAG_ALT = 'R', ///< - other display.
- STL_HELPFLAG = 'h', ///< Window is showing a help file.
- STL_HELPFLAG_ALT = 'H', ///< - other display.
- STL_FILETYPE = 'y', ///< 'filetype'.
- STL_FILETYPE_ALT = 'Y', ///< - other display.
- STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf.
- STL_PREVIEWFLAG_ALT = 'W', ///< - other display.
- STL_MODIFIED = 'm', ///< Modified flag.
- STL_MODIFIED_ALT = 'M', ///< - other display.
- STL_QUICKFIX = 'q', ///< Quickfix window description.
- STL_PERCENTAGE = 'p', ///< Percentage through file.
- STL_ALTPERCENT = 'P', ///< Percentage as TOP BOT ALL or NN%.
- STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y).
- STL_PAGENUM = 'N', ///< Page number (when printing).
- STL_SHOWCMD = 'S', ///< 'showcmd' buffer
- STL_FOLDCOL = 'C', ///< Fold column for 'statuscolumn'
- STL_SIGNCOL = 's', ///< Sign column for 'statuscolumn'
- STL_VIM_EXPR = '{', ///< Start of expression to substitute.
- STL_SEPARATE = '=', ///< Separation between alignment sections.
- STL_TRUNCMARK = '<', ///< Truncation mark if line is too long.
- STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0.
- STL_HIGHLIGHT = '#', ///< Highlight name.
- STL_TABPAGENR = 'T', ///< Tab page label nr.
- STL_TABCLOSENR = 'X', ///< Tab page close nr.
- STL_CLICK_FUNC = '@', ///< Click region start.
-};
/// C string containing all 'statusline' option flags
#define STL_ALL ((char[]) { \
STL_FILEPATH, STL_FULLPATH, STL_FILENAME, STL_COLUMN, STL_VIRTCOL, \
@@ -264,12 +226,6 @@ enum {
STL_CLICK_FUNC, STL_TABPAGENR, STL_TABCLOSENR, STL_CLICK_FUNC, \
0, })
-// flags used for parsed 'wildmode'
-#define WIM_FULL 0x01
-#define WIM_LONGEST 0x02
-#define WIM_LIST 0x04
-#define WIM_BUFLASTUSED 0x08
-
// arguments for can_bs()
// each defined char should be unique over all values
// except for BS_START, that intentionally also matches BS_NOSTOP
@@ -280,11 +236,6 @@ enum {
#define BS_START 's' // "Start"
#define BS_NOSTOP 'p' // "nostoP
-// flags for the 'culopt' option
-#define CULOPT_LINE 0x01 // Highlight complete line
-#define CULOPT_SCRLINE 0x02 // Highlight screen line
-#define CULOPT_NBR 0x04 // Highlight Number column
-
#define LISPWORD_VALUE \
"defun,define,defmacro,set!,lambda,if,case,let,flet,let*,letrec,do,do*,define-syntax,let-syntax,letrec-syntax,destructuring-bind,defpackage,defparameter,defstruct,deftype,defvar,do-all-symbols,do-external-symbols,do-symbols,dolist,dotimes,ecase,etypecase,eval-when,labels,macrolet,multiple-value-bind,multiple-value-call,multiple-value-prog1,multiple-value-setq,prog1,progv,typecase,unless,unwind-protect,when,with-input-from-string,with-open-file,with-open-stream,with-output-to-string,with-package-iterator,define-condition,handler-bind,handler-case,restart-bind,restart-case,with-simple-restart,store-value,use-value,muffle-warning,abort,continue,with-slots,with-slots*,with-accessors,with-accessors*,defclass,defmethod,print-unreadable-object"
@@ -319,47 +270,17 @@ EXTERN char *p_bg; ///< 'background'
EXTERN int p_bk; ///< 'backup'
EXTERN char *p_bkc; ///< 'backupcopy'
EXTERN unsigned bkc_flags; ///< flags from 'backupcopy'
-#define BKC_YES 0x001
-#define BKC_AUTO 0x002
-#define BKC_NO 0x004
-#define BKC_BREAKSYMLINK 0x008
-#define BKC_BREAKHARDLINK 0x010
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;
-
-// values for the 'belloff' option
-#define BO_ALL 0x0001
-#define BO_BS 0x0002
-#define BO_CRSR 0x0004
-#define BO_COMPL 0x0008
-#define BO_COPY 0x0010
-#define BO_CTRLG 0x0020
-#define BO_ERROR 0x0040
-#define BO_ESC 0x0080
-#define BO_EX 0x0100
-#define BO_HANGUL 0x0200
-#define BO_IM 0x0400
-#define BO_LANG 0x0800
-#define BO_MESS 0x1000
-#define BO_MATCH 0x2000
-#define BO_OPER 0x4000
-#define BO_REG 0x8000
-#define BO_SH 0x10000
-#define BO_SPELL 0x20000
-#define BO_TERM 0x40000
-#define BO_WILD 0x80000
-
EXTERN char *p_bsk; ///< 'backupskip'
EXTERN char *p_breakat; ///< 'breakat'
EXTERN char *p_bh; ///< 'bufhidden'
EXTERN char *p_bt; ///< 'buftype'
EXTERN char *p_cmp; ///< 'casemap'
EXTERN unsigned cmp_flags;
-#define CMP_INTERNAL 0x001
-#define CMP_KEEPASCII 0x002
EXTERN char *p_enc; ///< 'encoding'
EXTERN int p_deco; ///< 'delcombine'
EXTERN char *p_ccv; ///< 'charconvert'
@@ -367,9 +288,6 @@ EXTERN char *p_cino; ///< 'cinoptions'
EXTERN char *p_cedit; ///< 'cedit'
EXTERN char *p_cb; ///< 'clipboard'
EXTERN unsigned cb_flags;
-#define CB_UNNAMED 0x001
-#define CB_UNNAMEDPLUS 0x002
-#define CB_UNNAMEDMASK (CB_UNNAMED | CB_UNNAMEDPLUS)
EXTERN OptInt p_cwh; ///< 'cmdwinheight'
EXTERN OptInt p_ch; ///< 'cmdheight'
EXTERN char *p_cms; ///< 'commentstring'
@@ -380,18 +298,6 @@ EXTERN char *p_cia; ///< 'completeitemalign'
EXTERN unsigned cia_flags; ///< order flags of 'completeitemalign'
EXTERN char *p_cot; ///< 'completeopt'
EXTERN unsigned cot_flags; ///< flags from 'completeopt'
-// Keep in sync with p_cot_values in optionstr.c
-#define COT_MENU 0x001
-#define COT_MENUONE 0x002
-#define COT_ANY_MENU 0x003 // combination of menu flags
-#define COT_LONGEST 0x004 // false: insert full match,
- // true: insert longest prefix
-#define COT_PREVIEW 0x008
-#define COT_POPUP 0x010
-#define COT_ANY_PREVIEW 0x018 // combination of preview flags
-#define COT_NOINSERT 0x020 // false: select & insert, true: noinsert
-#define COT_NOSELECT 0x040 // false: select & insert, true: noselect
-#define COT_FUZZY 0x080 // true: fuzzy match enabled
#ifdef BACKSLASH_IN_FILENAME
EXTERN char *p_csl; ///< 'completeslash'
#endif
@@ -410,11 +316,6 @@ EXTERN int p_dg; ///< 'digraph'
EXTERN char *p_dir; ///< 'directory'
EXTERN char *p_dy; ///< 'display'
EXTERN unsigned dy_flags;
-#define DY_LASTLINE 0x001
-#define DY_TRUNCATE 0x002
-#define DY_UHEX 0x004
-// legacy flag, not used
-#define DY_MSGSEP 0x008
EXTERN char *p_ead; ///< 'eadirection'
EXTERN int p_emoji; ///< 'emoji'
EXTERN int p_ea; ///< 'equalalways'
@@ -442,17 +343,6 @@ EXTERN char *p_fcl; ///< 'foldclose'
EXTERN OptInt p_fdls; ///< 'foldlevelstart'
EXTERN char *p_fdo; ///< 'foldopen'
EXTERN unsigned fdo_flags;
-#define FDO_ALL 0x001
-#define FDO_BLOCK 0x002
-#define FDO_HOR 0x004
-#define FDO_MARK 0x008
-#define FDO_PERCENT 0x010
-#define FDO_QUICKFIX 0x020
-#define FDO_SEARCH 0x040
-#define FDO_TAG 0x080
-#define FDO_INSERT 0x100
-#define FDO_UNDO 0x200
-#define FDO_JUMP 0x400
EXTERN char *p_fex; ///< 'formatexpr'
EXTERN char *p_flp; ///< 'formatlistpat'
EXTERN char *p_fo; ///< 'formatoptions'
@@ -488,9 +378,6 @@ EXTERN char *p_isp; ///< 'isprint'
EXTERN int p_js; ///< 'joinspaces'
EXTERN char *p_jop; ///< 'jumpooptions'
EXTERN unsigned jop_flags;
-#define JOP_STACK 0x01
-#define JOP_VIEW 0x02
-#define JOP_CLEAN 0x04
EXTERN char *p_keymap; ///< 'keymap'
EXTERN char *p_kp; ///< 'keywordprg'
EXTERN char *p_km; ///< 'keymodel'
@@ -506,7 +393,6 @@ EXTERN char *p_lispwords; ///< 'lispwords'
EXTERN OptInt p_ls; ///< 'laststatus'
EXTERN OptInt p_stal; ///< 'showtabline'
EXTERN char *p_lcs; ///< 'listchars'
-
EXTERN int p_lz; ///< 'lazyredraw'
EXTERN int p_lpl; ///< 'loadplugins'
EXTERN int p_magic; ///< 'magic'
@@ -520,6 +406,7 @@ EXTERN OptInt p_mfd; ///< 'maxfuncdepth'
EXTERN OptInt p_mmd; ///< 'maxmapdepth'
EXTERN OptInt p_mmp; ///< 'maxmempattern'
EXTERN OptInt p_mis; ///< 'menuitems'
+EXTERN char *p_mopt; ///< 'messagesopt'
EXTERN char *p_msm; ///< 'mkspellmem'
EXTERN int p_ml; ///< 'modeline'
EXTERN int p_mle; ///< 'modelineexpr'
@@ -536,7 +423,6 @@ EXTERN OptInt p_mousescroll_vert INIT( = MOUSESCROLL_VERT_DFLT);
EXTERN OptInt p_mousescroll_hor INIT( = MOUSESCROLL_HOR_DFLT);
EXTERN OptInt p_mouset; ///< 'mousetime'
EXTERN int p_more; ///< 'more'
-EXTERN OptInt p_mhi; ///< 'msghistory'
EXTERN char *p_nf; ///< 'nrformats'
EXTERN char *p_opfunc; ///< 'operatorfunc'
EXTERN char *p_para; ///< 'paragraphs'
@@ -551,14 +437,6 @@ EXTERN char *p_qe; ///< 'quoteescape'
EXTERN int p_ro; ///< 'readonly'
EXTERN char *p_rdb; ///< 'redrawdebug'
EXTERN unsigned rdb_flags;
-#define RDB_COMPOSITOR 0x001
-#define RDB_NOTHROTTLE 0x002
-#define RDB_INVALID 0x004
-#define RDB_NODELTA 0x008
-#define RDB_LINE 0x010
-#define RDB_FLUSH 0x020
-#define RDB_INTERSECT 0x040
-
EXTERN OptInt p_rdt; ///< 'redrawtime'
EXTERN OptInt p_re; ///< 'regexpengine'
EXTERN OptInt p_report; ///< 'report'
@@ -580,26 +458,6 @@ EXTERN char *p_sel; ///< 'selection'
EXTERN char *p_slm; ///< 'selectmode'
EXTERN char *p_ssop; ///< 'sessionoptions'
EXTERN unsigned ssop_flags;
-
-#define SSOP_BUFFERS 0x001
-#define SSOP_WINPOS 0x002
-#define SSOP_RESIZE 0x004
-#define SSOP_WINSIZE 0x008
-#define SSOP_LOCALOPTIONS 0x010
-#define SSOP_OPTIONS 0x020
-#define SSOP_HELP 0x040
-#define SSOP_BLANK 0x080
-#define SSOP_GLOBALS 0x100
-#define SSOP_SLASH 0x200 // Deprecated, always set.
-#define SSOP_UNIX 0x400 // Deprecated, always set.
-#define SSOP_SESDIR 0x800
-#define SSOP_CURDIR 0x1000
-#define SSOP_FOLDS 0x2000
-#define SSOP_CURSOR 0x4000
-#define SSOP_TABPAGES 0x8000
-#define SSOP_TERMINAL 0x10000
-#define SSOP_SKIP_RTP 0x20000
-
EXTERN char *p_sh; ///< 'shell'
EXTERN char *p_shcf; ///< 'shellcmdflag'
EXTERN char *p_sp; ///< 'shellpipe'
@@ -636,13 +494,6 @@ EXTERN OptInt p_tpm; ///< 'tabpagemax'
EXTERN char *p_tal; ///< 'tabline'
EXTERN char *p_tpf; ///< 'termpastefilter'
EXTERN unsigned tpf_flags; ///< flags from 'termpastefilter'
-#define TPF_BS 0x001
-#define TPF_HT 0x002
-#define TPF_FF 0x004
-#define TPF_ESC 0x008
-#define TPF_DEL 0x010
-#define TPF_C0 0x020
-#define TPF_C1 0x040
EXTERN char *p_tfu; ///< 'tagfunc'
EXTERN char *p_spc; ///< 'spellcapcheck'
EXTERN char *p_spf; ///< 'spellfile'
@@ -655,28 +506,14 @@ EXTERN int p_sol; ///< 'startofline'
EXTERN char *p_su; ///< 'suffixes'
EXTERN char *p_swb; ///< 'switchbuf'
EXTERN unsigned swb_flags;
-// 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_spk; ///< 'splitkeep'
EXTERN char *p_syn; ///< 'syntax'
EXTERN char *p_tcl; ///< 'tabclose'
EXTERN unsigned tcl_flags; ///< flags from 'tabclose'
-#define TCL_LEFT 0x001
-#define TCL_USELAST 0x002
EXTERN OptInt p_ts; ///< 'tabstop'
EXTERN int p_tbs; ///< 'tagbsearch'
EXTERN char *p_tc; ///< 'tagcase'
EXTERN unsigned tc_flags; ///< flags from 'tagcase'
-#define TC_FOLLOWIC 0x01
-#define TC_IGNORE 0x02
-#define TC_MATCH 0x04
-#define TC_FOLLOWSCS 0x08
-#define TC_SMART 0x10
EXTERN OptInt p_tl; ///< 'taglength'
EXTERN int p_tr; ///< 'tagrelative'
EXTERN char *p_tags; ///< 'tags'
@@ -708,16 +545,10 @@ EXTERN char *p_vsts; ///< 'varsofttabstop'
EXTERN char *p_vts; ///< 'vartabstop'
EXTERN char *p_vdir; ///< 'viewdir'
EXTERN char *p_vop; ///< 'viewoptions'
-EXTERN unsigned vop_flags; ///< uses SSOP_ flags
+EXTERN unsigned vop_flags; ///< uses OptSsopFlags
EXTERN int p_vb; ///< 'visualbell'
EXTERN char *p_ve; ///< 'virtualedit'
EXTERN unsigned ve_flags;
-#define VE_BLOCK 5U // includes "all"
-#define VE_INSERT 6U // includes "all"
-#define VE_ALL 4U
-#define VE_ONEMORE 8U
-#define VE_NONE 16U // "none"
-#define VE_NONEU 32U // "NONE"
EXTERN OptInt p_verbose; ///< 'verbose'
#ifdef IN_OPTION_C
char *p_vfile = empty_string_option; ///< used before options are initialized
@@ -727,9 +558,6 @@ extern char *p_vfile; ///< 'verbosefile'
EXTERN int p_warn; ///< 'warn'
EXTERN char *p_wop; ///< 'wildoptions'
EXTERN unsigned wop_flags;
-#define WOP_FUZZY 0x01
-#define WOP_TAGFILE 0x02
-#define WOP_PUM 0x04
EXTERN OptInt p_window; ///< 'window'
EXTERN char *p_wak; ///< 'winaltkeys'
EXTERN char *p_wig; ///< 'wildignore'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 9f86ef7489..dcd6e0a58b 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -7,13 +7,16 @@
--- @field alias? string|string[]
--- @field short_desc? string|fun(): string
--- @field varname? string
---- @field type vim.option_type|vim.option_type[]
+--- @field flags_varname? string
+--- @field type vim.option_type
--- @field immutable? boolean
--- @field list? 'comma'|'onecomma'|'commacolon'|'onecommacolon'|'flags'|'flagscomma'
--- @field scope vim.option_scope[]
--- @field deny_duplicates? boolean
--- @field enable_if? string
---- @field defaults? vim.option_defaults
+--- @field defaults? vim.option_defaults|vim.option_value|fun(): string
+--- @field values? vim.option_valid_values
+--- @field flags? true|table<string,integer>
--- @field secure? true
--- @field noglob? true
--- @field normal_fname_chars? true
@@ -27,7 +30,11 @@
--- @field no_mkrc? true
--- @field alloced? true
--- @field redraw? vim.option_redraw[]
+---
+--- If not provided and `values` is present, then is set to 'did_set_str_generic'
--- @field cb? string
+---
+--- If not provided and `values` is present, then is set to 'expand_set_str_generic'
--- @field expand_cb? string
--- @field tags? string[]
@@ -35,14 +42,15 @@
--- @field condition? string
--- string: #ifdef string
--- !string: #ifndef string
---- @field if_true integer|boolean|string|fun(): string
---- @field if_false? integer|boolean|string
+--- @field if_true vim.option_value|fun(): string
+--- @field if_false? vim.option_value
--- @field doc? string Default to show in options.txt
---- @field meta? integer|boolean|string Default to use in Lua meta files
+--- @field meta? string Default to use in Lua meta files
--- @alias vim.option_scope 'global'|'buf'|'win'
--- @alias vim.option_type 'boolean'|'number'|'string'
---- @alias vim.option_value boolean|number|string
+--- @alias vim.option_value boolean|integer|string
+--- @alias vim.option_valid_values (string|[string,vim.option_valid_values])[]
--- @alias vim.option_redraw
--- |'statuslines'
@@ -78,7 +86,7 @@ local function N_(s) -- luacheck: ignore 211 (currently unused)
end
-- luacheck: ignore 621
-return {
+local options = {
cstr = cstr,
--- @type string[]
valid_scopes = { 'global', 'buf', 'win' },
@@ -87,7 +95,7 @@ return {
options = {
{
abbreviation = 'al',
- defaults = { if_true = 224 },
+ defaults = 224,
full_name = 'aleph',
scope = { 'global' },
short_desc = N_('ASCII code of the letter Aleph (Hebrew)'),
@@ -96,7 +104,7 @@ return {
},
{
abbreviation = 'ari',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Allow CTRL-_ in Insert mode. This is default off, to avoid that users
that accidentally type CTRL-_ instead of SHIFT-_ get into reverse
@@ -111,7 +119,8 @@ return {
{
abbreviation = 'ambw',
cb = 'did_set_ambiwidth',
- defaults = { if_true = 'single' },
+ defaults = 'single',
+ values = { 'single', 'double' },
desc = [=[
Tells Vim what to do with characters with East Asian Width Class
Ambiguous (such as Euro, Registered Sign, Copyright Sign, Greek
@@ -144,7 +153,6 @@ return {
set to one of CJK locales. See Unicode Standard Annex #11
(https://www.unicode.org/reports/tr11).
]=],
- expand_cb = 'expand_set_ambiwidth',
full_name = 'ambiwidth',
redraw = { 'all_windows', 'ui_option' },
scope = { 'global' },
@@ -155,7 +163,7 @@ return {
{
abbreviation = 'arab',
cb = 'did_set_arabic',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
This option can be set to start editing Arabic text.
Setting this option will:
@@ -180,7 +188,7 @@ return {
},
{
abbreviation = 'arshape',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on and 'termbidi' is off, the required visual character
corrections that need to take place for displaying the Arabic language
@@ -205,7 +213,7 @@ return {
{
abbreviation = 'acd',
cb = 'did_set_autochdir',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, Vim will change the current working directory whenever you
open a file, switch buffers, delete a buffer or open/close a window.
@@ -222,7 +230,7 @@ return {
},
{
abbreviation = 'ai',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Copy indent from current line when starting a new line (typing <CR>
in Insert mode or when using the "o" or "O" command). If you do not
@@ -244,7 +252,7 @@ return {
},
{
abbreviation = 'ar',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When a file has been detected to have been changed outside of Vim and
it has not been changed inside of Vim, automatically read it again.
@@ -264,7 +272,7 @@ return {
},
{
abbreviation = 'aw',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Write the contents of the file, if it has been modified, on each
`:next`, `:rewind`, `:last`, `:first`, `:previous`, `:stop`,
@@ -289,7 +297,7 @@ return {
},
{
abbreviation = 'awa',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Like 'autowrite', but also used for commands ":edit", ":enew", ":quit",
":qall", ":exit", ":xit", ":recover" and closing the Vim window.
@@ -305,7 +313,8 @@ return {
{
abbreviation = 'bg',
cb = 'did_set_background',
- defaults = { if_true = 'dark' },
+ defaults = 'dark',
+ values = { 'light', 'dark' },
desc = [=[
When set to "dark" or "light", adjusts the default color groups for
that background type. The |TUI| or other UI sets this on startup
@@ -330,7 +339,6 @@ return {
will change. To use other settings, place ":highlight" commands AFTER
the setting of the 'background' option.
]=],
- expand_cb = 'expand_set_background',
full_name = 'background',
scope = { 'global' },
short_desc = N_('"dark" or "light", used for highlight colors'),
@@ -340,7 +348,8 @@ return {
{
abbreviation = 'bs',
cb = 'did_set_backspace',
- defaults = { if_true = 'indent,eol,start' },
+ defaults = 'indent,eol,start',
+ values = { 'indent', 'eol', 'start', 'nostop' },
deny_duplicates = true,
desc = [=[
Influences the working of <BS>, <Del>, CTRL-W and CTRL-U in Insert
@@ -357,7 +366,6 @@ return {
When the value is empty, Vi compatible backspacing is used, none of
the ways mentioned for the items above are possible.
]=],
- expand_cb = 'expand_set_backspace',
full_name = 'backspace',
list = 'onecomma',
scope = { 'global' },
@@ -367,7 +375,7 @@ return {
},
{
abbreviation = 'bk',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Make a backup before overwriting a file. Leave it around after the
file has been successfully written. If you do not want to keep the
@@ -390,6 +398,8 @@ return {
abbreviation = 'bkc',
cb = 'did_set_backupcopy',
defaults = { condition = 'UNIX', if_false = 'auto', if_true = 'auto' },
+ values = { 'yes', 'auto', 'no', 'breaksymlink', 'breakhardlink' },
+ flags = true,
deny_duplicates = true,
desc = [=[
When writing a file and a backup is made, this option tells how it's
@@ -455,17 +465,17 @@ return {
the system may refuse to do this. In that case the "auto" value will
again not rename the file.
]=],
- expand_cb = 'expand_set_backupcopy',
full_name = 'backupcopy',
list = 'onecomma',
scope = { 'global', 'buf' },
short_desc = N_("make backup as a copy, don't rename the file"),
type = 'string',
varname = 'p_bkc',
+ flags_varname = 'bkc_flags',
},
{
abbreviation = 'bdir',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of directories for the backup file, separated with commas.
@@ -521,7 +531,7 @@ return {
{
abbreviation = 'bex',
cb = 'did_set_backupext_or_patchmode',
- defaults = { if_true = '~' },
+ defaults = '~',
desc = [=[
String which is appended to a file name to make the name of the
backup file. The default is quite unusual, because this avoids
@@ -585,8 +595,30 @@ return {
},
{
abbreviation = 'bo',
- cb = 'did_set_belloff',
- defaults = { if_true = 'all' },
+ defaults = 'all',
+ values = {
+ 'all',
+ 'backspace',
+ 'cursor',
+ 'complete',
+ 'copy',
+ 'ctrlg',
+ 'error',
+ 'esc',
+ 'ex',
+ 'hangul',
+ 'insertmode',
+ 'lang',
+ 'mess',
+ 'showmatch',
+ 'operator',
+ 'register',
+ 'shell',
+ 'spell',
+ 'term',
+ 'wildmode',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
Specifies for which events the bell will not be rung. It is a comma-
@@ -626,18 +658,18 @@ return {
indicate that an error occurred. It can be silenced by adding the
"error" keyword.
]=],
- expand_cb = 'expand_set_belloff',
full_name = 'belloff',
list = 'comma',
scope = { 'global' },
short_desc = N_('do not ring the bell for these reasons'),
type = 'string',
varname = 'p_bo',
+ flags_varname = 'bo_flags',
},
{
abbreviation = 'bin',
cb = 'did_set_binary',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
This option should be set before editing a binary file. You can also
use the |-b| Vim argument. When this option is switched on a few
@@ -675,7 +707,7 @@ return {
},
{
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When writing a file and the following conditions are met, a BOM (Byte
Order Mark) is prepended to the file:
@@ -708,6 +740,7 @@ return {
if_true = ' \t!@*-+;:,./?',
doc = '" ^I!@*-+;:,./?"',
},
+ flags = true,
desc = [=[
This option lets you choose which characters might cause a line
break if 'linebreak' is on. Only works for ASCII characters.
@@ -722,7 +755,7 @@ return {
},
{
abbreviation = 'bri',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Every wrapped line will continue visually indented (same amount of
space as the beginning of that line), thus preserving horizontal blocks
@@ -737,7 +770,9 @@ return {
{
abbreviation = 'briopt',
cb = 'did_set_breakindentopt',
- defaults = { if_true = '' },
+ defaults = '',
+ -- Keep this in sync with briopt_check().
+ values = { 'shift:', 'min:', 'sbr', 'list:', 'column:' },
deny_duplicates = true,
desc = [=[
Settings for 'breakindent'. It can consist of the following optional
@@ -768,7 +803,6 @@ return {
added for the 'showbreak' setting.
(default: off)
]=],
- expand_cb = 'expand_set_breakindentopt',
full_name = 'breakindentopt',
list = 'onecomma',
redraw = { 'current_buffer' },
@@ -799,7 +833,8 @@ return {
{
abbreviation = 'bh',
cb = 'did_set_bufhidden',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { '', 'hide', 'unload', 'delete', 'wipe' },
desc = [=[
This option specifies what happens when a buffer is no longer
displayed in a window:
@@ -821,7 +856,6 @@ return {
This option is used together with 'buftype' and 'swapfile' to specify
special kinds of buffers. See |special-buffers|.
]=],
- expand_cb = 'expand_set_bufhidden',
full_name = 'bufhidden',
noglob = true,
scope = { 'buf' },
@@ -832,7 +866,7 @@ return {
{
abbreviation = 'bl',
cb = 'did_set_buflisted',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When this option is set, the buffer shows up in the buffer list. If
it is reset it is not used for ":bnext", "ls", the Buffers menu, etc.
@@ -851,7 +885,17 @@ return {
{
abbreviation = 'bt',
cb = 'did_set_buftype',
- defaults = { if_true = '' },
+ defaults = '',
+ values = {
+ '',
+ 'acwrite',
+ 'help',
+ 'nofile',
+ 'nowrite',
+ 'quickfix',
+ 'terminal',
+ 'prompt',
+ },
desc = [=[
The value of this option specifies the type of a buffer:
<empty> normal buffer
@@ -898,7 +942,6 @@ return {
without saving. For writing there must be matching |BufWriteCmd|,
|FileWriteCmd| or |FileAppendCmd| autocommands.
]=],
- expand_cb = 'expand_set_buftype',
full_name = 'buftype',
noglob = true,
scope = { 'buf' },
@@ -909,8 +952,9 @@ return {
},
{
abbreviation = 'cmp',
- cb = 'did_set_casemap',
- defaults = { if_true = 'internal,keepascii' },
+ defaults = 'internal,keepascii',
+ values = { 'internal', 'keepascii' },
+ flags = true,
deny_duplicates = true,
desc = [=[
Specifies details about changing the case of letters. It may contain
@@ -923,17 +967,17 @@ return {
case mapping, the current locale is not effective.
This probably only matters for Turkish.
]=],
- expand_cb = 'expand_set_casemap',
full_name = 'casemap',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('specifies how case of letters is changed'),
type = 'string',
varname = 'p_cmp',
+ flags_varname = 'cmp_flags',
},
{
abbreviation = 'cdh',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, |:cd|, |:tcd| and |:lcd| without an argument changes the
current working directory to the |$HOME| directory like in Unix.
@@ -1007,7 +1051,7 @@ return {
varname = 'p_cedit',
},
{
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
|channel| connected to the buffer, or 0 if no channel is connected.
In a |:terminal| buffer this is the terminal channel.
@@ -1024,7 +1068,7 @@ return {
{
abbreviation = 'ccv',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
An expression that is used for character encoding conversion. It is
evaluated when a file that is to be read or has been written has a
@@ -1080,7 +1124,7 @@ return {
},
{
abbreviation = 'cin',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Enables automatic C program indenting. See 'cinkeys' to set the keys
that trigger reindenting in insert mode and 'cinoptions' to set your
@@ -1101,7 +1145,7 @@ return {
},
{
abbreviation = 'cink',
- defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
+ defaults = '0{,0},0),0],:,0#,!^F,o,O,e',
deny_duplicates = true,
desc = [=[
A list of keys that, when typed in Insert mode, cause reindenting of
@@ -1120,7 +1164,7 @@ return {
{
abbreviation = 'cino',
cb = 'did_set_cinoptions',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
The 'cinoptions' affect the way 'cindent' reindents lines in a C
@@ -1136,7 +1180,7 @@ return {
},
{
abbreviation = 'cinsd',
- defaults = { if_true = 'public,protected,private' },
+ defaults = 'public,protected,private',
deny_duplicates = true,
desc = [=[
Keywords that are interpreted as a C++ scope declaration by |cino-g|.
@@ -1154,7 +1198,7 @@ return {
},
{
abbreviation = 'cinw',
- defaults = { if_true = 'if,else,while,do,for,switch' },
+ defaults = 'if,else,while,do,for,switch',
deny_duplicates = true,
desc = [=[
These keywords start an extra indent in the next line when
@@ -1173,8 +1217,9 @@ return {
},
{
abbreviation = 'cb',
- cb = 'did_set_clipboard',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'unnamed', 'unnamedplus' },
+ flags = true,
desc = [=[
This option is a list of comma-separated names.
These names are recognized:
@@ -1200,18 +1245,18 @@ return {
"*". See |clipboard|.
]=],
deny_duplicates = true,
- expand_cb = 'expand_set_clipboard',
full_name = 'clipboard',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('use the clipboard as the unnamed register'),
type = 'string',
varname = 'p_cb',
+ flags_varname = 'cb_flags',
},
{
abbreviation = 'ch',
cb = 'did_set_cmdheight',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Number of screen lines to use for the command-line. Helps avoiding
|hit-enter| prompts.
@@ -1236,7 +1281,7 @@ return {
},
{
abbreviation = 'cwh',
- defaults = { if_true = 7 },
+ defaults = 7,
desc = [=[
Number of screen lines to use for the command-line window. |cmdwin|
]=],
@@ -1249,7 +1294,7 @@ return {
{
abbreviation = 'cc',
cb = 'did_set_colorcolumn',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
'colorcolumn' is a comma-separated list of screen columns that are
@@ -1304,7 +1349,7 @@ return {
{
abbreviation = 'com',
cb = 'did_set_comments',
- defaults = { if_true = 's1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-,fb:•' },
+ defaults = 's1:/*,mb:*,ex:*/,://,b:#,:%,:XCOMM,n:>,fb:-,fb:•',
deny_duplicates = true,
desc = [=[
A comma-separated list of strings that can start a comment line. See
@@ -1322,7 +1367,7 @@ return {
{
abbreviation = 'cms',
cb = 'did_set_commentstring',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
A template for a comment. The "%s" in the value is replaced with the
comment text, and should be padded with a space when possible.
@@ -1337,7 +1382,7 @@ return {
},
{
abbreviation = 'cp',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'compatible',
scope = { 'global' },
short_desc = N_('No description'),
@@ -1347,7 +1392,8 @@ return {
{
abbreviation = 'cpt',
cb = 'did_set_complete',
- defaults = { if_true = '.,w,b,u,t' },
+ defaults = '.,w,b,u,t',
+ values = { '.', 'w', 'b', 'u', 'k', 'kspell', 's', 'i', 'd', ']', 't', 'U', 'f' },
deny_duplicates = true,
desc = [=[
This option specifies how keyword completion |ins-completion| works
@@ -1383,7 +1429,6 @@ return {
based expansion (e.g., dictionary |i_CTRL-X_CTRL-K|, included patterns
|i_CTRL-X_CTRL-I|, tags |i_CTRL-X_CTRL-]| and normal expansions).
]=],
- expand_cb = 'expand_set_complete',
full_name = 'complete',
list = 'onecomma',
scope = { 'buf' },
@@ -1395,7 +1440,7 @@ return {
{
abbreviation = 'cfu',
cb = 'did_set_completefunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used for Insert mode completion
with CTRL-X CTRL-U. |i_CTRL-X_CTRL-U|
@@ -1417,7 +1462,8 @@ return {
{
abbreviation = 'cia',
cb = 'did_set_completeitemalign',
- defaults = { if_true = 'abbr,kind,menu' },
+ defaults = 'abbr,kind,menu',
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of |complete-items| that controls the alignment
@@ -1437,7 +1483,20 @@ return {
{
abbreviation = 'cot',
cb = 'did_set_completeopt',
- defaults = { if_true = 'menu,preview' },
+ defaults = 'menu,preview',
+ values = {
+ 'menu',
+ 'menuone',
+ 'longest',
+ 'preview',
+ 'popup',
+ 'noinsert',
+ 'noselect',
+ 'fuzzy',
+ 'nosort',
+ 'preinsert',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of options for Insert mode completion
@@ -1469,9 +1528,9 @@ return {
a match from the menu. Only works in combination with
"menu" or "menuone". No effect if "longest" is present.
- noselect Do not select a match in the menu, force the user to
- select one from the menu. Only works in combination with
- "menu" or "menuone".
+ noselect Same as "noinsert", except that no menu item is
+ pre-selected. If both "noinsert" and "noselect" are
+ present, "noselect" has precedence.
fuzzy Enable |fuzzy-matching| for completion candidates. This
allows for more flexible and intuitive matching, where
@@ -1480,19 +1539,30 @@ return {
difference how completion candidates are reduced from the
list of alternatives, but not how the candidates are
collected (using different completion types).
+
+ nosort Disable sorting of completion candidates based on fuzzy
+ scores when "fuzzy" is enabled. Candidates will appear
+ in their original order.
+
+ preinsert
+ Preinsert the portion of the first candidate word that is
+ not part of the current completion leader and using the
+ |hl-ComplMatchIns| highlight group. Does not work when
+ "fuzzy" is also included.
]=],
- expand_cb = 'expand_set_completeopt',
full_name = 'completeopt',
list = 'onecomma',
scope = { 'global', 'buf' },
short_desc = N_('options for Insert mode completion'),
type = 'string',
varname = 'p_cot',
+ flags_varname = 'cot_flags',
},
{
abbreviation = 'csl',
cb = 'did_set_completeslash',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { '', 'slash', 'backslash' },
desc = [=[
only modifiable in MS-Windows
When this option is set it overrules 'shellslash' for completion:
@@ -1507,7 +1577,6 @@ return {
command line completion the global value is used.
]=],
enable_if = 'BACKSLASH_IN_FILENAME',
- expand_cb = 'expand_set_completeslash',
full_name = 'completeslash',
scope = { 'buf' },
type = 'string',
@@ -1516,7 +1585,7 @@ return {
{
abbreviation = 'cocu',
cb = 'did_set_concealcursor',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Sets the modes in which text in the cursor line can also be concealed.
When the current mode is listed then concealing happens just like in
@@ -1544,7 +1613,7 @@ return {
},
{
abbreviation = 'cole',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Determine how text with the "conceal" syntax attribute |:syn-conceal|
is shown:
@@ -1573,7 +1642,7 @@ return {
},
{
abbreviation = 'cf',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When 'confirm' is on, certain operations that would normally
fail because of unsaved changes to a buffer, e.g. ":q" and ":e",
@@ -1592,7 +1661,7 @@ return {
},
{
abbreviation = 'ci',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Copy the structure of the existing lines indent when autoindenting a
new line. Normally the new indent is reconstructed by a series of
@@ -1613,7 +1682,7 @@ return {
{
abbreviation = 'cpo',
cb = 'did_set_cpoptions',
- defaults = { if_true = macros('CPO_VIM', 'string') },
+ defaults = macros('CPO_VIM', 'string'),
desc = [=[
A sequence of single character flags. When a character is present
this indicates Vi-compatible behavior. This is used for things where
@@ -1855,7 +1924,7 @@ return {
},
{
abbreviation = 'crb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When this option is set, as the cursor in the current
window moves other cursorbound windows (windows that also have
@@ -1872,7 +1941,7 @@ return {
},
{
abbreviation = 'cuc',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Highlight the screen column of the cursor with CursorColumn
|hl-CursorColumn|. Useful to align text. Will make screen redrawing
@@ -1891,7 +1960,7 @@ return {
},
{
abbreviation = 'cul',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Highlight the text line of the cursor with CursorLine |hl-CursorLine|.
Useful to easily spot the cursor. Will make screen redrawing slower.
@@ -1907,7 +1976,14 @@ return {
{
abbreviation = 'culopt',
cb = 'did_set_cursorlineopt',
- defaults = { if_true = 'both' },
+ defaults = 'both',
+ -- Keep this in sync with fill_culopt_flags().
+ values = { 'line', 'screenline', 'number', 'both' },
+ flags = {
+ Line = 0x01,
+ Screenline = 0x02,
+ Number = 0x04,
+ },
deny_duplicates = true,
desc = [=[
Comma-separated list of settings for how 'cursorline' is displayed.
@@ -1924,7 +2000,6 @@ return {
"line" and "screenline" cannot be used together.
]=],
- expand_cb = 'expand_set_cursorlineopt',
full_name = 'cursorlineopt',
list = 'onecomma',
redraw = { 'current_window', 'highlight_only' },
@@ -1933,8 +2008,8 @@ return {
type = 'string',
},
{
- cb = 'did_set_debug',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'msg', 'throw', 'beep' },
desc = [=[
These values can be used:
msg Error messages that would otherwise be omitted will be given
@@ -1947,8 +2022,9 @@ return {
"msg" and "throw" are useful for debugging 'foldexpr', 'formatexpr' or
'indentexpr'.
]=],
- expand_cb = 'expand_set_debug',
+ -- TODO(lewis6991): bug, values currently cannot be combined
full_name = 'debug',
+ list = 'comma',
scope = { 'global' },
short_desc = N_('to "msg" to see all error messages'),
type = 'string',
@@ -1956,7 +2032,7 @@ return {
},
{
abbreviation = 'def',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Pattern to be used to find a macro definition. It is a search
pattern, just like for the "/" command. This option is used for the
@@ -1986,7 +2062,7 @@ return {
},
{
abbreviation = 'deco',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If editing Unicode and this option is set, backspace and Normal mode
"x" delete each combining character on its own. When it is off (the
@@ -2006,7 +2082,7 @@ return {
},
{
abbreviation = 'dict',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of file names, separated by commas, that are used to lookup words
@@ -2043,7 +2119,7 @@ return {
},
{
cb = 'did_set_diff',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Join the current window in the group of windows that shows differences
between files. See |diff-mode|.
@@ -2058,7 +2134,7 @@ return {
{
abbreviation = 'dex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to obtain a diff file (either ed-style
or unified-style) from two versions of a file. See |diff-diffexpr|.
@@ -2076,17 +2152,46 @@ return {
{
abbreviation = 'dip',
cb = 'did_set_diffopt',
- defaults = { if_true = 'internal,filler,closeoff' },
+ defaults = 'internal,filler,closeoff',
+ -- Keep this in sync with diffopt_changed().
+ values = {
+ 'filler',
+ 'context:',
+ 'iblank',
+ 'icase',
+ 'iwhite',
+ 'iwhiteall',
+ 'iwhiteeol',
+ 'horizontal',
+ 'vertical',
+ 'closeoff',
+ 'hiddenoff',
+ 'foldcolumn:',
+ 'followwrap',
+ 'internal',
+ 'indent-heuristic',
+ { 'algorithm:', { 'myers', 'minimal', 'patience', 'histogram' } },
+ 'linematch:',
+ },
deny_duplicates = true,
desc = [=[
Option settings for diff mode. It can consist of the following items.
All are optional. Items must be separated by a comma.
- filler Show filler lines, to keep the text
- synchronized with a window that has inserted
- lines at the same position. Mostly useful
- when windows are side-by-side and 'scrollbind'
- is set.
+ algorithm:{text} Use the specified diff algorithm with the
+ internal diff engine. Currently supported
+ algorithms are:
+ myers the default algorithm
+ minimal spend extra time to generate the
+ smallest possible diff
+ patience patience diff algorithm
+ histogram histogram diff algorithm
+
+ closeoff When a window is closed where 'diff' is set
+ and there is only one window remaining in the
+ same tab page with 'diff' set, execute
+ `:diffoff` in that window. This undoes a
+ `:diffsplit` command.
context:{n} Use a context of {n} lines between a change
and a fold that contains unchanged lines.
@@ -2097,6 +2202,23 @@ return {
value (999999) to disable folding completely.
See |fold-diff|.
+ filler Show filler lines, to keep the text
+ synchronized with a window that has inserted
+ lines at the same position. Mostly useful
+ when windows are side-by-side and 'scrollbind'
+ is set.
+
+ foldcolumn:{n} Set the 'foldcolumn' option to {n} when
+ starting diff mode. Without this 2 is used.
+
+ followwrap Follow the 'wrap' option and leave as it is.
+
+ horizontal Start diff mode with horizontal splits (unless
+ explicitly specified otherwise).
+
+ hiddenoff Do not use diff mode for a buffer when it
+ becomes hidden.
+
iblank Ignore changes where lines are all blank. Adds
the "-B" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
@@ -2110,6 +2232,17 @@ return {
are considered the same. Adds the "-i" flag
to the "diff" command if 'diffexpr' is empty.
+ indent-heuristic
+ Use the indent heuristic for the internal
+ diff library.
+
+ internal Use the internal diff library. This is
+ ignored when 'diffexpr' is set. *E960*
+ When running out of memory when writing a
+ buffer this item will be ignored for diffs
+ involving that buffer. Set the 'verbose'
+ option to see when this happens.
+
iwhite Ignore changes in amount of white space. Adds
the "-b" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
@@ -2129,56 +2262,19 @@ return {
of the "diff" command for what this does
exactly.
- horizontal Start diff mode with horizontal splits (unless
- explicitly specified otherwise).
+ linematch:{n} Align and mark changes between the most
+ similar lines between the buffers. When the
+ total number of lines in the diff hunk exceeds
+ {n}, the lines will not be aligned because for
+ very large diff hunks there will be a
+ noticeable lag. A reasonable setting is
+ "linematch:60", as this will enable alignment
+ for a 2 buffer diff hunk of 30 lines each,
+ or a 3 buffer diff hunk of 20 lines each.
vertical Start diff mode with vertical splits (unless
explicitly specified otherwise).
- closeoff When a window is closed where 'diff' is set
- and there is only one window remaining in the
- same tab page with 'diff' set, execute
- `:diffoff` in that window. This undoes a
- `:diffsplit` command.
-
- hiddenoff Do not use diff mode for a buffer when it
- becomes hidden.
-
- foldcolumn:{n} Set the 'foldcolumn' option to {n} when
- starting diff mode. Without this 2 is used.
-
- followwrap Follow the 'wrap' option and leave as it is.
-
- internal Use the internal diff library. This is
- ignored when 'diffexpr' is set. *E960*
- When running out of memory when writing a
- buffer this item will be ignored for diffs
- involving that buffer. Set the 'verbose'
- option to see when this happens.
-
- indent-heuristic
- Use the indent heuristic for the internal
- diff library.
-
- linematch:{n} Enable a second stage diff on each generated
- hunk in order to align lines. When the total
- number of lines in a hunk exceeds {n}, the
- second stage diff will not be performed as
- very large hunks can cause noticeable lag. A
- recommended setting is "linematch:60", as this
- will enable alignment for a 2 buffer diff with
- hunks of up to 30 lines each, or a 3 buffer
- diff with hunks of up to 20 lines each.
-
- algorithm:{text} Use the specified diff algorithm with the
- internal diff engine. Currently supported
- algorithms are:
- myers the default algorithm
- minimal spend extra time to generate the
- smallest possible diff
- patience patience diff algorithm
- histogram histogram diff algorithm
-
Examples: >vim
set diffopt=internal,filler,context:4
set diffopt=
@@ -2197,7 +2293,7 @@ return {
},
{
abbreviation = 'dg',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Enable the entering of digraphs in Insert mode with {char1} <BS>
{char2}. See |digraphs|.
@@ -2210,7 +2306,7 @@ return {
},
{
abbreviation = 'dir',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of directory names for the swap file, separated with commas.
@@ -2268,7 +2364,9 @@ return {
{
abbreviation = 'dy',
cb = 'did_set_display',
- defaults = { if_true = 'lastline' },
+ defaults = 'lastline',
+ values = { 'lastline', 'truncate', 'uhex', 'msgsep' },
+ flags = true,
deny_duplicates = true,
desc = [=[
Change the way text is displayed. This is a comma-separated list of
@@ -2289,7 +2387,6 @@ return {
The "@" character can be changed by setting the "lastline" item in
'fillchars'. The character is highlighted with |hl-NonText|.
]=],
- expand_cb = 'expand_set_display',
full_name = 'display',
list = 'onecomma',
redraw = { 'all_windows' },
@@ -2297,18 +2394,18 @@ return {
short_desc = N_('list of flags for how to display text'),
type = 'string',
varname = 'p_dy',
+ flags_varname = 'dy_flags',
},
{
abbreviation = 'ead',
- cb = 'did_set_eadirection',
- defaults = { if_true = 'both' },
+ defaults = 'both',
+ values = { 'both', 'ver', 'hor' },
desc = [=[
Tells when the 'equalalways' option applies:
ver vertically, width of windows is not affected
hor horizontally, height of windows is not affected
both width and height of windows is affected
]=],
- expand_cb = 'expand_set_eadirection',
full_name = 'eadirection',
scope = { 'global' },
short_desc = N_("in which direction 'equalalways' works"),
@@ -2317,7 +2414,7 @@ return {
},
{
abbreviation = 'ed',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'edcompatible',
scope = { 'global' },
short_desc = N_('No description'),
@@ -2326,8 +2423,8 @@ return {
},
{
abbreviation = 'emo',
- cb = 'did_set_ambiwidth',
- defaults = { if_true = true },
+ cb = 'did_set_emoji',
+ defaults = true,
desc = [=[
When on all Unicode emoji characters are considered to be full width.
This excludes "text emoji" characters, which are normally displayed as
@@ -2348,7 +2445,7 @@ return {
{
abbreviation = 'enc',
cb = 'did_set_encoding',
- defaults = { if_true = macros('ENC_DFLT', 'string') },
+ defaults = macros('ENC_DFLT', 'string'),
deny_in_modelines = true,
desc = [=[
String-encoding used internally and for |RPC| communication.
@@ -2365,7 +2462,7 @@ return {
{
abbreviation = 'eof',
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Indicates that a CTRL-Z character was found at the end of the file
when reading it. Normally only happens when 'fileformat' is "dos".
@@ -2385,7 +2482,7 @@ return {
{
abbreviation = 'eol',
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When writing a file and this option is off and the 'binary' option
is on, or 'fixeol' option is off, no <EOL> will be written for the
@@ -2411,7 +2508,7 @@ return {
{
abbreviation = 'ea',
cb = 'did_set_equalalways',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, all the windows are automatically made the same size after
splitting or closing a window. This also happens the moment the
@@ -2436,7 +2533,7 @@ return {
},
{
abbreviation = 'ep',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
External program to use for "=" command. When this option is empty
the internal formatting functions are used; either 'lisp', 'cindent'
@@ -2456,7 +2553,7 @@ return {
},
{
abbreviation = 'eb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Ring the bell (beep or screen flash) for error messages. This only
makes a difference for error messages, the bell will be used always
@@ -2472,7 +2569,7 @@ return {
},
{
abbreviation = 'ef',
- defaults = { if_true = macros('DFLT_ERRORFILE', 'string') },
+ defaults = macros('DFLT_ERRORFILE', 'string'),
desc = [=[
Name of the errorfile for the QuickFix mode (see |:cf|).
When the "-q" command-line argument is used, 'errorfile' is set to the
@@ -2512,7 +2609,7 @@ return {
{
abbreviation = 'ei',
cb = 'did_set_eventignore',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
A list of autocommand event names, which are to be ignored.
@@ -2532,7 +2629,7 @@ return {
},
{
abbreviation = 'et',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
In Insert mode: Use the appropriate number of spaces to insert a
<Tab>. Spaces are used in indents with the '>' and '<' commands and
@@ -2547,7 +2644,7 @@ return {
},
{
abbreviation = 'ex',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Automatically execute .nvim.lua, .nvimrc, and .exrc files in the
current directory, if the file is in the |trust| list. Use |:trust| to
@@ -2570,7 +2667,7 @@ return {
{
abbreviation = 'fenc',
cb = 'did_set_encoding',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
File-content encoding for the current buffer. Conversion is done with
iconv() or as specified with 'charconvert'.
@@ -2622,7 +2719,7 @@ return {
},
{
abbreviation = 'fencs',
- defaults = { if_true = 'ucs-bom,utf-8,default,latin1' },
+ defaults = 'ucs-bom,utf-8,default,latin1',
deny_duplicates = true,
desc = [=[
This is a list of character encodings considered when starting to edit
@@ -2689,6 +2786,7 @@ return {
if_false = 'unix',
doc = 'Windows: "dos", Unix: "unix"',
},
+ values = { 'unix', 'dos', 'mac' },
desc = [=[
This gives the <EOL> of the current buffer, which is used for
reading/writing the buffer from/to a file:
@@ -2706,7 +2804,6 @@ return {
option is set, because the file would be different when written.
This option cannot be changed when 'modifiable' is off.
]=],
- expand_cb = 'expand_set_fileformat',
full_name = 'fileformat',
no_mkrc = true,
redraw = { 'curswant', 'statuslines' },
@@ -2717,7 +2814,7 @@ return {
},
{
abbreviation = 'ffs',
- cb = 'did_set_fileformats',
+ cb = 'did_set_str_generic',
defaults = {
condition = 'USE_CRNL',
if_true = 'dos,unix',
@@ -2772,7 +2869,7 @@ return {
used.
Also see |file-formats|.
]=],
- expand_cb = 'expand_set_fileformat',
+ expand_cb = 'expand_set_str_generic',
full_name = 'fileformats',
list = 'onecomma',
scope = { 'global' },
@@ -2802,7 +2899,7 @@ return {
{
abbreviation = 'ft',
cb = 'did_set_filetype_or_syntax',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is set, the FileType autocommand event is triggered.
All autocommands that match with the value of this option will be
@@ -2837,7 +2934,7 @@ return {
{
abbreviation = 'fcs',
cb = 'did_set_chars_option',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Characters to fill the statuslines, vertical separators and special
@@ -2912,7 +3009,7 @@ return {
{
abbreviation = 'ffu',
cb = 'did_set_findfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Function that is called to obtain the filename(s) for the |:find|
command. When this option is empty, the internal |file-searching|
@@ -2972,7 +3069,7 @@ return {
{
abbreviation = 'fixeol',
cb = 'did_set_eof_eol_fixeol_bomb',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When writing a file and this option is on, <EOL> at the end of file
will be restored if missing. Turn this option off if you want to
@@ -2991,15 +3088,14 @@ return {
},
{
abbreviation = 'fcl',
- cb = 'did_set_foldclose',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'all' },
deny_duplicates = true,
desc = [=[
When set to "all", a fold is closed when the cursor isn't in it and
its level is higher than 'foldlevel'. Useful if you want folds to
automatically close when moving out of them.
]=],
- expand_cb = 'expand_set_foldclose',
full_name = 'foldclose',
list = 'onecomma',
redraw = { 'current_window' },
@@ -3010,8 +3106,29 @@ return {
},
{
abbreviation = 'fdc',
- cb = 'did_set_foldcolumn',
- defaults = { if_true = '0' },
+ defaults = '0',
+ 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',
+ },
desc = [=[
When and how to draw the foldcolumn. Valid values are:
"auto": resize to the minimum amount of folds to display.
@@ -3021,7 +3138,6 @@ return {
"[1-9]": to display a fixed number of columns
See |folding|.
]=],
- expand_cb = 'expand_set_foldcolumn',
full_name = 'foldcolumn',
redraw = { 'current_window' },
scope = { 'win' },
@@ -3030,7 +3146,7 @@ return {
},
{
abbreviation = 'fen',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When off, all folds are open. This option can be used to quickly
switch between showing all text unfolded and viewing the text with
@@ -3049,7 +3165,7 @@ return {
{
abbreviation = 'fde',
cb = 'did_set_foldexpr',
- defaults = { if_true = '0' },
+ defaults = '0',
desc = [=[
The expression used for when 'foldmethod' is "expr". It is evaluated
for each line to obtain its fold level. The context is set to the
@@ -3074,7 +3190,7 @@ return {
{
abbreviation = 'fdi',
cb = 'did_set_foldignore',
- defaults = { if_true = '#' },
+ defaults = '#',
desc = [=[
Used only when 'foldmethod' is "indent". Lines starting with
characters in 'foldignore' will get their fold level from surrounding
@@ -3090,7 +3206,7 @@ return {
{
abbreviation = 'fdl',
cb = 'did_set_foldlevel',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Sets the fold level: Folds with a higher level will be closed.
Setting this option to zero will close all folds. Higher numbers will
@@ -3106,7 +3222,7 @@ return {
},
{
abbreviation = 'fdls',
- defaults = { if_true = -1 },
+ defaults = -1,
desc = [=[
Sets 'foldlevel' when starting to edit another buffer in a window.
Useful to always start editing with all folds closed (value zero),
@@ -3128,7 +3244,7 @@ return {
{
abbreviation = 'fmr',
cb = 'did_set_foldmarker',
- defaults = { if_true = '{{{,}}}' },
+ defaults = '{{{,}}}',
deny_duplicates = true,
desc = [=[
The start and end marker used when 'foldmethod' is "marker". There
@@ -3147,7 +3263,8 @@ return {
{
abbreviation = 'fdm',
cb = 'did_set_foldmethod',
- defaults = { if_true = 'manual' },
+ defaults = 'manual',
+ values = { 'manual', 'expr', 'marker', 'indent', 'syntax', 'diff' },
desc = [=[
The kind of folding used for the current window. Possible values:
|fold-manual| manual Folds are created manually.
@@ -3157,7 +3274,6 @@ return {
|fold-syntax| syntax Syntax highlighting items specify folds.
|fold-diff| diff Fold text that is not changed.
]=],
- expand_cb = 'expand_set_foldmethod',
full_name = 'foldmethod',
redraw = { 'current_window' },
scope = { 'win' },
@@ -3167,7 +3283,7 @@ return {
{
abbreviation = 'fml',
cb = 'did_set_foldminlines',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Sets the number of screen lines above which a fold can be displayed
closed. Also for manually closed folds. With the default value of
@@ -3186,7 +3302,7 @@ return {
{
abbreviation = 'fdn',
cb = 'did_set_foldnestmax',
- defaults = { if_true = 20 },
+ defaults = 20,
desc = [=[
Sets the maximum nesting of folds for the "indent" and "syntax"
methods. This avoids that too many folds will be created. Using more
@@ -3200,8 +3316,21 @@ return {
},
{
abbreviation = 'fdo',
- cb = 'did_set_foldopen',
- defaults = { if_true = 'block,hor,mark,percent,quickfix,search,tag,undo' },
+ defaults = 'block,hor,mark,percent,quickfix,search,tag,undo',
+ values = {
+ 'all',
+ 'block',
+ 'hor',
+ 'mark',
+ 'percent',
+ 'quickfix',
+ 'search',
+ 'tag',
+ 'insert',
+ 'undo',
+ 'jump',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
Specifies for which type of commands folds will be opened, if the
@@ -3235,7 +3364,6 @@ return {
To close folds you can re-apply 'foldlevel' with the |zx| command or
set the 'foldclose' option to "all".
]=],
- expand_cb = 'expand_set_foldopen',
full_name = 'foldopen',
list = 'onecomma',
redraw = { 'curswant' },
@@ -3243,11 +3371,12 @@ return {
short_desc = N_('for which commands a fold will be opened'),
type = 'string',
varname = 'p_fdo',
+ flags_varname = 'fdo_flags',
},
{
abbreviation = 'fdt',
cb = 'did_set_optexpr',
- defaults = { if_true = 'foldtext()' },
+ defaults = 'foldtext()',
desc = [=[
An expression which is used to specify the text displayed for a closed
fold. The context is set to the script where 'foldexpr' was set,
@@ -3274,7 +3403,7 @@ return {
{
abbreviation = 'fex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to format a range of lines for the |gq|
operator or automatic formatting (see 'formatoptions'). When this
@@ -3326,7 +3455,7 @@ return {
},
{
abbreviation = 'flp',
- defaults = { if_true = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*' },
+ defaults = '^\\s*\\d\\+[\\]:.)}\\t ]\\s*',
desc = [=[
A pattern that is used to recognize a list header. This is used for
the "n" flag in 'formatoptions'.
@@ -3347,7 +3476,7 @@ return {
{
abbreviation = 'fo',
cb = 'did_set_formatoptions',
- defaults = { if_true = macros('DFLT_FO_VIM', 'string') },
+ defaults = macros('DFLT_FO_VIM', 'string'),
desc = [=[
This is a sequence of letters which describes how automatic
formatting is to be done.
@@ -3366,7 +3495,7 @@ return {
},
{
abbreviation = 'fp',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
The name of an external program that will be used to format the lines
selected with the |gq| operator. The program must take the input on
@@ -3390,7 +3519,7 @@ return {
},
{
abbreviation = 'fs',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, the OS function fsync() will be called after saving a file
(|:write|, |writefile()|, …), |swap-file|, |undo-persistence| and |shada-file|.
@@ -3416,7 +3545,7 @@ return {
},
{
abbreviation = 'gd',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, the ":substitute" flag 'g' is default on. This means that
all matches in a line are substituted instead of one. When a 'g' flag
@@ -3440,7 +3569,7 @@ return {
},
{
abbreviation = 'gfm',
- defaults = { if_true = macros('DFLT_GREPFORMAT', 'string') },
+ defaults = macros('DFLT_GREPFORMAT', 'string'),
deny_duplicates = true,
desc = [=[
Format to recognize for the ":grep" command output.
@@ -3499,7 +3628,7 @@ return {
{
abbreviation = 'gcr',
cb = 'did_set_guicursor',
- defaults = { if_true = 'n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20' },
+ defaults = 'n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor',
deny_duplicates = true,
desc = [=[
Configures the cursor style for each mode. Works in the GUI and many
@@ -3528,6 +3657,7 @@ return {
ci Command-line Insert mode
cr Command-line Replace mode
sm showmatch in Insert mode
+ t Terminal mode
a all modes
The argument-list is a dash separated list of these arguments:
hor{N} horizontal bar, {N} percent of the character height
@@ -3544,7 +3674,8 @@ return {
cursor is not shown. Times are in msec. When one of
the numbers is zero, there is no blinking. E.g.: >vim
set guicursor=n:blinkon0
- < - Default is "blinkon0" for each mode.
+ <
+ Default is "blinkon0" for each mode.
{group-name}
Highlight group that decides the color and font of the
cursor.
@@ -3596,7 +3727,7 @@ return {
},
{
abbreviation = 'gfn',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This is a list of fonts which will be used for the GUI version of Vim.
In its simplest form the value is just one font name. When
@@ -3668,7 +3799,7 @@ return {
},
{
abbreviation = 'gfw',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Comma-separated list of fonts to be used for double-width characters.
@@ -3804,7 +3935,7 @@ return {
},
{
abbreviation = 'gtl',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty describes the text to use in a label of the GUI tab
pages line. When empty and when the result is empty Vim will use a
@@ -3830,7 +3961,7 @@ return {
},
{
abbreviation = 'gtt',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty describes the text to use in a tooltip for the GUI tab
pages line. When empty Vim will use a default tooltip.
@@ -3876,7 +4007,7 @@ return {
{
abbreviation = 'hh',
cb = 'did_set_helpheight',
- defaults = { if_true = 20 },
+ defaults = 20,
desc = [=[
Minimal initial height of the help window when it is opened with the
":help" command. The initial height of the help window is half of the
@@ -3921,7 +4052,7 @@ return {
},
{
abbreviation = 'hid',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When off a buffer is unloaded (including loss of undo information)
when it is |abandon|ed. When on a buffer becomes hidden when it is
@@ -3947,7 +4078,7 @@ return {
{
abbreviation = 'hl',
cb = 'did_set_highlight',
- defaults = { if_true = macros('HIGHLIGHT_INIT', 'string') },
+ defaults = macros('HIGHLIGHT_INIT', 'string'),
deny_duplicates = true,
full_name = 'highlight',
list = 'onecomma',
@@ -3958,11 +4089,11 @@ return {
},
{
abbreviation = 'hi',
- defaults = { if_true = 10000 },
+ defaults = 10000,
desc = [=[
A history of ":" commands, and a history of previous search patterns
is remembered. This option decides how many entries may be stored in
- each of these histories (see |cmdline-editing| and 'msghistory' for
+ each of these histories (see |cmdline-editing| and 'messagesopt' for
the number of messages to remember).
The maximum value is 10000.
]=],
@@ -3974,7 +4105,7 @@ return {
},
{
abbreviation = 'hk',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'hkmap',
scope = { 'global' },
short_desc = N_('No description'),
@@ -3983,7 +4114,7 @@ return {
},
{
abbreviation = 'hkp',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'hkmapp',
scope = { 'global' },
short_desc = N_('No description'),
@@ -3993,7 +4124,7 @@ return {
{
abbreviation = 'hls',
cb = 'did_set_hlsearch',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When there is a previous search pattern, highlight all its matches.
The |hl-Search| highlight group determines the highlighting for all
@@ -4043,7 +4174,7 @@ return {
},
{
cb = 'did_set_iconstring',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is not empty, it will be used for the icon text of
the window. This happens only when the 'icon' option is on.
@@ -4063,7 +4194,7 @@ return {
{
abbreviation = 'ic',
cb = 'did_set_ignorecase',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Ignore case in search patterns, |cmdline-completion|, when
searching in the tags file, and |expr-==|.
@@ -4079,7 +4210,7 @@ return {
},
{
abbreviation = 'imc',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When set the Input Method is always on when starting to edit a command
line, unless entering a search pattern (see 'imsearch' for that).
@@ -4114,7 +4245,7 @@ return {
{
abbreviation = 'imi',
cb = 'did_set_iminsert',
- defaults = { if_true = macros('B_IMODE_NONE', 'number') },
+ defaults = macros('B_IMODE_NONE', 'number'),
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used in
Insert mode. Valid values:
@@ -4139,7 +4270,7 @@ return {
},
{
abbreviation = 'ims',
- defaults = { if_true = macros('B_IMODE_USE_INSERT', 'number') },
+ defaults = macros('B_IMODE_USE_INSERT', 'number'),
desc = [=[
Specifies whether :lmap or an Input Method (IM) is to be used when
entering a search pattern. Valid values:
@@ -4162,7 +4293,8 @@ return {
{
abbreviation = 'icm',
cb = 'did_set_inccommand',
- defaults = { if_true = 'nosplit' },
+ defaults = 'nosplit',
+ values = { 'nosplit', 'split', '' },
desc = [=[
When nonempty, shows the effects of |:substitute|, |:smagic|,
|:snomagic| and user commands with the |:command-preview| flag as you
@@ -4178,7 +4310,6 @@ return {
'redrawtime') then 'inccommand' is automatically disabled until
|Command-line-mode| is done.
]=],
- expand_cb = 'expand_set_inccommand',
full_name = 'inccommand',
scope = { 'global' },
short_desc = N_('Live preview of substitution'),
@@ -4187,7 +4318,7 @@ return {
},
{
abbreviation = 'inc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Pattern to be used to find an include command. It is a search
pattern, just like for the "/" command (See |pattern|). This option
@@ -4209,7 +4340,7 @@ return {
{
abbreviation = 'inex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression to be used to transform the string found with the 'include'
option to a file name. Mostly useful to change "." to "/" for Java: >vim
@@ -4250,7 +4381,7 @@ return {
},
{
abbreviation = 'is',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
While typing a search command, show where the pattern, as it was typed
so far, matches. The matched string is highlighted. If the pattern
@@ -4293,7 +4424,7 @@ return {
{
abbreviation = 'inde',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to obtain the proper indent for a line.
It is used when a new line is created, for the |=| operator and
@@ -4345,7 +4476,7 @@ return {
},
{
abbreviation = 'indk',
- defaults = { if_true = '0{,0},0),0],:,0#,!^F,o,O,e' },
+ defaults = '0{,0},0),0],:,0#,!^F,o,O,e',
deny_duplicates = true,
desc = [=[
A list of keys that, when typed in Insert mode, cause reindenting of
@@ -4362,7 +4493,7 @@ return {
},
{
abbreviation = 'inf',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When doing keyword completion in insert mode |ins-completion|, and
'ignorecase' is also on, the case of the match is adjusted depending
@@ -4381,7 +4512,7 @@ return {
},
{
abbreviation = 'im',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'insertmode',
scope = { 'global' },
short_desc = N_('No description'),
@@ -4487,7 +4618,7 @@ return {
{
abbreviation = 'isk',
cb = 'did_set_iskeyword',
- defaults = { if_true = '@,48-57,_,192-255' },
+ defaults = '@,48-57,_,192-255',
deny_duplicates = true,
desc = [=[
Keywords are used in searching and recognizing with many commands:
@@ -4513,7 +4644,7 @@ return {
{
abbreviation = 'isp',
cb = 'did_set_isopt',
- defaults = { if_true = '@,161-255' },
+ defaults = '@,161-255',
deny_duplicates = true,
desc = [=[
The characters given by this option are displayed directly on the
@@ -4553,7 +4684,7 @@ return {
},
{
abbreviation = 'js',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Insert two spaces after a '.', '?' and '!' with a join command.
Otherwise only one space is inserted.
@@ -4566,8 +4697,9 @@ return {
},
{
abbreviation = 'jop',
- cb = 'did_set_jumpoptions',
- defaults = { if_true = 'clean' },
+ defaults = 'clean',
+ values = { 'stack', 'view', 'clean' },
+ flags = true,
deny_duplicates = true,
desc = [=[
List of words that change the behavior of the |jumplist|.
@@ -4584,18 +4716,18 @@ return {
clean Remove unloaded buffers from the jumplist.
EXPERIMENTAL: this flag may change in the future.
]=],
- expand_cb = 'expand_set_jumpoptions',
full_name = 'jumpoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('Controls the behavior of the jumplist'),
type = 'string',
varname = 'p_jop',
+ flags_varname = 'jop_flags',
},
{
abbreviation = 'kmp',
cb = 'did_set_keymap',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Name of a keyboard mapping. See |mbyte-keymap|.
Setting this option to a valid keymap name has the side effect of
@@ -4615,7 +4747,8 @@ return {
{
abbreviation = 'km',
cb = 'did_set_keymodel',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'startsel', 'stopsel' },
deny_duplicates = true,
desc = [=[
List of comma-separated words, which enable special things that keys
@@ -4627,7 +4760,6 @@ return {
Special keys in this context are the cursor keys, <End>, <Home>,
<PageUp> and <PageDown>.
]=],
- expand_cb = 'expand_set_keymodel',
full_name = 'keymodel',
list = 'onecomma',
scope = { 'global' },
@@ -4668,7 +4800,7 @@ return {
{
abbreviation = 'lmap',
cb = 'did_set_langmap',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
This option allows switching your keyboard into a special language
@@ -4722,7 +4854,7 @@ return {
},
{
abbreviation = 'lm',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Language to use for menu translation. Tells which file is loaded
from the "lang" directory in 'runtimepath': >vim
@@ -4753,7 +4885,7 @@ return {
{
abbreviation = 'lnr',
cb = 'did_set_langnoremap',
- defaults = { if_true = true },
+ defaults = true,
full_name = 'langnoremap',
scope = { 'global' },
short_desc = N_("do not apply 'langmap' to mapped characters"),
@@ -4763,7 +4895,7 @@ return {
{
abbreviation = 'lrm',
cb = 'did_set_langremap',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When off, setting 'langmap' does not apply to characters resulting from
a mapping. If setting 'langmap' disables some of your mappings, make
@@ -4778,7 +4910,7 @@ return {
{
abbreviation = 'ls',
cb = 'did_set_laststatus',
- defaults = { if_true = 2 },
+ defaults = 2,
desc = [=[
The value of this option influences when the last window will have a
status line:
@@ -4798,7 +4930,7 @@ return {
},
{
abbreviation = 'lz',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When this option is set, the screen will not be redrawn while
executing macros, registers and other commands that have not been
@@ -4816,7 +4948,7 @@ return {
},
{
abbreviation = 'lbr',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If on, Vim will wrap long lines at a character in 'breakat' rather
than at the last character that fits on the screen. Unlike
@@ -4862,7 +4994,7 @@ return {
},
{
abbreviation = 'lsp',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
only in the GUI
Number of pixel lines inserted between characters. Useful if the font
@@ -4882,7 +5014,7 @@ return {
},
{
cb = 'did_set_lisp',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Lisp mode: When <Enter> is typed in insert mode set the indent for
the next line to Lisp standards (well, sort of). Also happens with
@@ -4902,7 +5034,8 @@ return {
{
abbreviation = 'lop',
cb = 'did_set_lispoptions',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'expr:0', 'expr:1' },
deny_duplicates = true,
desc = [=[
Comma-separated list of items that influence the Lisp indenting when
@@ -4913,7 +5046,6 @@ return {
Note that when using 'indentexpr' the `=` operator indents all the
lines, otherwise the first line is not indented (Vi-compatible).
]=],
- expand_cb = 'expand_set_lispoptions',
full_name = 'lispoptions',
list = 'onecomma',
scope = { 'buf' },
@@ -4940,7 +5072,7 @@ return {
varname = 'p_lispwords',
},
{
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
List mode: By default, show tabs as ">", trailing spaces as "-", and
non-breakable space characters as "+". Useful to see the difference
@@ -4968,7 +5100,7 @@ return {
{
abbreviation = 'lcs',
cb = 'did_set_chars_option',
- defaults = { if_true = 'tab:> ,trail:-,nbsp:+' },
+ defaults = 'tab:> ,trail:-,nbsp:+',
deny_duplicates = true,
desc = [=[
Strings to use in 'list' mode and for the |:list| command. It is a
@@ -5079,7 +5211,7 @@ return {
},
{
abbreviation = 'lpl',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on the plugin scripts are loaded when starting up |load-plugins|.
This option can be reset in your |vimrc| file to disable the loading
@@ -5094,7 +5226,7 @@ return {
varname = 'p_lpl',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Changes the special characters that can be used in search patterns.
See |pattern|.
@@ -5112,7 +5244,7 @@ return {
},
{
abbreviation = 'mef',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Name of the errorfile for the |:make| command (see |:make_makeprg|)
and the |:grep| command.
@@ -5137,7 +5269,7 @@ return {
{
abbreviation = 'menc',
cb = 'did_set_encoding',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Encoding used for reading the output of external commands. When empty,
encoding is not converted.
@@ -5160,7 +5292,7 @@ return {
},
{
abbreviation = 'mp',
- defaults = { if_true = 'make' },
+ defaults = 'make',
desc = [=[
Program to use for the ":make" command. See |:make_makeprg|.
This option may contain '%' and '#' characters (see |:_%| and |:_#|),
@@ -5189,7 +5321,7 @@ return {
{
abbreviation = 'mps',
cb = 'did_set_matchpairs',
- defaults = { if_true = '(:),{:},[:]' },
+ defaults = '(:),{:},[:]',
deny_duplicates = true,
desc = [=[
Characters that form pairs. The |%| command jumps from one to the
@@ -5217,7 +5349,7 @@ return {
},
{
abbreviation = 'mat',
- defaults = { if_true = 5 },
+ defaults = 5,
desc = [=[
Tenths of a second to show the matching paren, when 'showmatch' is
set. Note that this is not in milliseconds, like other options that
@@ -5231,7 +5363,7 @@ return {
},
{
abbreviation = 'mco',
- defaults = { if_true = macros('MAX_MCO', 'number') },
+ defaults = macros('MAX_MCO', 'number'),
full_name = 'maxcombine',
scope = { 'global' },
short_desc = N_('maximum nr of combining characters displayed'),
@@ -5240,7 +5372,7 @@ return {
},
{
abbreviation = 'mfd',
- defaults = { if_true = 100 },
+ defaults = 100,
desc = [=[
Maximum depth of function calls for user functions. This normally
catches endless recursion. When using a recursive function with
@@ -5259,7 +5391,7 @@ return {
},
{
abbreviation = 'mmd',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Maximum number of times a mapping is done without resulting in a
character to be used. This normally catches endless mappings, like
@@ -5276,7 +5408,7 @@ return {
},
{
abbreviation = 'mmp',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Maximum amount of memory (in Kbyte) to use for pattern matching.
The maximum value is about 2000000. Use this to work without a limit.
@@ -5299,7 +5431,7 @@ return {
},
{
abbreviation = 'mis',
- defaults = { if_true = 25 },
+ defaults = 25,
desc = [=[
Maximum number of items to use in a menu. Used for menus that are
generated from a list of items, e.g., the Buffers menu. Changing this
@@ -5312,9 +5444,43 @@ return {
varname = 'p_mis',
},
{
+ abbreviation = 'mopt',
+ cb = 'did_set_messagesopt',
+ defaults = 'hit-enter,history:500',
+ values = { 'hit-enter', 'wait:', 'history:' },
+ flags = true,
+ deny_duplicates = true,
+ desc = [=[
+ Option settings for outputting messages. It can consist of the
+ following items. Items must be separated by a comma.
+
+ hit-enter Use a |hit-enter| prompt when the message is longer than
+ 'cmdheight' size.
+
+ wait:{n} Instead of using a |hit-enter| prompt, simply wait for
+ {n} milliseconds so that the user has a chance to read
+ the message. The maximum value of {n} is 10000. Use
+ 0 to disable the wait (but then the user may miss an
+ important message).
+ This item is ignored when "hit-enter" is present, but
+ required when "hit-enter" is not present.
+
+ history:{n} Determines how many entries are remembered in the
+ |:messages| history. The maximum value is 10000.
+ Setting it to zero clears the message history.
+ This item must always be present.
+ ]=],
+ full_name = 'messagesopt',
+ list = 'onecommacolon',
+ scope = { 'global' },
+ short_desc = N_('options for outputting messages'),
+ type = 'string',
+ varname = 'p_mopt',
+ },
+ {
abbreviation = 'msm',
cb = 'did_set_mkspellmem',
- defaults = { if_true = '460000,2000,500' },
+ defaults = '460000,2000,500',
desc = [=[
Parameters for |:mkspell|. This tunes when to start compressing the
word tree. Compression can be slow when there are many words, but
@@ -5379,7 +5545,7 @@ return {
},
{
abbreviation = 'mle',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on allow some options that are an expression to be set in the
modeline. Check the option for whether it is affected by
@@ -5396,7 +5562,7 @@ return {
},
{
abbreviation = 'mls',
- defaults = { if_true = 5 },
+ defaults = 5,
desc = [=[
If 'modeline' is on 'modelines' gives the number of lines that is
checked for set commands. If 'modeline' is off or 'modelines' is zero
@@ -5412,7 +5578,7 @@ return {
{
abbreviation = 'ma',
cb = 'did_set_modifiable',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When off the buffer contents cannot be changed. The 'fileformat' and
'fileencoding' options also can't be changed.
@@ -5429,7 +5595,7 @@ return {
{
abbreviation = 'mod',
cb = 'did_set_modified',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, the buffer is considered to be modified. This option is set
when:
@@ -5462,7 +5628,7 @@ return {
varname = 'p_mod',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, listings pause when the whole screen is filled. You will get
the |more-prompt|. When this option is off there are no pauses, the
@@ -5476,7 +5642,7 @@ return {
},
{
cb = 'did_set_mouse',
- defaults = { if_true = 'nvi' },
+ defaults = 'nvi',
desc = [=[
Enables mouse support. For example, to enable the mouse in Normal mode
and Visual mode: >vim
@@ -5525,7 +5691,7 @@ return {
},
{
abbreviation = 'mousef',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
The window that the mouse pointer is on is automatically activated.
When changing the window layout or window focus in another way, the
@@ -5542,7 +5708,7 @@ return {
},
{
abbreviation = 'mh',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
only in the GUI
When on, the mouse pointer is hidden when characters are typed.
@@ -5557,8 +5723,8 @@ return {
},
{
abbreviation = 'mousem',
- cb = 'did_set_mousemodel',
- defaults = { if_true = 'popup_setpos' },
+ defaults = 'popup_setpos',
+ values = { 'extend', 'popup', 'popup_setpos' },
desc = [=[
Sets the model to use for the mouse. The name mostly specifies what
the right mouse button is used for:
@@ -5608,7 +5774,6 @@ return {
"g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click)
"g<RightMouse>" is "<C-RightMouse> ("CTRL-T")
]=],
- expand_cb = 'expand_set_mousemodel',
full_name = 'mousemodel',
scope = { 'global' },
short_desc = N_('changes meaning of mouse buttons'),
@@ -5617,7 +5782,7 @@ return {
},
{
abbreviation = 'mousemev',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, mouse move events are delivered to the input queue and are
available for mapping. The default, off, avoids the mouse movement
@@ -5634,7 +5799,8 @@ return {
},
{
cb = 'did_set_mousescroll',
- defaults = { if_true = 'ver:3,hor:6' },
+ defaults = 'ver:3,hor:6',
+ values = { 'hor:', 'ver:' },
desc = [=[
This option controls the number of lines / columns to scroll by when
scrolling with a mouse wheel (|scroll-mouse-wheel|). The option is
@@ -5654,7 +5820,6 @@ return {
< Will make Nvim scroll 5 lines at a time when scrolling vertically, and
scroll 2 columns at a time when scrolling horizontally.
]=],
- expand_cb = 'expand_set_mousescroll',
full_name = 'mousescroll',
list = 'comma',
scope = { 'global' },
@@ -5742,7 +5907,7 @@ return {
},
{
abbreviation = 'mouset',
- defaults = { if_true = 500 },
+ defaults = 500,
desc = [=[
Defines the maximum time in msec between two mouse clicks for the
second click to be recognized as a multi click.
@@ -5754,22 +5919,9 @@ return {
varname = 'p_mouset',
},
{
- abbreviation = 'mhi',
- defaults = { if_true = 500 },
- desc = [=[
- Determines how many entries are remembered in the |:messages| history.
- The maximum value is 10000.
- ]=],
- full_name = 'msghistory',
- scope = { 'global' },
- short_desc = N_('how many messages are remembered'),
- type = 'number',
- varname = 'p_mhi',
- },
- {
abbreviation = 'nf',
- cb = 'did_set_nrformats',
- defaults = { if_true = 'bin,hex' },
+ defaults = 'bin,hex',
+ values = { 'bin', 'octal', 'hex', 'alpha', 'unsigned', 'blank' },
deny_duplicates = true,
desc = [=[
This defines what bases Vim will consider for numbers when using the
@@ -5813,7 +5965,6 @@ return {
considered decimal. This also happens for numbers that are not
recognized as octal or hex.
]=],
- expand_cb = 'expand_set_nrformats',
full_name = 'nrformats',
list = 'onecomma',
scope = { 'buf' },
@@ -5824,7 +5975,7 @@ return {
{
abbreviation = 'nu',
cb = 'did_set_number_relativenumber',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Print the line number in front of each line. When the 'n' option is
excluded from 'cpoptions' a wrapped line will not use the column of
@@ -5857,7 +6008,7 @@ return {
{
abbreviation = 'nuw',
cb = 'did_set_numberwidth',
- defaults = { if_true = 4 },
+ defaults = 4,
desc = [=[
Minimal number of columns to use for the line number. Only relevant
when the 'number' or 'relativenumber' option is set or printing lines
@@ -5879,7 +6030,7 @@ return {
{
abbreviation = 'ofu',
cb = 'did_set_omnifunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used for Insert mode omni
completion with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O|
@@ -5902,7 +6053,7 @@ return {
},
{
abbreviation = 'odev',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
only for Windows
Enable reading and writing from devices. This may get Vim stuck on a
@@ -5920,7 +6071,7 @@ return {
{
abbreviation = 'opfunc',
cb = 'did_set_operatorfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be called by the |g@| operator.
See |:map-operator| for more info and an example. The value can be
@@ -5964,7 +6115,7 @@ return {
},
{
abbreviation = 'para',
- defaults = { if_true = 'IPLPPPQPP TPHPLIPpLpItpplpipbp' },
+ defaults = 'IPLPPPQPP TPHPLIPpLpItpplpipbp',
desc = [=[
Specifies the nroff macros that separate paragraphs. These are pairs
of two letters (see |object-motions|).
@@ -5977,7 +6128,7 @@ return {
},
{
cb = 'did_set_paste',
- defaults = { if_true = false },
+ defaults = false,
full_name = 'paste',
pri_mkrc = true,
scope = { 'global' },
@@ -5987,7 +6138,7 @@ return {
},
{
abbreviation = 'pt',
- defaults = { if_true = '' },
+ defaults = '',
full_name = 'pastetoggle',
scope = { 'global' },
short_desc = N_('No description'),
@@ -5997,7 +6148,7 @@ return {
{
abbreviation = 'pex',
cb = 'did_set_optexpr',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Expression which is evaluated to apply a patch to a file and generate
the resulting new version of the file. See |diff-patchexpr|.
@@ -6014,7 +6165,7 @@ return {
{
abbreviation = 'pm',
cb = 'did_set_backupext_or_patchmode',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty the oldest version of a file is kept. This can be used
to keep the original version of a file if you are changing files in a
@@ -6043,7 +6194,7 @@ return {
},
{
abbreviation = 'pa',
- defaults = { if_true = '.,,' },
+ defaults = '.,,',
deny_duplicates = true,
desc = [=[
This is a list of directories which will be searched when using the
@@ -6105,7 +6256,7 @@ return {
},
{
abbreviation = 'pi',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When changing the indent of the current line, preserve as much of the
indent structure as possible. Normally the indent is replaced by a
@@ -6128,7 +6279,7 @@ return {
},
{
abbreviation = 'pvh',
- defaults = { if_true = 12 },
+ defaults = 12,
desc = [=[
Default height for a preview window. Used for |:ptag| and associated
commands. Used for |CTRL-W_}| when no count is given.
@@ -6142,7 +6293,7 @@ return {
{
abbreviation = 'pvw',
cb = 'did_set_previewwindow',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Identifies the preview window. Only one window can have this option
set. It's normally not set directly, but by using one of the commands
@@ -6157,7 +6308,7 @@ return {
type = 'boolean',
},
{
- defaults = { if_true = true },
+ defaults = true,
full_name = 'prompt',
scope = { 'global' },
short_desc = N_('enable prompt in Ex mode'),
@@ -6167,7 +6318,7 @@ return {
{
abbreviation = 'pb',
cb = 'did_set_pumblend',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Enables pseudo-transparency for the |popup-menu|. Valid values are in
the range of 0 for fully opaque popupmenu (disabled) to 100 for fully
@@ -6191,7 +6342,7 @@ return {
},
{
abbreviation = 'ph',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Maximum number of items to show in the popup menu
(|ins-completion-menu|). Zero means "use available screen space".
@@ -6204,7 +6355,7 @@ return {
},
{
abbreviation = 'pw',
- defaults = { if_true = 15 },
+ defaults = 15,
desc = [=[
Minimum width for the popup menu (|ins-completion-menu|). If the
cursor column + 'pumwidth' exceeds screen width, the popup menu is
@@ -6218,7 +6369,7 @@ return {
},
{
abbreviation = 'pyx',
- defaults = { if_true = 3 },
+ defaults = 3,
desc = [=[
Specifies the python version used for pyx* functions and commands
|python_x|. As only Python 3 is supported, this always has the value
@@ -6237,7 +6388,7 @@ return {
{
abbreviation = 'qftf',
cb = 'did_set_quickfixtextfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used to get the text to display
in the quickfix and location list windows. This can be used to
@@ -6261,7 +6412,7 @@ return {
},
{
abbreviation = 'qe',
- defaults = { if_true = '\\' },
+ defaults = '\\',
desc = [=[
The characters that are used to escape quotes in a string. Used for
objects like a', a" and a` |a'|.
@@ -6278,7 +6429,7 @@ return {
{
abbreviation = 'ro',
cb = 'did_set_readonly',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If on, writes fail unless you use a '!'. Protects you from
accidentally overwriting a file. Default on when Vim is started
@@ -6299,8 +6450,16 @@ return {
},
{
abbreviation = 'rdb',
- cb = 'did_set_redrawdebug',
- defaults = { if_true = '' },
+ defaults = '',
+ values = {
+ 'compositor',
+ 'nothrottle',
+ 'invalid',
+ 'nodelta',
+ 'line',
+ 'flush',
+ },
+ flags = true,
desc = [=[
Flags to change the way redrawing works, for debugging purposes.
Most useful with 'writedelay' set to some reasonable value.
@@ -6339,14 +6498,15 @@ return {
short_desc = N_('Changes the way redrawing works (debug)'),
type = 'string',
varname = 'p_rdb',
+ flags_varname = 'rdb_flags',
},
{
abbreviation = 'rdt',
- defaults = { if_true = 2000 },
+ defaults = 2000,
desc = [=[
Time in milliseconds for redrawing the display. Applies to
- 'hlsearch', 'inccommand', |:match| highlighting and syntax
- highlighting.
+ 'hlsearch', 'inccommand', |:match| highlighting, syntax highlighting,
+ and async |LanguageTree:parse()|.
When redrawing takes more than this many milliseconds no further
matches will be highlighted.
For syntax highlighting the time applies per window. When over the
@@ -6362,7 +6522,7 @@ return {
},
{
abbreviation = 're',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
This selects the default regexp engine. |two-engines|
The possible values are:
@@ -6386,7 +6546,7 @@ return {
{
abbreviation = 'rnu',
cb = 'did_set_number_relativenumber',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Show the line number relative to the line with the cursor in front of
each line. Relative line numbers help you use the |count| you can
@@ -6413,7 +6573,7 @@ return {
type = 'boolean',
},
{
- defaults = { if_true = true },
+ defaults = true,
full_name = 'remap',
scope = { 'global' },
short_desc = N_('No description'),
@@ -6421,7 +6581,7 @@ return {
immutable = true,
},
{
- defaults = { if_true = 2 },
+ defaults = 2,
desc = [=[
Threshold for reporting number of lines changed. When the number of
changed lines is more than 'report' a message will be given for most
@@ -6437,7 +6597,7 @@ return {
},
{
abbreviation = 'ri',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Inserting characters in Insert mode will work backwards. See "typing
backwards" |ins-reverse|. This option can be toggled with the CTRL-_
@@ -6451,7 +6611,7 @@ return {
},
{
abbreviation = 'rl',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, display orientation becomes right-to-left, i.e., characters
that are stored in the file appear from the right to the left.
@@ -6471,8 +6631,8 @@ return {
},
{
abbreviation = 'rlc',
- cb = 'did_set_rightleftcmd',
- defaults = { if_true = 'search' },
+ defaults = 'search',
+ values = { 'search' },
desc = [=[
Each word in this option enables the command line editing to work in
right-to-left mode for a group of commands:
@@ -6482,8 +6642,8 @@ return {
This is useful for languages such as Hebrew, Arabic and Farsi.
The 'rightleft' option must be set for 'rightleftcmd' to take effect.
]=],
- expand_cb = 'expand_set_rightleftcmd',
full_name = 'rightleftcmd',
+ list = 'comma',
redraw = { 'current_window' },
scope = { 'win' },
short_desc = N_('commands for which editing works right-to-left'),
@@ -6491,7 +6651,7 @@ return {
},
{
abbreviation = 'ru',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Show the line and column number of the cursor position, separated by a
comma. When there is room, the relative position of the displayed
@@ -6526,7 +6686,7 @@ return {
{
abbreviation = 'ruf',
cb = 'did_set_rulerformat',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is not empty, it determines the content of the ruler
string, as displayed for the 'ruler' option.
@@ -6583,6 +6743,7 @@ return {
indent/ indent scripts |indent-expression|
keymap/ key mapping files |mbyte-keymap|
lang/ menu translations |:menutrans|
+ lsp/ LSP client configurations |lsp-config|
lua/ |Lua| plugins
menu.vim GUI menus |menu.vim|
pack/ packages |:packadd|
@@ -6697,7 +6858,7 @@ return {
{
abbreviation = 'scb',
cb = 'did_set_scrollbind',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
See also |scroll-binding|. When this option is set, scrolling the
current window also scrolls other scrollbind windows (windows that
@@ -6716,7 +6877,7 @@ return {
},
{
abbreviation = 'sj',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Minimal number of lines to scroll when the cursor gets off the
screen (e.g., with "j"). Not used for scroll commands (e.g., CTRL-E,
@@ -6733,7 +6894,7 @@ return {
},
{
abbreviation = 'so',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Minimal number of screen lines to keep above and below the cursor.
This will make some context visible around where you are working. If
@@ -6754,8 +6915,8 @@ return {
},
{
abbreviation = 'sbo',
- cb = 'did_set_scrollopt',
- defaults = { if_true = 'ver,jump' },
+ defaults = 'ver,jump',
+ values = { 'ver', 'hor', 'jump' },
deny_duplicates = true,
desc = [=[
This is a comma-separated list of words that specifies how
@@ -6786,7 +6947,6 @@ return {
When 'diff' mode is active there always is vertical scroll binding,
even when "ver" isn't there.
]=],
- expand_cb = 'expand_set_scrollopt',
full_name = 'scrollopt',
list = 'onecomma',
scope = { 'global' },
@@ -6796,7 +6956,7 @@ return {
},
{
abbreviation = 'sect',
- defaults = { if_true = 'SHNHH HUnhsh' },
+ defaults = 'SHNHH HUnhsh',
desc = [=[
Specifies the nroff macros that separate sections. These are pairs of
two letters (See |object-motions|). The default makes a section start
@@ -6809,7 +6969,7 @@ return {
varname = 'p_sections',
},
{
- defaults = { if_true = false },
+ defaults = false,
full_name = 'secure',
scope = { 'global' },
secure = true,
@@ -6820,7 +6980,8 @@ return {
{
abbreviation = 'sel',
cb = 'did_set_selection',
- defaults = { if_true = 'inclusive' },
+ defaults = 'inclusive',
+ values = { 'inclusive', 'exclusive', 'old' },
desc = [=[
This option defines the behavior of the selection. It is only used
in Visual and Select mode.
@@ -6836,11 +6997,12 @@ return {
selection.
When "old" is used and 'virtualedit' allows the cursor to move past
the end of line the line break still isn't included.
+ When "exclusive" is used, cursor position in visual mode will be
+ adjusted for inclusive motions |inclusive-motion-selection-exclusive|.
Note that when "exclusive" is used and selecting from the end
backwards, you cannot include the last character of a line, when
starting in Normal mode and 'virtualedit' empty.
]=],
- expand_cb = 'expand_set_selection',
full_name = 'selection',
scope = { 'global' },
short_desc = N_('what type of selection to use'),
@@ -6849,8 +7011,8 @@ return {
},
{
abbreviation = 'slm',
- cb = 'did_set_selectmode',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'mouse', 'key', 'cmd' },
deny_duplicates = true,
desc = [=[
This is a comma-separated list of words, which specifies when to start
@@ -6861,7 +7023,6 @@ return {
cmd when using "v", "V" or CTRL-V
See |Select-mode|.
]=],
- expand_cb = 'expand_set_selectmode',
full_name = 'selectmode',
list = 'onecomma',
scope = { 'global' },
@@ -6872,7 +7033,29 @@ return {
{
abbreviation = 'ssop',
cb = 'did_set_sessionoptions',
- defaults = { if_true = 'blank,buffers,curdir,folds,help,tabpages,winsize,terminal' },
+ defaults = 'blank,buffers,curdir,folds,help,tabpages,winsize,terminal',
+ -- Also used for 'viewoptions'.
+ values = {
+ 'buffers',
+ 'winpos',
+ 'resize',
+ 'winsize',
+ 'localoptions',
+ 'options',
+ 'help',
+ 'blank',
+ 'globals',
+ 'slash',
+ 'unix',
+ 'sesdir',
+ 'curdir',
+ 'folds',
+ 'cursor',
+ 'tabpages',
+ 'terminal',
+ 'skiprtp',
+ },
+ flags = true,
deny_duplicates = true,
desc = [=[
Changes the effect of the |:mksession| command. It is a comma-
@@ -6913,13 +7096,13 @@ return {
If you leave out "options" many things won't work well after restoring
the session.
]=],
- expand_cb = 'expand_set_sessionoptions',
full_name = 'sessionoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('options for |:mksession|'),
type = 'string',
varname = 'p_ssop',
+ flags_varname = 'ssop_flags',
},
{
abbreviation = 'sd',
@@ -7056,7 +7239,7 @@ return {
{
abbreviation = 'sdf',
alias = { 'vif', 'viminfofile' },
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
When non-empty, overrides the file name used for |shada| (viminfo).
@@ -7310,7 +7493,7 @@ return {
},
{
abbreviation = 'stmp',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, use temp files for shell commands. When off use a pipe.
When using a pipe is not possible temp files are used anyway.
@@ -7331,7 +7514,7 @@ return {
},
{
abbreviation = 'sxe',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When 'shellxquote' is set to "(" then the characters listed in this
option will be escaped with a '^' character. This makes it possible
@@ -7374,7 +7557,7 @@ return {
},
{
abbreviation = 'sr',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Round indent to multiple of 'shiftwidth'. Applies to > and <
commands. CTRL-T and CTRL-D in Insert mode always round the indent to
@@ -7389,7 +7572,7 @@ return {
{
abbreviation = 'sw',
cb = 'did_set_shiftwidth_tabstop',
- defaults = { if_true = 8 },
+ defaults = 8,
desc = [=[
Number of spaces to use for each step of (auto)indent. Used for
|'cindent'|, |>>|, |<<|, etc.
@@ -7405,7 +7588,7 @@ return {
{
abbreviation = 'shm',
cb = 'did_set_shortmess',
- defaults = { if_true = 'ltToOCF' },
+ defaults = 'ltToOCF',
desc = [=[
This option helps to avoid all the |hit-enter| prompts caused by file
messages, for example with CTRL-G, and to avoid some other messages.
@@ -7475,7 +7658,7 @@ return {
{
abbreviation = 'sbr',
cb = 'did_set_showbreak',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
String to put at the start of lines that have been wrapped. Useful
values are "> " or "+++ ": >vim
@@ -7503,7 +7686,7 @@ return {
},
{
abbreviation = 'sc',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Show (partial) command in the last line of the screen. Set this
option off if your terminal is slow.
@@ -7526,7 +7709,8 @@ return {
{
abbreviation = 'sloc',
cb = 'did_set_showcmdloc',
- defaults = { if_true = 'last' },
+ defaults = 'last',
+ values = { 'last', 'statusline', 'tabline' },
desc = [=[
This option can be used to display the (partially) entered command in
another location. Possible values are:
@@ -7540,7 +7724,6 @@ return {
place the text. Without a custom 'statusline' or 'tabline' it will be
displayed in a convenient location.
]=],
- expand_cb = 'expand_set_showcmdloc',
full_name = 'showcmdloc',
scope = { 'global' },
short_desc = N_('change location of partial command'),
@@ -7549,7 +7732,7 @@ return {
},
{
abbreviation = 'sft',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When completing a word in insert mode (see |ins-completion|) from the
tags file, show both the tag name and a tidied-up form of the search
@@ -7568,7 +7751,7 @@ return {
},
{
abbreviation = 'sm',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When a bracket is inserted, briefly jump to the matching one. The
jump is only done if the match can be seen on the screen. The time to
@@ -7594,7 +7777,7 @@ return {
},
{
abbreviation = 'smd',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
If in Insert, Replace or Visual mode put a message on the last line.
The |hl-ModeMsg| highlight group determines the highlighting.
@@ -7609,7 +7792,7 @@ return {
{
abbreviation = 'stal',
cb = 'did_set_showtabline',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The value of this option specifies when the line with tab page labels
will be displayed:
@@ -7629,7 +7812,7 @@ return {
},
{
abbreviation = 'ss',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The minimal number of columns to scroll horizontally. Used only when
the 'wrap' option is off and the cursor is moved off of the screen.
@@ -7645,7 +7828,7 @@ return {
},
{
abbreviation = 'siso',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
The minimal number of screen columns to keep to the left and to the
right of the cursor if 'nowrap' is set. Setting this option to a
@@ -7677,7 +7860,31 @@ return {
{
abbreviation = 'scl',
cb = 'did_set_signcolumn',
- defaults = { if_true = 'auto' },
+ defaults = 'auto',
+ 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',
+ },
desc = [=[
When and how to draw the signcolumn. Valid values are:
"auto" only when there is a sign to display
@@ -7696,7 +7903,6 @@ return {
"number" display signs in the 'number' column. If the number
column is not present, then behaves like "auto".
]=],
- expand_cb = 'expand_set_signcolumn',
full_name = 'signcolumn',
redraw = { 'current_window' },
scope = { 'win' },
@@ -7705,7 +7911,7 @@ return {
},
{
abbreviation = 'scs',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Override the 'ignorecase' option if the search pattern contains upper
case characters. Only used when the search pattern is typed and
@@ -7722,7 +7928,7 @@ return {
},
{
abbreviation = 'si',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Do smart autoindenting when starting a new line. Works for C-like
programs, but can also be used for other languages. 'cindent' does
@@ -7752,7 +7958,7 @@ return {
},
{
abbreviation = 'sta',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, a <Tab> in front of a line inserts blanks according to
'shiftwidth'. 'tabstop' or 'softtabstop' is used in other places. A
@@ -7774,7 +7980,7 @@ return {
{
abbreviation = 'sms',
cb = 'did_set_smoothscroll',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Scrolling works with screen lines. When 'wrap' is set and the first
line in the window wraps part of it may not be visible, as if it is
@@ -7792,7 +7998,7 @@ return {
},
{
abbreviation = 'sts',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Number of spaces that a <Tab> counts for while performing editing
operations, like inserting a <Tab> or using <BS>. It "feels" like
@@ -7818,7 +8024,7 @@ return {
},
{
cb = 'did_set_spell',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on spell checking will be done. See |spell|.
The languages are specified with 'spelllang'.
@@ -7832,7 +8038,7 @@ return {
{
abbreviation = 'spc',
cb = 'did_set_spellcapcheck',
- defaults = { if_true = '[.?!]\\_[\\])\'"\\t ]\\+' },
+ defaults = '[.?!]\\_[\\])\'"\\t ]\\+',
desc = [=[
Pattern to locate the end of a sentence. The following word will be
checked to start with a capital letter. If not then it is highlighted
@@ -7854,7 +8060,7 @@ return {
{
abbreviation = 'spf',
cb = 'did_set_spellfile',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Name of the word list file where words are added for the |zg| and |zw|
@@ -7891,7 +8097,7 @@ return {
{
abbreviation = 'spl',
cb = 'did_set_spelllang',
- defaults = { if_true = 'en' },
+ defaults = 'en',
deny_duplicates = true,
desc = [=[
A comma-separated list of word list names. When the 'spell' option is
@@ -7943,7 +8149,9 @@ return {
{
abbreviation = 'spo',
cb = 'did_set_spelloptions',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'camel', 'noplainbuffer' },
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of options for spell checking:
@@ -7956,7 +8164,6 @@ return {
designated regions of the buffer are spellchecked in
this case.
]=],
- expand_cb = 'expand_set_spelloptions',
full_name = 'spelloptions',
list = 'onecomma',
redraw = { 'current_buffer', 'highlight_only' },
@@ -7968,7 +8175,9 @@ return {
{
abbreviation = 'sps',
cb = 'did_set_spellsuggest',
- defaults = { if_true = 'best' },
+ defaults = 'best',
+ -- Keep this in sync with spell_check_sps().
+ values = { 'best', 'fast', 'double', 'expr:', 'file:', 'timeout:' },
deny_duplicates = true,
desc = [=[
Methods used for spelling suggestions. Both for the |z=| command and
@@ -8038,7 +8247,6 @@ return {
security reasons.
]=],
expand = true,
- expand_cb = 'expand_set_spellsuggest',
full_name = 'spellsuggest',
list = 'onecomma',
scope = { 'global' },
@@ -8049,7 +8257,7 @@ return {
},
{
abbreviation = 'sb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, splitting a window will put the new window below the current
one. |:split|
@@ -8062,8 +8270,8 @@ return {
},
{
abbreviation = 'spk',
- cb = 'did_set_splitkeep',
- defaults = { if_true = 'cursor' },
+ defaults = 'cursor',
+ values = { 'cursor', 'screen', 'topline' },
desc = [=[
The value of this option determines the scroll behavior when opening,
closing or resizing horizontal splits.
@@ -8078,7 +8286,6 @@ return {
with the previous cursor position. For "screen", the text cannot always
be kept on the same screen line when 'wrap' is enabled.
]=],
- expand_cb = 'expand_set_splitkeep',
full_name = 'splitkeep',
scope = { 'global' },
short_desc = N_('determines scroll behavior for split windows'),
@@ -8087,7 +8294,7 @@ return {
},
{
abbreviation = 'spr',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, splitting a window will put the new window right of the
current one. |:vsplit|
@@ -8100,7 +8307,7 @@ return {
},
{
abbreviation = 'sol',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When "on" the commands listed below move the cursor to the first
non-blank of the line. When off the cursor is kept in the same column
@@ -8124,7 +8331,7 @@ return {
{
abbreviation = 'stc',
cb = 'did_set_statuscolumn',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option determines the content of the area to the
side of a window, normally containing the fold, sign and number columns.
@@ -8189,7 +8396,7 @@ return {
{
abbreviation = 'stl',
cb = 'did_set_statusline',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option determines the content of the status line.
Also see |status-line|.
@@ -8200,6 +8407,7 @@ return {
All fields except the {item} are optional. A single percent sign can
be given as "%%".
+ *stl-%!*
When the option starts with "%!" then it is used as an expression,
evaluated and the result is used as the option value. Example: >vim
set statusline=%!MyStatusLine()
@@ -8413,7 +8621,7 @@ return {
},
{
abbreviation = 'su',
- defaults = { if_true = '.bak,~,.o,.h,.info,.swp,.obj' },
+ defaults = '.bak,~,.o,.h,.info,.swp,.obj',
deny_duplicates = true,
desc = [=[
Files with these suffixes get a lower priority when multiple files
@@ -8436,7 +8644,7 @@ return {
},
{
abbreviation = 'sua',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Comma-separated list of suffixes, which are used when searching for a
@@ -8454,7 +8662,7 @@ return {
{
abbreviation = 'swf',
cb = 'did_set_swapfile',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Use a swapfile for the buffer. This option can be reset when a
swapfile is not wanted for a specific buffer. For example, with
@@ -8484,8 +8692,9 @@ return {
},
{
abbreviation = 'swb',
- cb = 'did_set_switchbuf',
- defaults = { if_true = 'uselast' },
+ defaults = 'uselast',
+ values = { 'useopen', 'usetab', 'split', 'newtab', 'vsplit', 'uselast' },
+ flags = true,
deny_duplicates = true,
desc = [=[
This option controls the behavior when switching between buffers.
@@ -8516,17 +8725,17 @@ return {
If a window has 'winfixbuf' enabled, 'switchbuf' is currently not
applied to the split window.
]=],
- expand_cb = 'expand_set_switchbuf',
full_name = 'switchbuf',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('sets behavior when switching to another buffer'),
type = 'string',
varname = 'p_swb',
+ flags_varname = 'swb_flags',
},
{
abbreviation = 'smc',
- defaults = { if_true = 3000 },
+ defaults = 3000,
desc = [=[
Maximum column in which to search for syntax items. In long lines the
text after this column is not highlighted and following lines may not
@@ -8545,7 +8754,7 @@ return {
{
abbreviation = 'syn',
cb = 'did_set_filetype_or_syntax',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is set, the syntax with this name is loaded, unless
syntax highlighting has been switched off with ":syntax off".
@@ -8581,8 +8790,9 @@ return {
},
{
abbreviation = 'tcl',
- cb = 'did_set_tabclose',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'left', 'uselast' },
+ flags = true,
deny_duplicates = true,
desc = [=[
This option controls the behavior when closing tab pages (e.g., using
@@ -8595,18 +8805,18 @@ return {
possible. This option takes precedence over the
others.
]=],
- expand_cb = 'expand_set_tabclose',
full_name = 'tabclose',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('which tab page to focus when closing a tab'),
type = 'string',
varname = 'p_tcl',
+ flags_varname = 'tcl_flags',
},
{
abbreviation = 'tal',
cb = 'did_set_tabline',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option determines the content of the tab pages
line at the top of the Vim window. When empty Vim will use a default
@@ -8639,7 +8849,7 @@ return {
},
{
abbreviation = 'tpm',
- defaults = { if_true = 50 },
+ defaults = 50,
desc = [=[
Maximum number of tab pages to be opened by the |-p| command line
argument or the ":tab all" command. |tabpage|
@@ -8653,7 +8863,7 @@ return {
{
abbreviation = 'ts',
cb = 'did_set_shiftwidth_tabstop',
- defaults = { if_true = 8 },
+ defaults = 8,
desc = [=[
Number of spaces that a <Tab> in the file counts for. Also see
the |:retab| command, and the 'softtabstop' option.
@@ -8704,7 +8914,7 @@ return {
},
{
abbreviation = 'tbs',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When searching for a tag (e.g., for the |:ta| command), Vim can either
use a binary search or a linear search in a tags file. Binary
@@ -8764,7 +8974,9 @@ return {
{
abbreviation = 'tc',
cb = 'did_set_tagcase',
- defaults = { if_true = 'followic' },
+ defaults = 'followic',
+ values = { 'followic', 'ignore', 'match', 'followscs', 'smart' },
+ flags = true,
desc = [=[
This option specifies how case is handled when searching the tags
file:
@@ -8774,17 +8986,17 @@ return {
match Match case
smart Ignore case unless an upper case letter is used
]=],
- expand_cb = 'expand_set_tagcase',
full_name = 'tagcase',
scope = { 'global', 'buf' },
short_desc = N_('how to handle case when searching in tags files'),
type = 'string',
varname = 'p_tc',
+ flags_varname = 'tc_flags',
},
{
abbreviation = 'tfu',
cb = 'did_set_tagfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used to perform tag searches.
The function gets the tag pattern and should return a List of matching
@@ -8805,7 +9017,7 @@ return {
},
{
abbreviation = 'tl',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
If non-zero, tags are significant up to this number of characters.
]=],
@@ -8817,7 +9029,7 @@ return {
},
{
abbreviation = 'tr',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
If on and using a tags file in another directory, file names in that
tags file are relative to the directory where the tags file is.
@@ -8830,7 +9042,7 @@ return {
},
{
abbreviation = 'tag',
- defaults = { if_true = './tags;,tags' },
+ defaults = './tags;,tags',
deny_duplicates = true,
desc = [=[
Filenames for the tag command, separated by spaces or commas. To
@@ -8862,7 +9074,7 @@ return {
},
{
abbreviation = 'tgst',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When on, the |tagstack| is used normally. When off, a ":tag" or
":tselect" command with an argument will not push the tag onto the
@@ -8880,7 +9092,7 @@ return {
},
{
abbreviation = 'tbidi',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
The terminal is in charge of Bi-directionality of text (as specified
by Unicode). The terminal is also expected to do the required shaping
@@ -8899,7 +9111,7 @@ return {
},
{
abbreviation = 'tenc',
- defaults = { if_true = '' },
+ defaults = '',
full_name = 'termencoding',
scope = { 'global' },
short_desc = N_('Terminal encoding'),
@@ -8908,7 +9120,7 @@ return {
},
{
abbreviation = 'tgc',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Enables 24-bit RGB color in the |TUI|. Uses "gui" |:highlight|
attributes instead of "cterm" attributes. |guifg|
@@ -8927,8 +9139,9 @@ return {
},
{
abbreviation = 'tpf',
- cb = 'did_set_termpastefilter',
- defaults = { if_true = 'BS,HT,ESC,DEL' },
+ defaults = 'BS,HT,ESC,DEL',
+ values = { 'BS', 'HT', 'FF', 'ESC', 'DEL', 'C0', 'C1' },
+ flags = true,
deny_duplicates = true,
desc = [=[
A comma-separated list of options for specifying control characters
@@ -8950,15 +9163,15 @@ return {
C1 Control characters 0x80...0x9F
]=],
- expand_cb = 'expand_set_termpastefilter',
full_name = 'termpastefilter',
list = 'onecomma',
scope = { 'global' },
type = 'string',
varname = 'p_tpf',
+ flags_varname = 'tpf_flags',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
If the host terminal supports it, buffer all screen updates
made during a redraw cycle so that each screen is displayed in
@@ -8973,7 +9186,7 @@ return {
varname = 'p_termsync',
},
{
- defaults = { if_true = false },
+ defaults = false,
full_name = 'terse',
scope = { 'global' },
short_desc = N_('No description'),
@@ -8983,7 +9196,7 @@ return {
{
abbreviation = 'tw',
cb = 'did_set_textwidth',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Maximum width of text that is being inserted. A longer line will be
broken after white space to get this width. A zero value disables
@@ -9001,7 +9214,7 @@ return {
},
{
abbreviation = 'tsr',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of file names, separated by commas, that are used to lookup words
@@ -9031,7 +9244,7 @@ return {
{
abbreviation = 'tsrfu',
cb = 'did_set_thesaurusfunc',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
This option specifies a function to be used for thesaurus completion
with CTRL-X CTRL-T. |i_CTRL-X_CTRL-T| See |compl-thesaurusfunc|.
@@ -9051,7 +9264,7 @@ return {
},
{
abbreviation = 'top',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on: The tilde command "~" behaves like an operator.
]=],
@@ -9063,7 +9276,7 @@ return {
},
{
abbreviation = 'to',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
This option and 'timeoutlen' determine the behavior when part of a
mapped key sequence has been received. For example, if <c-f> is
@@ -9078,7 +9291,7 @@ return {
},
{
abbreviation = 'tm',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Time in milliseconds to wait for a mapped sequence to complete.
]=],
@@ -9090,7 +9303,7 @@ return {
},
{
cb = 'did_set_title_icon',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, the title of the window will be set to the value of
'titlestring' (if it is not empty), or to:
@@ -9112,7 +9325,7 @@ return {
},
{
cb = 'did_set_titlelen',
- defaults = { if_true = 85 },
+ defaults = 85,
desc = [=[
Gives the percentage of 'columns' to use for the length of the window
title. When the title is longer, only the end of the path name is
@@ -9131,7 +9344,7 @@ return {
varname = 'p_titlelen',
},
{
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
If not empty, this option will be used to set the window title when
exiting. Only if 'title' is enabled.
@@ -9148,7 +9361,7 @@ return {
},
{
cb = 'did_set_titlestring',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When this option is not empty, it will be used for the title of the
window. This happens only when the 'title' option is on.
@@ -9157,6 +9370,10 @@ return {
expanded according to the rules used for 'statusline'. If it contains
an invalid '%' format, the value is used as-is and no error or warning
will be given when the value is set.
+
+ The default behaviour is equivalent to: >vim
+ set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim
+ <
This option cannot be set in a modeline when 'modelineexpr' is off.
Example: >vim
@@ -9180,7 +9397,7 @@ return {
varname = 'p_titlestring',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
This option and 'ttimeoutlen' determine the behavior when part of a
key code sequence has been received by the |TUI|.
@@ -9203,7 +9420,7 @@ return {
},
{
abbreviation = 'ttm',
- defaults = { if_true = 50 },
+ defaults = 50,
desc = [=[
Time in milliseconds to wait for a key code sequence to complete. Also
used for CTRL-\ CTRL-N and CTRL-\ CTRL-G when part of a command has
@@ -9218,7 +9435,7 @@ return {
},
{
abbreviation = 'tf',
- defaults = { if_true = true },
+ defaults = true,
full_name = 'ttyfast',
no_mkrc = true,
scope = { 'global' },
@@ -9228,7 +9445,7 @@ return {
},
{
abbreviation = 'udir',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
List of directory names for undo files, separated with commas.
@@ -9265,7 +9482,7 @@ return {
{
abbreviation = 'udf',
cb = 'did_set_undofile',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When on, Vim automatically saves undo history to an undo file when
writing a buffer to a file, and restores undo history from the same
@@ -9285,7 +9502,7 @@ return {
{
abbreviation = 'ul',
cb = 'did_set_undolevels',
- defaults = { if_true = 1000 },
+ defaults = 1000,
desc = [=[
Maximum number of changes that can be undone. Since undo information
is kept in memory, higher numbers will cause more memory to be used.
@@ -9313,7 +9530,7 @@ return {
},
{
abbreviation = 'ur',
- defaults = { if_true = 10000 },
+ defaults = 10000,
desc = [=[
Save the whole buffer for undo when reloading it. This applies to the
":e!" command and reloading for when the buffer changed outside of
@@ -9336,7 +9553,7 @@ return {
{
abbreviation = 'uc',
cb = 'did_set_updatecount',
- defaults = { if_true = 200 },
+ defaults = 200,
desc = [=[
After typing this many characters the swap file will be written to
disk. When zero, no swap file will be created at all (see chapter on
@@ -9358,7 +9575,7 @@ return {
},
{
abbreviation = 'ut',
- defaults = { if_true = 4000 },
+ defaults = 4000,
desc = [=[
If this many milliseconds nothing is typed the swap file will be
written to disk (see |crash-recovery|). Also used for the
@@ -9432,7 +9649,7 @@ return {
{
abbreviation = 'vsts',
cb = 'did_set_varsofttabstop',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
A list of the number of spaces that a <Tab> counts for while editing,
such as inserting a <Tab> or using <BS>. It "feels" like variable-
@@ -9460,7 +9677,7 @@ return {
{
abbreviation = 'vts',
cb = 'did_set_vartabstop',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
A list of the number of spaces that a <Tab> in the file counts for,
separated by commas. Each value corresponds to one tab, with the
@@ -9482,7 +9699,7 @@ return {
},
{
abbreviation = 'vbs',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Sets the verbosity level. Also set by |-V| and |:verbose|.
@@ -9521,7 +9738,7 @@ return {
{
abbreviation = 'vfile',
cb = 'did_set_verbosefile',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When not empty all messages are written in a file with this name.
When the file exists messages are appended.
@@ -9543,7 +9760,7 @@ return {
},
{
abbreviation = 'vdir',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
Name of the directory where to store files for |:mkview|.
This option cannot be set from a |modeline| or in the |sandbox|, for
@@ -9559,8 +9776,9 @@ return {
},
{
abbreviation = 'vop',
- cb = 'did_set_viewoptions',
- defaults = { if_true = 'folds,cursor,curdir' },
+ cb = 'did_set_str_generic',
+ defaults = 'folds,cursor,curdir',
+ flags = true,
deny_duplicates = true,
desc = [=[
Changes the effect of the |:mkview| command. It is a comma-separated
@@ -9576,18 +9794,28 @@ return {
slash |deprecated| Always enabled. Uses "/" in filenames.
unix |deprecated| Always enabled. Uses "\n" line endings.
]=],
- expand_cb = 'expand_set_sessionoptions',
+ expand_cb = 'expand_set_str_generic',
full_name = 'viewoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('specifies what to save for :mkview'),
type = 'string',
varname = 'p_vop',
+ flags_varname = 'vop_flags',
},
{
abbreviation = 've',
cb = 'did_set_virtualedit',
- defaults = { if_true = '' },
+ defaults = '',
+ values = { 'block', 'insert', 'all', 'onemore', 'none', 'NONE' },
+ flags = {
+ Block = 5,
+ Insert = 6,
+ All = 4,
+ Onemore = 8,
+ None = 16,
+ NoneU = 32,
+ },
deny_duplicates = true,
desc = [=[
A comma-separated list of these words:
@@ -9617,7 +9845,6 @@ return {
not get a warning for it.
When combined with other words, "none" is ignored.
]=],
- expand_cb = 'expand_set_virtualedit',
full_name = 'virtualedit',
list = 'onecomma',
redraw = { 'curswant' },
@@ -9625,10 +9852,11 @@ return {
short_desc = N_('when to use virtual editing'),
type = 'string',
varname = 'p_ve',
+ flags_varname = 've_flags',
},
{
abbreviation = 'vb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Use visual bell instead of beeping. Also see 'errorbells'.
]=],
@@ -9639,7 +9867,7 @@ return {
varname = 'p_vb',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Give a warning message when a shell command is used while the buffer
has been changed.
@@ -9653,7 +9881,7 @@ return {
{
abbreviation = 'ww',
cb = 'did_set_whichwrap',
- defaults = { if_true = 'b,s' },
+ defaults = 'b,s',
desc = [=[
Allow specified keys that move the cursor left/right to move to the
previous/next line when the cursor is on the first/last character in
@@ -9724,7 +9952,7 @@ return {
{
abbreviation = 'wcm',
cb = 'did_set_wildchar',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
'wildcharm' works exactly like 'wildchar', except that it is
recognized when used inside a macro. You can find "spare" command-line
@@ -9743,7 +9971,7 @@ return {
},
{
abbreviation = 'wig',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
A list of file patterns. A file that matches with one of these
@@ -9767,7 +9995,7 @@ return {
},
{
abbreviation = 'wic',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
When set case is ignored when completing file names and directories.
Has no effect when 'fileignorecase' is set.
@@ -9782,7 +10010,7 @@ return {
},
{
abbreviation = 'wmnu',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
When 'wildmenu' is on, command-line completion operates in an enhanced
mode. On pressing 'wildchar' (usually <Tab>) to invoke completion,
@@ -9831,7 +10059,10 @@ return {
{
abbreviation = 'wim',
cb = 'did_set_wildmode',
- defaults = { if_true = 'full' },
+ defaults = 'full',
+ -- Keep this in sync with check_opt_wim().
+ values = { 'full', 'longest', 'list', 'lastused' },
+ flags = true,
deny_duplicates = false,
desc = [=[
Completion mode that is used for the character specified with
@@ -9878,7 +10109,6 @@ return {
< Complete longest common string, then list alternatives.
More info here: |cmdline-completion|.
]=],
- expand_cb = 'expand_set_wildmode',
full_name = 'wildmode',
list = 'onecommacolon',
scope = { 'global' },
@@ -9888,8 +10118,9 @@ return {
},
{
abbreviation = 'wop',
- cb = 'did_set_wildoptions',
- defaults = { if_true = 'pum,tagfile' },
+ defaults = 'pum,tagfile',
+ values = { 'fuzzy', 'tagfile', 'pum' },
+ flags = true,
deny_duplicates = true,
desc = [=[
A list of words that change how |cmdline-completion| is done.
@@ -9910,18 +10141,18 @@ return {
d #define
f function
]=],
- expand_cb = 'expand_set_wildoptions',
full_name = 'wildoptions',
list = 'onecomma',
scope = { 'global' },
short_desc = N_('specifies how command line completion is done'),
type = 'string',
varname = 'p_wop',
+ flags_varname = 'wop_flags',
},
{
abbreviation = 'wak',
- cb = 'did_set_winaltkeys',
- defaults = { if_true = 'menu' },
+ defaults = 'menu',
+ values = { 'yes', 'menu', 'no' },
desc = [=[
only used in Win32
Some GUI versions allow the access to menu entries by using the ALT
@@ -9939,7 +10170,6 @@ return {
key is never used for the menu.
This option is not used for <F10>; on Win32.
]=],
- expand_cb = 'expand_set_winaltkeys',
full_name = 'winaltkeys',
scope = { 'global' },
short_desc = N_('when the windows system handles ALT keys'),
@@ -9949,7 +10179,7 @@ return {
{
abbreviation = 'wbr',
cb = 'did_set_winbar',
- defaults = { if_true = '' },
+ defaults = '',
desc = [=[
When non-empty, this option enables the window bar and determines its
contents. The window bar is a bar that's shown at the top of every
@@ -9976,7 +10206,7 @@ return {
{
abbreviation = 'winbl',
cb = 'did_set_winblend',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Enables pseudo-transparency for a floating window. Valid values are in
the range of 0 for fully opaque window (disabled) to 100 for fully
@@ -10016,7 +10246,7 @@ return {
},
{
abbreviation = 'wfb',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
If enabled, the window and the buffer it is displaying are paired.
For example, attempting to change the buffer with |:edit| will fail.
@@ -10031,7 +10261,7 @@ return {
},
{
abbreviation = 'wfh',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Keep the window height when windows are opened or closed and
'equalalways' is set. Also for |CTRL-W_=|. Set by default for the
@@ -10046,7 +10276,7 @@ return {
},
{
abbreviation = 'wfw',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Keep the window width when windows are opened or closed and
'equalalways' is set. Also for |CTRL-W_=|.
@@ -10061,7 +10291,7 @@ return {
{
abbreviation = 'wh',
cb = 'did_set_winheight',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
Minimal number of lines for the current window. This is not a hard
minimum, Vim will use fewer lines if there is not enough room. If the
@@ -10090,7 +10320,7 @@ return {
{
abbreviation = 'winhl',
cb = 'did_set_winhighlight',
- defaults = { if_true = '' },
+ defaults = '',
deny_duplicates = true,
desc = [=[
Window-local highlights. Comma-delimited list of highlight
@@ -10122,7 +10352,7 @@ return {
{
abbreviation = 'wmh',
cb = 'did_set_winminheight',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The minimal height of a window, when it's not the current window.
This is a hard minimum, windows will never become smaller.
@@ -10143,7 +10373,7 @@ return {
{
abbreviation = 'wmw',
cb = 'did_set_winminwidth',
- defaults = { if_true = 1 },
+ defaults = 1,
desc = [=[
The minimal width of a window, when it's not the current window.
This is a hard minimum, windows will never become smaller.
@@ -10165,7 +10395,7 @@ return {
{
abbreviation = 'wiw',
cb = 'did_set_winwidth',
- defaults = { if_true = 20 },
+ defaults = 20,
desc = [=[
Minimal number of columns for the current window. This is not a hard
minimum, Vim will use fewer columns if there is not enough room. If
@@ -10186,7 +10416,7 @@ return {
},
{
cb = 'did_set_wrap',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
This option changes how text is displayed. It doesn't change the text
in the buffer, see 'textwidth' for that.
@@ -10212,7 +10442,7 @@ return {
},
{
abbreviation = 'wm',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Number of characters from the right window border where wrapping
starts. When typing text beyond this limit, an <EOL> will be inserted
@@ -10230,7 +10460,7 @@ return {
},
{
abbreviation = 'ws',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Searches wrap around the end of the file. Also applies to |]s| and
|[s|, searching for spelling mistakes.
@@ -10243,7 +10473,7 @@ return {
varname = 'p_ws',
},
{
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Allows writing files. When not set, writing a file is not allowed.
Can be used for a view-only mode, where modifications to the text are
@@ -10259,7 +10489,7 @@ return {
},
{
abbreviation = 'wa',
- defaults = { if_true = false },
+ defaults = false,
desc = [=[
Allows writing to any file with no need for "!" override.
]=],
@@ -10271,7 +10501,7 @@ return {
},
{
abbreviation = 'wb',
- defaults = { if_true = true },
+ defaults = true,
desc = [=[
Make a backup before overwriting a file. The backup is removed after
the file was successfully written, unless the 'backup' option is
@@ -10294,7 +10524,7 @@ return {
},
{
abbreviation = 'wd',
- defaults = { if_true = 0 },
+ defaults = 0,
desc = [=[
Only takes effect together with 'redrawdebug'.
The number of milliseconds to wait after each line or each flush
@@ -10307,3 +10537,29 @@ return {
},
},
}
+
+--- @param o vim.option_meta
+local function preprocess(o)
+ if o.values then
+ o.cb = o.cb or 'did_set_str_generic'
+ o.expand_cb = o.expand_cb or 'expand_set_str_generic'
+ end
+
+ if type(o.alias) == 'string' then
+ o.alias = {
+ o.alias --[[@as string]],
+ }
+ end
+
+ if type(o.defaults) ~= 'table' then
+ o.defaults = {
+ if_true = o.defaults --[[@as string|boolean|number ]],
+ }
+ end
+end
+
+for _, o in ipairs(options.options) do
+ preprocess(o)
+end
+
+return options
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index bfb26a0be6..645bb23638 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -3,6 +3,7 @@
#include <stdint.h>
#include <string.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
@@ -15,7 +16,6 @@
#include "nvim/digraph.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
-#include "nvim/eval/typval_defs.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_getln.h"
@@ -45,6 +45,7 @@
#include "nvim/spellfile.h"
#include "nvim/spellsuggest.h"
#include "nvim/strings.h"
+#include "nvim/terminal.h"
#include "nvim/types_defs.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -68,84 +69,6 @@ static const char e_wrong_number_of_characters_for_field_str[]
static const char e_wrong_character_width_for_field_str[]
= N_("E1512: Wrong character width for field \"%s\"");
-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", "insertmode", "lang", "mess", "showmatch",
- "operator", "register", "shell", "spell", "term", "wildmode",
- NULL };
-// Note: Keep this in sync with briopt_check()
-static char *(p_briopt_values[]) = { "shift:", "min:", "sbr", "list:", "column:", NULL };
-// Note: Keep this in sync with diffopt_changed()
-static char *(p_dip_values[]) = { "filler", "context:", "iblank", "icase",
- "iwhite", "iwhiteall", "iwhiteeol", "horizontal", "vertical",
- "closeoff", "hiddenoff", "foldcolumn:", "followwrap", "internal",
- "indent-heuristic", "linematch:", "algorithm:", NULL };
-static char *(p_dip_algorithm_values[]) = { "myers", "minimal", "patience", "histogram", NULL };
-static char *(p_nf_values[]) = { "bin", "octal", "hex", "alpha", "unsigned", "blank", NULL };
-static char *(p_ff_values[]) = { "unix", "dos", "mac", NULL };
-static char *(p_cb_values[]) = { "unnamed", "unnamedplus", NULL };
-static char *(p_cmp_values[]) = { "internal", "keepascii", NULL };
-// Note: Keep this in sync with fill_culopt_flags()
-static char *(p_culopt_values[]) = { "line", "screenline", "number", "both", 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 };
-// Note: Keep this in sync with spell_check_sps()
-static char *(p_sps_values[]) = { "best", "fast", "double", "expr:", "file:", "timeout:", 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_vars.h
-static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vsplit", "uselast",
- NULL };
-static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL };
-static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL };
-// Keep in sync with TCL_ flags in option_vars.h
-static char *(p_tcl_values[]) = { "left", "uselast", NULL };
-static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
-// Note: Keep this in sync with check_opt_wim()
-static char *(p_wim_values[]) = { "full", "longest", "list", "lastused", NULL };
-static char *(p_wop_values[]) = { "fuzzy", "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", "popup",
- "noinsert", "noselect", "fuzzy", 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_spo_values[]) = { "camel", "noplainbuffer", NULL };
-static char *(p_icm_values[]) = { "nosplit", "split", NULL };
-static char *(p_jop_values[]) = { "stack", "view", "clean", NULL };
-static char *(p_tpf_values[]) = { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL };
-static char *(p_rdb_values[]) = { "compositor", "nothrottle", "invalid", "nodelta", "line",
- "flush", NULL };
-static char *(p_sloc_values[]) = { "last", "statusline", "tabline", NULL };
-
/// All possible flags for 'shm'.
/// the literal chars before 0 are removed flags. these are safely ignored
static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES,
@@ -158,23 +81,23 @@ static char SHM_ALL[] = { SHM_RO, SHM_MOD, SHM_LINES,
/// option values.
void didset_string_options(void)
{
- opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, true);
- opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, true);
- opt_strings_flags(p_bo, p_bo_values, &bo_flags, true);
- opt_strings_flags(p_cot, p_cot_values, &cot_flags, true);
- opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true);
- opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true);
- opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true);
- opt_strings_flags(p_dy, p_dy_values, &dy_flags, true);
- opt_strings_flags(p_jop, p_jop_values, &jop_flags, true);
- opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true);
- opt_strings_flags(p_tc, p_tc_values, &tc_flags, false);
- opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
- opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
- opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
- opt_strings_flags(p_tcl, p_tcl_values, &tcl_flags, true);
- opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
- opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
+ check_str_opt(kOptCasemap, NULL);
+ check_str_opt(kOptBackupcopy, NULL);
+ check_str_opt(kOptBelloff, NULL);
+ check_str_opt(kOptCompleteopt, NULL);
+ check_str_opt(kOptSessionoptions, NULL);
+ check_str_opt(kOptViewoptions, NULL);
+ check_str_opt(kOptFoldopen, NULL);
+ check_str_opt(kOptDisplay, NULL);
+ check_str_opt(kOptJumpoptions, NULL);
+ check_str_opt(kOptRedrawdebug, NULL);
+ check_str_opt(kOptTagcase, NULL);
+ check_str_opt(kOptTermpastefilter, NULL);
+ check_str_opt(kOptVirtualedit, NULL);
+ check_str_opt(kOptSwitchbuf, NULL);
+ check_str_opt(kOptTabclose, NULL);
+ check_str_opt(kOptWildoptions, NULL);
+ check_str_opt(kOptClipboard, NULL);
}
char *illegal_char(char *errbuf, size_t errbuflen, int c)
@@ -300,7 +223,7 @@ int check_signcolumn(char *scl, win_T *wp)
return FAIL;
}
- if (check_opt_strings(val, p_scl_values, false) == OK) {
+ if (opt_strings_flags(val, opt_scl_values, NULL, false) == OK) {
if (wp == NULL) {
return OK;
}
@@ -428,7 +351,7 @@ bool check_illegal_path_names(char *val, uint32_t flags)
/// An option that accepts a list of flags is changed.
/// e.g. 'viewoptions', 'switchbuf', 'casemap', etc.
-static const char *did_set_opt_flags(char *val, char **values, unsigned *flagp, bool list)
+static const char *did_set_opt_flags(char *val, const char **values, unsigned *flagp, bool list)
{
if (opt_strings_flags(val, values, flagp, list) != OK) {
return e_invarg;
@@ -436,11 +359,40 @@ static const char *did_set_opt_flags(char *val, char **values, unsigned *flagp,
return NULL;
}
-/// An option that accepts a list of string values is changed.
-/// e.g. 'nrformats', 'scrollopt', 'wildoptions', etc.
-static const char *did_set_opt_strings(char *val, char **values, bool list)
+static const char **opt_values(OptIndex idx, size_t *values_len)
+{
+ OptIndex idx1 = idx == kOptViewoptions ? kOptSessionoptions
+ : idx == kOptFileformats ? kOptFileformat
+ : idx;
+
+ vimoption_T *opt = get_option(idx1);
+ if (values_len != NULL) {
+ *values_len = opt->values_len;
+ }
+ return opt->values;
+}
+
+static int check_str_opt(OptIndex idx, char **varp)
{
- return did_set_opt_flags(val, values, NULL, list);
+ vimoption_T *opt = get_option(idx);
+ if (varp == NULL) {
+ varp = opt->var;
+ }
+ bool list = opt->flags & (kOptFlagComma | kOptFlagOneComma);
+ const char **values = opt_values(idx, NULL);
+ return opt_strings_flags(*varp, values, opt->flags_var, list);
+}
+
+int expand_set_str_generic(optexpand_T *args, int *numMatches, char ***matches)
+{
+ size_t values_len;
+ const char **values = opt_values(args->oe_idx, &values_len);
+ return expand_set_opt_string(args, values, values_len, numMatches, matches);
+}
+
+const char *did_set_str_generic(optset_T *args)
+{
+ return check_str_opt(args->os_idx, args->os_varp) != OK ? e_invarg : NULL;
}
/// An option which is a list of flags is set. Valid values are in "flags".
@@ -456,7 +408,7 @@ static const char *did_set_option_listflag(char *val, char *flags, char *errbuf,
}
/// Expand an option that accepts a list of string values.
-static int expand_set_opt_string(optexpand_T *args, char **values, size_t numValues,
+static int expand_set_opt_string(optexpand_T *args, const char **values, size_t numValues,
int *numMatches, char ***matches)
{
regmatch_T *regmatch = args->oe_regmatch;
@@ -473,8 +425,10 @@ static int expand_set_opt_string(optexpand_T *args, char **values, size_t numVal
(*matches)[count++] = xstrdup(option_val);
}
- for (char **val = values; *val != NULL; val++) {
- if (include_orig_val && *option_val != NUL) {
+ for (const char **val = values; *val != NULL; val++) {
+ if (**val == NUL) {
+ continue; // Ignore empty
+ } else if (include_orig_val && *option_val != NUL) {
if (strcmp(*val, option_val) == 0) {
continue;
}
@@ -566,28 +520,30 @@ static int expand_set_opt_listflag(optexpand_T *args, char *flags, int *numMatch
}
/// The 'ambiwidth' option is changed.
-const char *did_set_ambiwidth(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_ambiwidth(optset_T *args)
{
- if (check_opt_strings(p_ambw, p_ambw_values, false) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
return check_chars_options();
}
-int expand_set_ambiwidth(optexpand_T *args, int *numMatches, char ***matches)
+/// The 'emoji' option is changed.
+const char *did_set_emoji(optset_T *args)
{
- return expand_set_opt_string(args,
- p_ambw_values,
- ARRAY_SIZE(p_ambw_values) - 1,
- numMatches,
- matches);
+ if (check_str_opt(kOptAmbiwidth, NULL) != OK) {
+ return e_invarg;
+ }
+ return check_chars_options();
}
/// The 'background' option is changed.
const char *did_set_background(optset_T *args)
{
- if (check_opt_strings(p_bg, p_bg_values, false) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
if (args->os_oldval.string.data[0] == *p_bg) {
@@ -609,16 +565,16 @@ const char *did_set_background(optset_T *args)
check_string_option(&p_bg);
init_highlight(false, false);
}
- return NULL;
-}
-int expand_set_background(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bg_values,
- ARRAY_SIZE(p_bg_values) - 1,
- numMatches,
- matches);
+ // Notify all terminal buffers that the background color changed so they can
+ // send a theme update notification
+ FOR_ALL_BUFFERS(buf) {
+ if (buf->terminal) {
+ terminal_notify_theme(buf->terminal, dark);
+ }
+ }
+
+ return NULL;
}
/// The 'backspace' option is changed.
@@ -628,19 +584,9 @@ const char *did_set_backspace(optset_T *args FUNC_ATTR_UNUSED)
if (*p_bs != '2') {
return e_invarg;
}
- } else if (check_opt_strings(p_bs, p_bs_values, true) != OK) {
- return e_invarg;
+ return NULL;
}
- return NULL;
-}
-
-int expand_set_backspace(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bs_values,
- ARRAY_SIZE(p_bs_values) - 1,
- numMatches,
- matches);
+ return did_set_str_generic(args);
}
/// The 'backupcopy' option is changed.
@@ -664,15 +610,15 @@ const char *did_set_backupcopy(optset_T *args)
// make the local value empty: use the global value
*flags = 0;
} else {
- if (opt_strings_flags(bkc, p_bkc_values, flags, true) != OK) {
+ if (opt_strings_flags(bkc, opt_bkc_values, flags, true) != OK) {
return e_invarg;
}
- if (((*flags & BKC_AUTO) != 0)
- + ((*flags & BKC_YES) != 0)
- + ((*flags & BKC_NO) != 0) != 1) {
+ if (((*flags & kOptBkcFlagAuto) != 0)
+ + ((*flags & kOptBkcFlagYes) != 0)
+ + ((*flags & kOptBkcFlagNo) != 0) != 1) {
// Must have exactly one of "auto", "yes" and "no".
- opt_strings_flags(oldval, p_bkc_values, flags, true);
+ opt_strings_flags(oldval, opt_bkc_values, flags, true);
return e_invarg;
}
}
@@ -680,15 +626,6 @@ const char *did_set_backupcopy(optset_T *args)
return NULL;
}
-int expand_set_backupcopy(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bkc_values,
- ARRAY_SIZE(p_bkc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'backupext' or the 'patchmode' option is changed.
const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -700,21 +637,6 @@ const char *did_set_backupext_or_patchmode(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-/// The 'belloff' option is changed.
-const char *did_set_belloff(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_bo, p_bo_values, &bo_flags, true);
-}
-
-int expand_set_belloff(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bo_values,
- ARRAY_SIZE(p_bo_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'breakat' option is changed.
const char *did_set_breakat(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -749,29 +671,11 @@ const char *did_set_breakindentopt(optset_T *args)
return NULL;
}
-int expand_set_breakindentopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_briopt_values,
- ARRAY_SIZE(p_briopt_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'bufhidden' option is changed.
const char *did_set_bufhidden(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- return did_set_opt_strings(buf->b_p_bh, p_bufhidden_values, false);
-}
-
-int expand_set_bufhidden(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_bufhidden_values,
- ARRAY_SIZE(p_bufhidden_values) - 1,
- numMatches,
- matches);
+ return did_set_opt_flags(buf->b_p_bh, opt_bh_values, NULL, false);
}
/// The 'buftype' option is changed.
@@ -782,7 +686,7 @@ const char *did_set_buftype(optset_T *args)
// When 'buftype' is set, check for valid value.
if ((buf->terminal && buf->b_p_bt[0] != 't')
|| (!buf->terminal && buf->b_p_bt[0] == 't')
- || check_opt_strings(buf->b_p_bt, p_buftype_values, false) != OK) {
+ || opt_strings_flags(buf->b_p_bt, opt_bt_values, NULL, false) != OK) {
return e_invarg;
}
if (win->w_status_height || global_stl_height()) {
@@ -794,30 +698,6 @@ const char *did_set_buftype(optset_T *args)
return NULL;
}
-int expand_set_buftype(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_buftype_values,
- ARRAY_SIZE(p_buftype_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'casemap' option is changed.
-const char *did_set_casemap(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_cmp, p_cmp_values, &cmp_flags, true);
-}
-
-int expand_set_casemap(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_cmp_values,
- ARRAY_SIZE(p_cmp_values) - 1,
- numMatches,
- matches);
-}
-
/// The global 'listchars' or 'fillchars' option is changed.
static const char *did_set_global_chars_option(win_T *win, char *val, CharsOption what,
int opt_flags, char *errbuf, size_t errbuflen)
@@ -901,21 +781,6 @@ const char *did_set_cinoptions(optset_T *args)
return NULL;
}
-/// The 'clipboard' option is changed.
-const char *did_set_clipboard(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_cb, p_cb_values, &cb_flags, true);
-}
-
-int expand_set_clipboard(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_cb_values,
- ARRAY_SIZE(p_cb_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'colorcolumn' option is changed.
const char *did_set_colorcolumn(optset_T *args)
{
@@ -1007,18 +872,6 @@ const char *did_set_complete(optset_T *args)
return NULL;
}
-int expand_set_complete(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_cpt_values[]) = {
- ".", "w", "b", "u", "k", "kspell", "s", "i", "d", "]", "t", "U", "f", NULL
- };
- return expand_set_opt_string(args,
- p_cpt_values,
- ARRAY_SIZE(p_cpt_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'completeitemalign' option is changed.
const char *did_set_completeitemalign(optset_T *args)
{
@@ -1079,46 +932,28 @@ const char *did_set_completeopt(optset_T *args FUNC_ATTR_UNUSED)
buf->b_cot_flags = 0;
}
- if (check_opt_strings(cot, p_cot_values, true) != OK) {
+ if (opt_strings_flags(cot, opt_cot_values, NULL, true) != OK) {
return e_invarg;
}
- if (opt_strings_flags(cot, p_cot_values, flags, true) != OK) {
+ if (opt_strings_flags(cot, opt_cot_values, flags, true) != OK) {
return e_invarg;
}
return NULL;
}
-int expand_set_completeopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_cot_values,
- ARRAY_SIZE(p_cot_values) - 1,
- numMatches,
- matches);
-}
-
#ifdef BACKSLASH_IN_FILENAME
/// The 'completeslash' option is changed.
const char *did_set_completeslash(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- if (check_opt_strings(p_csl, p_csl_values, false) != OK
- || check_opt_strings(buf->b_p_csl, p_csl_values, false) != OK) {
+ if (opt_strings_flags(p_csl, opt_csl_values, NULL, false) != OK
+ || opt_strings_flags(buf->b_p_csl, opt_csl_values, NULL, false) != OK) {
return e_invarg;
}
return NULL;
}
-
-int expand_set_completeslash(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_csl_values,
- ARRAY_SIZE(p_csl_values) - 1,
- numMatches,
- matches);
-}
#endif
/// The 'concealcursor' option is changed.
@@ -1161,37 +996,10 @@ const char *did_set_cursorlineopt(optset_T *args)
return NULL;
}
-int expand_set_cursorlineopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_culopt_values,
- ARRAY_SIZE(p_culopt_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'debug' option is changed.
-const char *did_set_debug(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_debug, p_debug_values, false);
-}
-
-int expand_set_debug(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_debug_values,
- ARRAY_SIZE(p_debug_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'diffopt' option is changed.
const char *did_set_diffopt(optset_T *args FUNC_ATTR_UNUSED)
{
- if (diffopt_changed() == FAIL) {
- return e_invarg;
- }
- return NULL;
+ return diffopt_changed() == FAIL ? e_invarg : NULL;
}
int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches)
@@ -1204,56 +1012,29 @@ int expand_set_diffopt(optexpand_T *args, int *numMatches, char ***matches)
if (xp->xp_pattern - args->oe_set_arg >= (int)algo_len
&& strncmp(xp->xp_pattern - algo_len, "algorithm:", algo_len) == 0) {
return expand_set_opt_string(args,
- p_dip_algorithm_values,
- ARRAY_SIZE(p_dip_algorithm_values) - 1,
+ opt_dip_algorithm_values,
+ ARRAY_SIZE(opt_dip_algorithm_values) - 1,
numMatches,
matches);
}
return FAIL;
}
- return expand_set_opt_string(args,
- p_dip_values,
- ARRAY_SIZE(p_dip_values) - 1,
- numMatches,
- matches);
+ return expand_set_str_generic(args, numMatches, matches);
}
/// The 'display' option is changed.
-const char *did_set_display(optset_T *args FUNC_ATTR_UNUSED)
+const char *did_set_display(optset_T *args)
{
- if (opt_strings_flags(p_dy, p_dy_values, &dy_flags, true) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
init_chartab();
msg_grid_validate();
return NULL;
}
-int expand_set_display(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_dy_values,
- ARRAY_SIZE(p_dy_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'eadirection' option is changed.
-const char *did_set_eadirection(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_ead, p_ead_values, false);
-}
-
-int expand_set_eadirection(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ead_values,
- ARRAY_SIZE(p_ead_values) - 1,
- numMatches,
- matches);
-}
-
/// One of the 'encoding', 'fileencoding' or 'makeencoding'
/// options is changed.
const char *did_set_encoding(optset_T *args)
@@ -1329,14 +1110,17 @@ int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches)
const char *did_set_fileformat(optset_T *args)
{
buf_T *buf = (buf_T *)args->os_buf;
- char **varp = (char **)args->os_varp;
const char *oldval = args->os_oldval.string.data;
int opt_flags = args->os_flags;
if (!MODIFIABLE(buf) && !(opt_flags & OPT_GLOBAL)) {
return e_modifiable;
- } else if (check_opt_strings(*varp, p_ff_values, false) != OK) {
- return e_invarg;
}
+
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
+ }
+
redraw_titles();
// update flag in swap file
ml_setflags(buf);
@@ -1348,30 +1132,15 @@ const char *did_set_fileformat(optset_T *args)
return NULL;
}
-int expand_set_fileformat(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ff_values,
- ARRAY_SIZE(p_ff_values) - 1,
- numMatches,
- matches);
-}
-
/// Function given to ExpandGeneric() to obtain the possible arguments of the
/// fileformat options.
char *get_fileformat_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
- if (idx >= (int)ARRAY_SIZE(p_ff_values)) {
+ if (idx >= (int)ARRAY_SIZE(opt_ff_values)) {
return NULL;
}
- return p_ff_values[idx];
-}
-
-/// The 'fileformats' option is changed.
-const char *did_set_fileformats(optset_T *args)
-{
- return did_set_opt_strings(p_ffs, p_ff_values, true);
+ return (char *)opt_ff_values[idx];
}
/// The 'filetype' or the 'syntax' option is changed.
@@ -1392,40 +1161,6 @@ const char *did_set_filetype_or_syntax(optset_T *args)
return NULL;
}
-/// The 'foldclose' option is changed.
-const char *did_set_foldclose(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_fcl, p_fcl_values, true);
-}
-
-int expand_set_foldclose(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fcl_values,
- ARRAY_SIZE(p_fcl_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'foldcolumn' option is changed.
-const char *did_set_foldcolumn(optset_T *args)
-{
- char **varp = (char **)args->os_varp;
- if (**varp == NUL || check_opt_strings(*varp, p_fdc_values, false) != OK) {
- return e_invarg;
- }
- return NULL;
-}
-
-int expand_set_foldcolumn(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fdc_values,
- ARRAY_SIZE(p_fdc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'foldexpr' option is changed.
const char *did_set_foldexpr(optset_T *args)
{
@@ -1472,11 +1207,11 @@ const char *did_set_foldmarker(optset_T *args)
/// The 'foldmethod' option is changed.
const char *did_set_foldmethod(optset_T *args)
{
- win_T *win = (win_T *)args->os_win;
- char **varp = (char **)args->os_varp;
- if (check_opt_strings(*varp, p_fdm_values, false) != OK || **varp == NUL) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
+ win_T *win = (win_T *)args->os_win;
foldUpdateAll(win);
if (foldmethodIsDiff(win)) {
newFoldLevel();
@@ -1484,30 +1219,6 @@ const char *did_set_foldmethod(optset_T *args)
return NULL;
}
-int expand_set_foldmethod(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fdm_values,
- ARRAY_SIZE(p_fdm_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'foldopen' option is changed.
-const char *did_set_foldopen(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_fdo, p_fdo_values, &fdo_flags, true);
-}
-
-int expand_set_foldopen(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_fdo_values,
- ARRAY_SIZE(p_fdo_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'formatoptions' option is changed.
const char *did_set_formatoptions(optset_T *args)
{
@@ -1586,16 +1297,7 @@ const char *did_set_inccommand(optset_T *args FUNC_ATTR_UNUSED)
if (cmdpreview) {
return e_invarg;
}
- return did_set_opt_strings(p_icm, p_icm_values, false);
-}
-
-int expand_set_inccommand(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_icm_values,
- ARRAY_SIZE(p_icm_values) - 1,
- numMatches,
- matches);
+ return did_set_str_generic(args);
}
/// The 'iskeyword' option is changed.
@@ -1629,21 +1331,6 @@ const char *did_set_isopt(optset_T *args)
return NULL;
}
-/// The 'jumpoptions' option is changed.
-const char *did_set_jumpoptions(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_jop, p_jop_values, &jop_flags, true);
-}
-
-int expand_set_jumpoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_jop_values,
- ARRAY_SIZE(p_jop_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'keymap' option has changed.
const char *did_set_keymap(optset_T *args)
{
@@ -1699,23 +1386,15 @@ const char *did_set_keymap(optset_T *args)
/// The 'keymodel' option is changed.
const char *did_set_keymodel(optset_T *args FUNC_ATTR_UNUSED)
{
- if (check_opt_strings(p_km, p_km_values, true) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
km_stopsel = (vim_strchr(p_km, 'o') != NULL);
km_startsel = (vim_strchr(p_km, 'a') != NULL);
return NULL;
}
-int expand_set_keymodel(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_km_values,
- ARRAY_SIZE(p_km_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'lispoptions' option is changed.
const char *did_set_lispoptions(optset_T *args)
{
@@ -1727,16 +1406,6 @@ const char *did_set_lispoptions(optset_T *args)
return NULL;
}
-int expand_set_lispoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_lop_values[]) = { "expr:0", "expr:1", NULL };
- return expand_set_opt_string(args,
- p_lop_values,
- ARRAY_SIZE(p_lop_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'matchpairs' option is changed.
const char *did_set_matchpairs(optset_T *args)
{
@@ -1764,6 +1433,15 @@ const char *did_set_matchpairs(optset_T *args)
return NULL;
}
+/// Process the updated 'messagesopt' option value.
+const char *did_set_messagesopt(optset_T *args FUNC_ATTR_UNUSED)
+{
+ if (messagesopt_changed() == FAIL) {
+ return e_invarg;
+ }
+ return NULL;
+}
+
/// The 'mkspellmem' option is changed.
const char *did_set_mkspellmem(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -1786,21 +1464,6 @@ int expand_set_mouse(optexpand_T *args, int *numMatches, char ***matches)
return expand_set_opt_listflag(args, MOUSE_ALL, numMatches, matches);
}
-/// The 'mousemodel' option is changed.
-const char *did_set_mousemodel(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_mousem, p_mousem_values, false);
-}
-
-int expand_set_mousemodel(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_mousem_values,
- ARRAY_SIZE(p_mousem_values) - 1,
- numMatches,
- matches);
-}
-
/// Handle setting 'mousescroll'.
/// @return error message, NULL if it's OK.
const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED)
@@ -1866,33 +1529,6 @@ const char *did_set_mousescroll(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_mousescroll(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_mousescroll_values[]) = { "hor:", "ver:", NULL };
- return expand_set_opt_string(args,
- p_mousescroll_values,
- ARRAY_SIZE(p_mousescroll_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'nrformats' option is changed.
-const char *did_set_nrformats(optset_T *args)
-{
- char **varp = (char **)args->os_varp;
-
- return did_set_opt_strings(*varp, p_nf_values, true);
-}
-
-int expand_set_nrformats(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_nf_values,
- ARRAY_SIZE(p_nf_values) - 1,
- numMatches,
- matches);
-}
-
/// One of the '*expr' options is changed:, 'diffexpr', 'foldexpr', 'foldtext',
/// 'formatexpr', 'includeexpr', 'indentexpr', 'patchexpr' and 'charconvert'.
const char *did_set_optexpr(optset_T *args)
@@ -1909,61 +1545,18 @@ const char *did_set_optexpr(optset_T *args)
return NULL;
}
-/// The 'redrawdebug' option is changed.
-const char *did_set_redrawdebug(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_rdb, p_rdb_values, &rdb_flags, true);
-}
-
-/// The 'rightleftcmd' option is changed.
-const char *did_set_rightleftcmd(optset_T *args)
-{
- char **varp = (char **)args->os_varp;
-
- // Currently only "search" is a supported value.
- if (**varp != NUL && strcmp(*varp, "search") != 0) {
- return e_invarg;
- }
-
- return NULL;
-}
-
-int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char ***matches)
-{
- static char *(p_rlc_values[]) = { "search", NULL };
- return expand_set_opt_string(args,
- p_rlc_values,
- ARRAY_SIZE(p_rlc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'rulerformat' option is changed.
const char *did_set_rulerformat(optset_T *args)
{
return did_set_statustabline_rulerformat(args, true, false);
}
-/// The 'scrollopt' option is changed.
-const char *did_set_scrollopt(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_sbo, p_scbopt_values, true);
-}
-
-int expand_set_scrollopt(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_scbopt_values,
- ARRAY_SIZE(p_scbopt_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'selection' option is changed.
const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED)
{
- if (*p_sel == NUL || check_opt_strings(p_sel, p_sel_values, false) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
if (VIsual_active) {
// Visual selection may be drawn differently.
@@ -1972,54 +1565,22 @@ const char *did_set_selection(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_selection(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_sel_values,
- ARRAY_SIZE(p_sel_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'selectmode' option is changed.
-const char *did_set_selectmode(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_slm, p_slm_values, true);
-}
-
-int expand_set_selectmode(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_slm_values,
- ARRAY_SIZE(p_slm_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'sessionoptions' option is changed.
const char *did_set_sessionoptions(optset_T *args)
{
- if (opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, true) != OK) {
- return e_invarg;
+ const char *errmsg = did_set_str_generic(args);
+ if (errmsg != NULL) {
+ return errmsg;
}
- if ((ssop_flags & SSOP_CURDIR) && (ssop_flags & SSOP_SESDIR)) {
+ if ((ssop_flags & kOptSsopFlagCurdir) && (ssop_flags & kOptSsopFlagSesdir)) {
// Don't allow both "sesdir" and "curdir".
const char *oldval = args->os_oldval.string.data;
- opt_strings_flags(oldval, p_ssop_values, &ssop_flags, true);
+ opt_strings_flags(oldval, opt_ssop_values, &ssop_flags, true);
return e_invarg;
}
return NULL;
}
-int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ssop_values,
- ARRAY_SIZE(p_ssop_values) - 1,
- numMatches,
- matches);
-}
-
const char *did_set_shada(optset_T *args)
{
char *errbuf = args->os_errbuf;
@@ -2099,7 +1660,7 @@ const char *did_set_showbreak(optset_T *args)
/// The 'showcmdloc' option is changed.
const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED)
{
- const char *errmsg = did_set_opt_strings(p_sloc, p_sloc_values, false);
+ const char *errmsg = did_set_str_generic(args);
if (errmsg == NULL) {
comp_col();
@@ -2108,15 +1669,6 @@ const char *did_set_showcmdloc(optset_T *args FUNC_ATTR_UNUSED)
return errmsg;
}
-int expand_set_showcmdloc(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_sloc_values,
- ARRAY_SIZE(p_sloc_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'signcolumn' option is changed.
const char *did_set_signcolumn(optset_T *args)
{
@@ -2134,15 +1686,6 @@ const char *did_set_signcolumn(optset_T *args)
return NULL;
}
-int expand_set_signcolumn(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_scl_values,
- ARRAY_SIZE(p_scl_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'spellcapcheck' option is changed.
const char *did_set_spellcapcheck(optset_T *args)
{
@@ -2185,25 +1728,16 @@ const char *did_set_spelloptions(optset_T *args)
const char *val = args->os_newval.string.data;
if (!(opt_flags & OPT_LOCAL)
- && opt_strings_flags(val, p_spo_values, &spo_flags, true) != OK) {
+ && opt_strings_flags(val, opt_spo_values, &spo_flags, true) != OK) {
return e_invarg;
}
if (!(opt_flags & OPT_GLOBAL)
- && opt_strings_flags(val, p_spo_values, &win->w_s->b_p_spo_flags, true) != OK) {
+ && opt_strings_flags(val, opt_spo_values, &win->w_s->b_p_spo_flags, true) != OK) {
return e_invarg;
}
return NULL;
}
-int expand_set_spelloptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_spo_values,
- ARRAY_SIZE(p_spo_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'spellsuggest' option is changed.
const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED)
{
@@ -2213,30 +1747,6 @@ const char *did_set_spellsuggest(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_sps_values,
- ARRAY_SIZE(p_sps_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'splitkeep' option is changed.
-const char *did_set_splitkeep(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_strings(p_spk, p_spk_values, false);
-}
-
-int expand_set_splitkeep(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_spk_values,
- ARRAY_SIZE(p_spk_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'statuscolumn' option is changed.
const char *did_set_statuscolumn(optset_T *args)
{
@@ -2275,7 +1785,11 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf
if (wid && *s == '(' && (errmsg = check_stl_option(p_ruf)) == NULL) {
ru_wid = wid;
} else {
- errmsg = check_stl_option(p_ruf);
+ // Validate the flags in 'rulerformat' only if it doesn't point to
+ // a custom function ("%!" flag).
+ if ((*varp)[1] != '!') {
+ errmsg = check_stl_option(p_ruf);
+ }
}
} else if (rulerformat || s[0] != '%' || s[1] != '!') {
// check 'statusline', 'winbar', 'tabline' or 'statuscolumn'
@@ -2288,36 +1802,6 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf
return errmsg;
}
-/// The 'switchbuf' option is changed.
-const char *did_set_switchbuf(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_swb, p_swb_values, &swb_flags, true);
-}
-
-int expand_set_switchbuf(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_swb_values,
- ARRAY_SIZE(p_swb_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'tabclose' option is changed.
-const char *did_set_tabclose(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_tcl, p_tcl_values, &tcl_flags, true);
-}
-
-int expand_set_tabclose(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_tcl_values,
- ARRAY_SIZE(p_tcl_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'tabline' option is changed.
const char *did_set_tabline(optset_T *args)
{
@@ -2344,37 +1828,12 @@ const char *did_set_tagcase(optset_T *args)
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) {
+ } else if (opt_strings_flags(p, opt_tc_values, flags, false) != OK) {
return e_invarg;
}
return NULL;
}
-int expand_set_tagcase(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_tc_values,
- ARRAY_SIZE(p_tc_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'termpastefilter' option is changed.
-const char *did_set_termpastefilter(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_tpf, p_tpf_values, &tpf_flags, true);
-}
-
-int expand_set_termpastefilter(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_tpf_values,
- ARRAY_SIZE(p_tpf_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'titlestring' or the 'iconstring' option is changed.
static const char *did_set_titleiconstring(optset_T *args, int flagval)
{
@@ -2471,12 +1930,6 @@ const char *did_set_verbosefile(optset_T *args)
return NULL;
}
-/// The 'viewoptions' option is changed.
-const char *did_set_viewoptions(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_vop, p_ssop_values, &vop_flags, true);
-}
-
/// The 'virtualedit' option is changed.
const char *did_set_virtualedit(optset_T *args)
{
@@ -2494,7 +1947,7 @@ const char *did_set_virtualedit(optset_T *args)
// make the local value empty: use the global value
*flags = 0;
} else {
- if (opt_strings_flags(ve, p_ve_values, flags, true) != OK) {
+ if (opt_strings_flags(ve, opt_ve_values, flags, true) != OK) {
return e_invarg;
} else if (strcmp(ve, args->os_oldval.string.data) != 0) {
// Recompute cursor position in case the new 've' setting
@@ -2506,15 +1959,6 @@ const char *did_set_virtualedit(optset_T *args)
return NULL;
}
-int expand_set_virtualedit(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_ve_values,
- ARRAY_SIZE(p_ve_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'whichwrap' option is changed.
const char *did_set_whichwrap(optset_T *args)
{
@@ -2539,48 +1983,6 @@ const char *did_set_wildmode(optset_T *args FUNC_ATTR_UNUSED)
return NULL;
}
-int expand_set_wildmode(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_wim_values,
- ARRAY_SIZE(p_wim_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'wildoptions' option is changed.
-const char *did_set_wildoptions(optset_T *args FUNC_ATTR_UNUSED)
-{
- return did_set_opt_flags(p_wop, p_wop_values, &wop_flags, true);
-}
-
-int expand_set_wildoptions(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_wop_values,
- ARRAY_SIZE(p_wop_values) - 1,
- numMatches,
- matches);
-}
-
-/// The 'winaltkeys' option is changed.
-const char *did_set_winaltkeys(optset_T *args FUNC_ATTR_UNUSED)
-{
- if (*p_wak == NUL || check_opt_strings(p_wak, p_wak_values, false) != OK) {
- return e_invarg;
- }
- return NULL;
-}
-
-int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char ***matches)
-{
- return expand_set_opt_string(args,
- p_wak_values,
- ARRAY_SIZE(p_wak_values) - 1,
- numMatches,
- matches);
-}
-
/// The 'winbar' option is changed.
const char *did_set_winbar(optset_T *args)
{
@@ -2603,16 +2005,6 @@ int expand_set_winhighlight(optexpand_T *args, int *numMatches, char ***matches)
return expand_set_opt_generic(args, get_highlight_name, numMatches, matches);
}
-/// 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.
///
@@ -2621,11 +2013,14 @@ static int check_opt_strings(char *val, char **values, int list)
/// @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(const char *val, char **values, unsigned *flagp, bool list)
+static int opt_strings_flags(const char *val, const char **values, unsigned *flagp, bool list)
{
unsigned new_flags = 0;
- while (*val) {
+ // If not list and val is empty, then force one iteration of the while loop
+ bool iter_one = (*val == NUL) && !list;
+
+ while (*val || iter_one) {
for (unsigned i = 0;; i++) {
if (values[i] == NULL) { // val not found in values[]
return FAIL;
@@ -2640,6 +2035,9 @@ static int opt_strings_flags(const char *val, char **values, unsigned *flagp, bo
break; // check next item in val list
}
}
+ if (iter_one) {
+ break;
+ }
}
if (flagp != NULL) {
*flagp = new_flags;
@@ -2651,7 +2049,7 @@ static int opt_strings_flags(const char *val, char **values, unsigned *flagp, bo
/// @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);
+ return opt_strings_flags(p, opt_ff_values, NULL, false);
}
static const char e_conflicts_with_value_of_listchars[]
@@ -2689,49 +2087,54 @@ static schar_T get_encoded_char_adv(const char **p)
}
struct chars_tab {
- schar_T *cp; ///< char value
- const char *name; ///< char id
- const char *def; ///< default value
- const char *fallback; ///< default value when "def" isn't single-width
+ schar_T *cp; ///< char value
+ String name; ///< char id
+ const char *def; ///< default value
+ const char *fallback; ///< default value when "def" isn't single-width
};
+#define CHARSTAB_ENTRY(cp, name, def, fallback) \
+ { (cp), { name, STRLEN_LITERAL(name) }, def, fallback }
+
static fcs_chars_T fcs_chars;
static const struct chars_tab fcs_tab[] = {
- { &fcs_chars.stl, "stl", " ", NULL },
- { &fcs_chars.stlnc, "stlnc", " ", NULL },
- { &fcs_chars.wbr, "wbr", " ", NULL },
- { &fcs_chars.horiz, "horiz", "─", "-" },
- { &fcs_chars.horizup, "horizup", "â”´", "-" },
- { &fcs_chars.horizdown, "horizdown", "┬", "-" },
- { &fcs_chars.vert, "vert", "│", "|" },
- { &fcs_chars.vertleft, "vertleft", "┤", "|" },
- { &fcs_chars.vertright, "vertright", "├", "|" },
- { &fcs_chars.verthoriz, "verthoriz", "┼", "+" },
- { &fcs_chars.fold, "fold", "·", "-" },
- { &fcs_chars.foldopen, "foldopen", "-", NULL },
- { &fcs_chars.foldclosed, "foldclose", "+", NULL },
- { &fcs_chars.foldsep, "foldsep", "│", "|" },
- { &fcs_chars.diff, "diff", "-", NULL },
- { &fcs_chars.msgsep, "msgsep", " ", NULL },
- { &fcs_chars.eob, "eob", "~", NULL },
- { &fcs_chars.lastline, "lastline", "@", NULL },
+ CHARSTAB_ENTRY(&fcs_chars.stl, "stl", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.stlnc, "stlnc", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.wbr, "wbr", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.horiz, "horiz", "─", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.horizup, "horizup", "â”´", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.horizdown, "horizdown", "┬", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.vert, "vert", "│", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.vertleft, "vertleft", "┤", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.vertright, "vertright", "├", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.verthoriz, "verthoriz", "┼", "+"),
+ CHARSTAB_ENTRY(&fcs_chars.fold, "fold", "·", "-"),
+ CHARSTAB_ENTRY(&fcs_chars.foldopen, "foldopen", "-", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.foldclosed, "foldclose", "+", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.foldsep, "foldsep", "│", "|"),
+ CHARSTAB_ENTRY(&fcs_chars.diff, "diff", "-", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.msgsep, "msgsep", " ", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.eob, "eob", "~", NULL),
+ CHARSTAB_ENTRY(&fcs_chars.lastline, "lastline", "@", NULL),
};
static lcs_chars_T lcs_chars;
static const struct chars_tab lcs_tab[] = {
- { &lcs_chars.eol, "eol", NULL, NULL },
- { &lcs_chars.ext, "extends", NULL, NULL },
- { &lcs_chars.nbsp, "nbsp", NULL, NULL },
- { &lcs_chars.prec, "precedes", NULL, NULL },
- { &lcs_chars.space, "space", NULL, NULL },
- { &lcs_chars.tab2, "tab", NULL, NULL },
- { &lcs_chars.lead, "lead", NULL, NULL },
- { &lcs_chars.trail, "trail", NULL, NULL },
- { &lcs_chars.conceal, "conceal", NULL, NULL },
- { NULL, "multispace", NULL, NULL },
- { NULL, "leadmultispace", NULL, NULL },
+ CHARSTAB_ENTRY(&lcs_chars.eol, "eol", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.ext, "extends", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.nbsp, "nbsp", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.prec, "precedes", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.space, "space", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.tab2, "tab", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.lead, "lead", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.trail, "trail", NULL, NULL),
+ CHARSTAB_ENTRY(&lcs_chars.conceal, "conceal", NULL, NULL),
+ CHARSTAB_ENTRY(NULL, "multispace", NULL, NULL),
+ CHARSTAB_ENTRY(NULL, "leadmultispace", NULL, NULL),
};
+#undef CHARSTAB_ENTRY
+
static char *field_value_err(char *errbuf, size_t errbuflen, const char *fmt, const char *field)
{
if (errbuf == NULL) {
@@ -2812,13 +2215,13 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
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] == ':')) {
+ if (!(strncmp(p, tab[i].name.data,
+ tab[i].name.size) == 0 && p[tab[i].name.size] == ':')) {
continue;
}
- if (what == kListchars && strcmp(tab[i].name, "multispace") == 0) {
- const char *s = p + len + 1;
+ const char *s = p + tab[i].name.size + 1;
+ if (what == kListchars && strcmp(tab[i].name.data, "multispace") == 0) {
if (round == 0) {
// Get length of lcs-multispace string in the first round
last_multispace = p;
@@ -2828,7 +2231,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
if (c1 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
multispace_len++;
}
@@ -2836,7 +2239,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
// lcs-multispace cannot be an empty string
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
p = s;
} else {
@@ -2852,8 +2255,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
break;
}
- if (what == kListchars && strcmp(tab[i].name, "leadmultispace") == 0) {
- const char *s = p + len + 1;
+ if (what == kListchars && strcmp(tab[i].name.data, "leadmultispace") == 0) {
if (round == 0) {
// get length of lcs-leadmultispace string in first round
last_lmultispace = p;
@@ -2863,7 +2265,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
if (c1 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
lead_multispace_len++;
}
@@ -2871,7 +2273,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
// lcs-leadmultispace cannot be an empty string
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
p = s;
} else {
@@ -2887,17 +2289,16 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
break;
}
- const char *s = p + len + 1;
if (*s == NUL) {
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
schar_T c1 = get_encoded_char_adv(&s);
if (c1 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
schar_T c2 = 0;
schar_T c3 = 0;
@@ -2905,20 +2306,20 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
if (*s == NUL) {
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
c2 = get_encoded_char_adv(&s);
if (c2 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
if (!(*s == ',' || *s == NUL)) {
c3 = get_encoded_char_adv(&s);
if (c3 == 0) {
return field_value_err(errbuf, errbuflen,
e_wrong_character_width_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
}
}
@@ -2938,7 +2339,7 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
} else {
return field_value_err(errbuf, errbuflen,
e_wrong_number_of_characters_for_field_str,
- tab[i].name);
+ tab[i].name.data);
}
}
@@ -2969,22 +2370,22 @@ const char *set_chars_option(win_T *wp, const char *value, CharsOption what, boo
/// 'fillchars' option.
char *get_fillchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
- if (idx >= (int)ARRAY_SIZE(fcs_tab)) {
+ if (idx < 0 || idx >= (int)ARRAY_SIZE(fcs_tab)) {
return NULL;
}
- return (char *)fcs_tab[idx].name;
+ return fcs_tab[idx].name.data;
}
/// Function given to ExpandGeneric() to obtain possible arguments of the
/// 'listchars' option.
char *get_listchars_name(expand_T *xp FUNC_ATTR_UNUSED, int idx)
{
- if (idx >= (int)ARRAY_SIZE(lcs_tab)) {
+ if (idx < 0 || idx >= (int)ARRAY_SIZE(lcs_tab)) {
return NULL;
}
- return (char *)lcs_tab[idx].name;
+ return lcs_tab[idx].name.data;
}
/// Check all global and local values of 'listchars' and 'fillchars'.
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index ccf6c9554a..3126881266 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -15,7 +15,6 @@
#include "nvim/cmdexpand.h"
#include "nvim/cmdexpand_defs.h"
#include "nvim/eval.h"
-#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
@@ -581,7 +580,7 @@ void expand_env(char *src, char *dst, int dstlen)
/// @param esc Escape spaces in expanded variables
/// @param one `srcp` is a single filename
/// @param prefix Start again after this (can be NULL)
-void expand_env_esc(char *restrict srcp, char *restrict dst, int dstlen, bool esc, bool one,
+void expand_env_esc(const char *restrict srcp, char *restrict dst, int dstlen, bool esc, bool one,
char *prefix)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index 1981d0dfd4..1b202fdc59 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -8,16 +8,13 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
-#include <stdint.h>
+#include <string.h>
#include <uv.h>
#include "auto/config.h"
-#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
#include "nvim/memory.h"
-#include "nvim/message.h"
#include "nvim/os/fileio.h"
#include "nvim/os/fs.h"
#include "nvim/os/os_defs.h"
@@ -28,7 +25,7 @@
#endif
#ifdef INCLUDE_GENERATED_DECLARATIONS
-# include "os/fileio.c.generated.h"
+# include "os/fileio.c.generated.h" // IWYU pragma: keep
#endif
/// Open file
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index d0da37b8e7..451994241d 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -370,8 +370,8 @@ static bool is_executable_in_path(const char *name, char **abspath)
char *path = xstrdup(path_env);
#endif
- size_t buf_len = strlen(name) + strlen(path) + 2;
- char *buf = xmalloc(buf_len);
+ const size_t bufsize = strlen(name) + strlen(path) + 2;
+ char *buf = xmalloc(bufsize);
// Walk through all entries in $PATH to check if "name" exists there and
// is an executable file.
@@ -382,7 +382,7 @@ static bool is_executable_in_path(const char *name, char **abspath)
// Combine the $PATH segment with `name`.
xmemcpyz(buf, p, (size_t)(e - p));
- (void)append_path(buf, name, buf_len);
+ (void)append_path(buf, name, bufsize);
#ifdef MSWIN
if (is_executable_ext(buf, abspath)) {
diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c
index 2d17581bac..3259fb500b 100644
--- a/src/nvim/os/input.c
+++ b/src/nvim/os/input.c
@@ -1,4 +1,5 @@
#include <assert.h>
+#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
@@ -13,7 +14,6 @@
#include "nvim/event/loop.h"
#include "nvim/event/multiqueue.h"
#include "nvim/event/rstream.h"
-#include "nvim/event/stream.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
@@ -29,6 +29,7 @@
#include "nvim/profile.h"
#include "nvim/state.h"
#include "nvim/state_defs.h"
+#include "nvim/types_defs.h"
#define READ_BUFFER_SIZE 0xfff
#define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN)
@@ -402,6 +403,7 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
if (type != KS_EXTRA
|| !((mouse_code >= KE_LEFTMOUSE && mouse_code <= KE_RIGHTRELEASE)
+ || (mouse_code >= KE_X1MOUSE && mouse_code <= KE_X2RELEASE)
|| (mouse_code >= KE_MOUSEDOWN && mouse_code <= KE_MOUSERIGHT)
|| mouse_code == KE_MOUSEMOVE)) {
return bufsize;
diff --git a/src/nvim/os/nvim.rc b/src/nvim/os/nvim.rc
new file mode 100644
index 0000000000..e838c93c16
--- /dev/null
+++ b/src/nvim/os/nvim.rc
@@ -0,0 +1,2 @@
+#include "winuser.h"
+2 RT_MANIFEST nvim.manifest
diff --git a/src/nvim/os/pty_proc_unix.c b/src/nvim/os/pty_proc_unix.c
index 3bca065d2d..10a464bbc3 100644
--- a/src/nvim/os/pty_proc_unix.c
+++ b/src/nvim/os/pty_proc_unix.c
@@ -30,6 +30,7 @@
#endif
#include "auto/config.h"
+#include "klib/kvec.h"
#include "nvim/eval/typval.h"
#include "nvim/event/defs.h"
#include "nvim/event/loop.h"
diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c
index 81b75dc4d3..d60d0b3e55 100644
--- a/src/nvim/os/shell.c
+++ b/src/nvim/os/shell.c
@@ -700,6 +700,7 @@ int os_call_shell(char *cmd, int opts, char *extra_args)
}
if (!emsg_silent && exitcode != 0 && !(opts & kShellOptSilent)) {
+ msg_ext_set_kind("shell_ret");
msg_puts(_("\nshell returned "));
msg_outnum(exitcode);
msg_putchar('\n');
@@ -1067,7 +1068,7 @@ static void out_data_ring(const char *output, size_t size)
}
if (output == NULL && size == SIZE_MAX) { // Print mode
- out_data_append_to_screen(last_skipped, &last_skipped_len, true);
+ out_data_append_to_screen(last_skipped, &last_skipped_len, STDOUT_FILENO, true);
return;
}
@@ -1095,14 +1096,15 @@ static void out_data_ring(const char *output, size_t size)
/// @param output Data to append to screen lines.
/// @param count Size of data.
/// @param eof If true, there will be no more data output.
-static void out_data_append_to_screen(const char *output, size_t *count, bool eof)
+static void out_data_append_to_screen(const char *output, size_t *count, int fd, bool eof)
FUNC_ATTR_NONNULL_ALL
{
const char *p = output;
const char *end = output + *count;
+ msg_ext_set_kind(fd == STDERR_FILENO ? "shell_err" : "shell_out");
while (p < end) {
if (*p == '\n' || *p == '\r' || *p == TAB || *p == BELL) {
- msg_putchar_hl((uint8_t)(*p), 0);
+ msg_putchar_hl((uint8_t)(*p), fd == STDERR_FILENO ? HLF_E : 0);
p++;
} else {
// Note: this is not 100% precise:
@@ -1118,7 +1120,7 @@ static void out_data_append_to_screen(const char *output, size_t *count, bool eo
goto end;
}
- msg_outtrans_len(p, i, 0, false);
+ msg_outtrans_len(p, i, fd == STDERR_FILENO ? HLF_E : 0, false);
p += i;
}
}
@@ -1133,7 +1135,7 @@ static size_t out_data_cb(RStream *stream, const char *ptr, size_t count, void *
// Save the skipped output. If it is the final chunk, we display it later.
out_data_ring(ptr, count);
} else if (count > 0) {
- out_data_append_to_screen(ptr, &count, eof);
+ out_data_append_to_screen(ptr, &count, stream->s.fd, eof);
}
return count;
@@ -1206,10 +1208,11 @@ static void read_input(StringBuilder *buf)
size_t len = 0;
linenr_T lnum = curbuf->b_op_start.lnum;
char *lp = ml_get(lnum);
+ size_t lplen = (size_t)ml_get_len(lnum);
while (true) {
- size_t l = strlen(lp + written);
- if (l == 0) {
+ lplen -= written;
+ if (lplen == 0) {
len = 0;
} else if (lp[written] == NL) {
// NL -> NUL translation
@@ -1217,11 +1220,11 @@ static void read_input(StringBuilder *buf)
kv_push(*buf, NUL);
} else {
char *s = vim_strchr(lp + written, NL);
- len = s == NULL ? l : (size_t)(s - (lp + written));
+ len = s == NULL ? lplen : (size_t)(s - (lp + written));
kv_concat_len(*buf, lp + written, len);
}
- if (len == l) {
+ if (len == lplen) {
// Finished a line, add a NL, unless this line should not have one.
if (lnum != curbuf->b_op_end.lnum
|| (!curbuf->b_p_bin && curbuf->b_p_fixeol)
@@ -1234,6 +1237,7 @@ static void read_input(StringBuilder *buf)
break;
}
lp = ml_get(lnum);
+ lplen = (size_t)ml_get_len(lnum);
written = 0;
} else if (len > 0) {
written += len;
diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c
index ecedf144e5..98d13fc9d1 100644
--- a/src/nvim/os/signal.c
+++ b/src/nvim/os/signal.c
@@ -187,8 +187,7 @@ static void on_signal(SignalWatcher *handle, int signum, void *data)
switch (signum) {
#ifdef SIGPWR
case SIGPWR:
- // Signal of a power failure(eg batteries low), flush the swap files to
- // be safe
+ // Signal of a power failure (eg batteries low), flush the swap files to be safe
ml_sync_all(false, false, true);
break;
#endif
diff --git a/src/nvim/path.c b/src/nvim/path.c
index 80890acb7d..3df77571a1 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -8,6 +8,7 @@
#include "auto/config.h"
#include "nvim/ascii_defs.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/eval.h"
@@ -1874,11 +1875,12 @@ void path_fix_case(char *name)
return;
}
+ size_t taillen = strlen(tail);
const char *entry;
while ((entry = os_scandir_next(&dir))) {
// Only accept names that differ in case and are the same byte
// length. TODO: accept different length name.
- if (STRICMP(tail, entry) == 0 && strlen(tail) == strlen(entry)) {
+ if (STRICMP(tail, entry) == 0 && taillen == strlen(entry)) {
char newname[MAXPATHL + 1];
// Verify the inode is equal.
@@ -2071,7 +2073,7 @@ char *path_shorten_fname(char *full_path, char *dir_name)
/// @param[in] flags Flags passed to expand_wildcards().
///
/// @returns OK when *file is set to allocated array of matches
-/// and *num_file(can be zero) to the number of matches.
+/// and *num_file (can be zero) to the number of matches.
/// If FAIL is returned, *num_file and *file are either
/// unchanged or *num_file is set to 0 and *file is set
/// to NULL or points to "".
@@ -2269,14 +2271,12 @@ int append_path(char *path, const char *to_append, size_t max_len)
// Combine the path segments, separated by a slash.
if (current_length > 0 && !vim_ispathsep_nocolon(path[current_length - 1])) {
- current_length += 1; // Count the trailing slash.
-
// +1 for the NUL at the end.
- if (current_length + 1 > max_len) {
- return FAIL;
+ if (current_length + STRLEN_LITERAL(PATHSEPSTR) + 1 > max_len) {
+ return FAIL; // No space for trailing slash.
}
-
- xstrlcat(path, PATHSEPSTR, max_len);
+ xstrlcpy(path + current_length, PATHSEPSTR, max_len - current_length);
+ current_length += STRLEN_LITERAL(PATHSEPSTR);
}
// +1 for the NUL at the end.
@@ -2284,7 +2284,7 @@ int append_path(char *path, const char *to_append, size_t max_len)
return FAIL;
}
- xstrlcat(path, to_append, max_len);
+ xstrlcpy(path + current_length, to_append, max_len - current_length);
return OK;
}
diff --git a/src/nvim/po/af.po b/src/nvim/po/af.po
index 64acd93f57..3d537a51f2 100644
--- a/src/nvim/po/af.po
+++ b/src/nvim/po/af.po
@@ -1353,8 +1353,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Patrone kan nie deur letters afgebaken word nie"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "vervang met %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "vervang met %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up (^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Onderbreek) "
diff --git a/src/nvim/po/ca.po b/src/nvim/po/ca.po
index 44975d9161..2162f13592 100644
--- a/src/nvim/po/ca.po
+++ b/src/nvim/po/ca.po
@@ -1241,8 +1241,8 @@ msgstr "E146: Les expressions regulars no poden estar delimitades per lletres"
# «amb» o «per» + tecles. eac
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "substituir amb %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "substituir amb %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/cs.cp1250.po b/src/nvim/po/cs.cp1250.po
index 43b6c82960..4939a150ac 100644
--- a/src/nvim/po/cs.cp1250.po
+++ b/src/nvim/po/cs.cp1250.po
@@ -1255,8 +1255,8 @@ msgstr "E146: Regulární výrazy nesmí být oddìleny písmeny"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradit za %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradit za %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/cs.po b/src/nvim/po/cs.po
index 7df69e061f..9cb552c6de 100644
--- a/src/nvim/po/cs.po
+++ b/src/nvim/po/cs.po
@@ -1255,8 +1255,8 @@ msgstr "E146: Regulární výrazy nesmí být oddìleny písmeny"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradit za %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradit za %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po
index c65477bf13..ba7ffa9f7b 100644
--- a/src/nvim/po/da.po
+++ b/src/nvim/po/da.po
@@ -984,8 +984,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Regulære udtryk kan ikke afgrænses af bogstaver"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "erstat med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "erstat med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Afbrudt) "
diff --git a/src/nvim/po/de.po b/src/nvim/po/de.po
index 0d1195eecf..0eb72b9565 100644
--- a/src/nvim/po/de.po
+++ b/src/nvim/po/de.po
@@ -666,8 +666,8 @@ msgstr "E146: Reguläre Ausdrücke können nicht durch Buchstaben begrenzt werden"
#: ../ex_cmds.c:3958
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "ersetze durch %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "ersetze durch %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4373
msgid "(Interrupted) "
diff --git a/src/nvim/po/en_GB.po b/src/nvim/po/en_GB.po
index 7b849d4e68..f93dcad206 100644
--- a/src/nvim/po/en_GB.po
+++ b/src/nvim/po/en_GB.po
@@ -1199,8 +1199,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Regular expressions cannot be delimited by letters"
#: ../ex_cmds.c:3964
-#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
+#, c-format"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgstr ""
#: ../ex_cmds.c:4379
diff --git a/src/nvim/po/eo.po b/src/nvim/po/eo.po
index 5cd18ad24b..4033ff8a43 100644
--- a/src/nvim/po/eo.po
+++ b/src/nvim/po/eo.po
@@ -950,8 +950,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Ne eblas limigi regulesprimon per literoj"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "ĉu anstataŭigi per %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "ĉu anstataŭigi per %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Interrompita) "
diff --git a/src/nvim/po/es.po b/src/nvim/po/es.po
index 292ca15264..b67c275bf3 100644
--- a/src/nvim/po/es.po
+++ b/src/nvim/po/es.po
@@ -1243,8 +1243,8 @@ msgstr "E146: Las expresiones regulares no se pueden delimitar con letras"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "¿Reemplazar con %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "¿Reemplazar con %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/fi.po b/src/nvim/po/fi.po
index c9cad6cc69..e1700d61ff 100644
--- a/src/nvim/po/fi.po
+++ b/src/nvim/po/fi.po
@@ -1521,8 +1521,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Säännöllistä ilmausta ei voi rajata kirjaimilla"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "korvaa kohteella %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "korvaa kohteella %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Keskeytetty)"
diff --git a/src/nvim/po/fr.po b/src/nvim/po/fr.po
index 92d2ad215f..0dae00f3a3 100644
--- a/src/nvim/po/fr.po
+++ b/src/nvim/po/fr.po
@@ -855,8 +855,8 @@ msgstr ""
"lettres"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "remplacer par %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "remplacer par %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Interrompu) "
diff --git a/src/nvim/po/ga.po b/src/nvim/po/ga.po
index 7a5893b5f2..902bf9fae1 100644
--- a/src/nvim/po/ga.po
+++ b/src/nvim/po/ga.po
@@ -972,8 +972,8 @@ msgstr ""
"E146: Ní cheadaítear litreacha mar theormharcóirí ar shloinn ionadaíochta"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "cuir %s ina ionad (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "cuir %s? ina ionad (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Idirbhriste) "
diff --git a/src/nvim/po/it.po b/src/nvim/po/it.po
index d02052ec39..5caaa3c1f0 100644
--- a/src/nvim/po/it.po
+++ b/src/nvim/po/it.po
@@ -1227,8 +1227,8 @@ msgstr "E146: Le espressioni regolari non possono essere delimitate da lettere"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "sostituire con %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "sostituire con %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/ja.euc-jp.po b/src/nvim/po/ja.euc-jp.po
index 78f927c21d..02bd36efbb 100644
--- a/src/nvim/po/ja.euc-jp.po
+++ b/src/nvim/po/ja.euc-jp.po
@@ -867,8 +867,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Àµµ¬É½¸½¤Ïʸ»ú¤Ç¶èÀڤ뤳¤È¤¬¤Ç¤­¤Þ¤»¤ó"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s ¤ËÃÖ´¹¤·¤Þ¤¹¤«? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s ¤ËÃÖ´¹¤·¤Þ¤¹¤«? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(³ä¹þ¤Þ¤ì¤Þ¤·¤¿) "
diff --git a/src/nvim/po/ja.po b/src/nvim/po/ja.po
index 1a493c0336..8d417d222a 100644
--- a/src/nvim/po/ja.po
+++ b/src/nvim/po/ja.po
@@ -2998,8 +2998,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: æ­£è¦è¡¨ç¾ã¯æ–‡å­—ã§åŒºåˆ‡ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s ã«ç½®æ›ã—ã¾ã™ã‹? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s ã«ç½®æ›ã—ã¾ã™ã‹? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(割込ã¾ã‚Œã¾ã—ãŸ) "
diff --git a/src/nvim/po/ko.UTF-8.po b/src/nvim/po/ko.UTF-8.po
index 03dff518f3..66463713d7 100644
--- a/src/nvim/po/ko.UTF-8.po
+++ b/src/nvim/po/ko.UTF-8.po
@@ -1221,8 +1221,8 @@ msgstr "E146: 정규표현ì‹ì€ 글ìžë¡œ êµ¬ë¶„ë  ìˆ˜ 없습니다"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s(으)로 바꿈 (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s(으)로 바꿈? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/nb.po b/src/nvim/po/nb.po
index d512789246..6f10dcc232 100644
--- a/src/nvim/po/nb.po
+++ b/src/nvim/po/nb.po
@@ -1236,8 +1236,8 @@ msgstr "E146: Regulære uttrykk kan ikke bli adskilt av bokstaver"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "Erstatt med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "Erstatt med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/nl.po b/src/nvim/po/nl.po
index f6ce5ddc9f..75d516a67d 100644
--- a/src/nvim/po/nl.po
+++ b/src/nvim/po/nl.po
@@ -1223,8 +1223,8 @@ msgstr "E146: reguliere expressies kunnen niet begrensd worden door letters"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "vervang door %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "vervang door %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/no.po b/src/nvim/po/no.po
index d512789246..6f10dcc232 100644
--- a/src/nvim/po/no.po
+++ b/src/nvim/po/no.po
@@ -1236,8 +1236,8 @@ msgstr "E146: Regulære uttrykk kan ikke bli adskilt av bokstaver"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "Erstatt med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "Erstatt med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/pl.UTF-8.po b/src/nvim/po/pl.UTF-8.po
index b7200a7ba8..521edb383d 100644
--- a/src/nvim/po/pl.UTF-8.po
+++ b/src/nvim/po/pl.UTF-8.po
@@ -1207,8 +1207,8 @@ msgstr "E146: Wzorce regularne nie mogą być rozgraniczane literami"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "zamień na %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "zamień na %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/pt_BR.po b/src/nvim/po/pt_BR.po
index 3f2f87991c..4e7225fc9c 100644
--- a/src/nvim/po/pt_BR.po
+++ b/src/nvim/po/pt_BR.po
@@ -4216,8 +4216,8 @@ msgstr "E146: Expressões regulares não podem ser delimitadas por letras"
#: ../ex_cmds.c:3958
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "substituir por %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "substituir por %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4373
msgid "(Interrupted) "
diff --git a/src/nvim/po/ru.po b/src/nvim/po/ru.po
index 64c18f890c..90dc5b2d8e 100644
--- a/src/nvim/po/ru.po
+++ b/src/nvim/po/ru.po
@@ -1215,8 +1215,8 @@ msgstr "E146: РегулÑрные Ð²Ñ‹Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð½Ðµ могут разде
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "заменить на %s? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "заменить на %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/sk.cp1250.po b/src/nvim/po/sk.cp1250.po
index 9510a357f0..394b5aff01 100644
--- a/src/nvim/po/sk.cp1250.po
+++ b/src/nvim/po/sk.cp1250.po
@@ -1224,8 +1224,8 @@ msgstr "E146: Regulárne výrazy nesmú by oddelené písmenami"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradi %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradi %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/sk.po b/src/nvim/po/sk.po
index 21791e0061..79ed3e9539 100644
--- a/src/nvim/po/sk.po
+++ b/src/nvim/po/sk.po
@@ -1224,8 +1224,8 @@ msgstr "E146: Regulárne výrazy nesmú by» oddelené písmenami"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "nahradi» %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "nahradi» %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/po/sr.po b/src/nvim/po/sr.po
index 1cbb37eeb1..79b6747921 100644
--- a/src/nvim/po/sr.po
+++ b/src/nvim/po/sr.po
@@ -1201,8 +1201,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Регуларни изрази не могу да Ñе раздвајају Ñловима"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "заменити Ñа %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "заменити Ñа %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Прекинуто)"
diff --git a/src/nvim/po/sv.po b/src/nvim/po/sv.po
index 89a7717746..285dadb595 100644
--- a/src/nvim/po/sv.po
+++ b/src/nvim/po/sv.po
@@ -2628,8 +2628,8 @@ msgstr "E146: Reguljära uttryck kan inte vara åtskilda av bokstäver"
#: ../ex_cmds.c:3958
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "ersätt med %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "ersätt med %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4373
msgid "(Interrupted) "
diff --git a/src/nvim/po/tr.po b/src/nvim/po/tr.po
index 5b9a549cb2..ea41cf56b3 100644
--- a/src/nvim/po/tr.po
+++ b/src/nvim/po/tr.po
@@ -1867,8 +1867,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Düzenli ifadeler harflerle sınırlandırılamaz"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "%s ile deÄŸiÅŸtir (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "%s ile deÄŸiÅŸtir? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Yarıda kesildi) "
diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po
index 6f2f90ad2c..cb422cc2b6 100644
--- a/src/nvim/po/uk.po
+++ b/src/nvim/po/uk.po
@@ -3008,8 +3008,8 @@ msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: РегулÑрні вирази не можна розділÑти літерами"
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "Замінити на %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "Замінити на %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
msgid "(Interrupted) "
msgstr "(Перервано) "
diff --git a/src/nvim/po/vi.po b/src/nvim/po/vi.po
index 3e88ac446c..b976d39647 100644
--- a/src/nvim/po/vi.po
+++ b/src/nvim/po/vi.po
@@ -4,219 +4,147 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Vim 6.3 \n"
+"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-05-26 14:21+0200\n"
-"PO-Revision-Date: 2005-02-30 21:37+0400\n"
-"Last-Translator: Phan Vinh Thinh <teppi@vnlinux.org>\n"
+"POT-Creation-Date: 2024-12-03 22:06+0100\n"
+"PO-Revision-Date: 2024-12-03 22:07+0100\n"
+"Last-Translator: Phạm Bình An <111893501+brianhuster@users.noreply.github.com>\n"
"Language-Team: Phan Vinh Thinh <teppi@vnlinux.org>\n"
-"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: ../api/private/helpers.c:201
-#, fuzzy
-msgid "Unable to get option value"
-msgstr "E258: Không thể trả lá»i cho máy con"
-
-#: ../api/private/helpers.c:204
-msgid "internal error: unknown option type"
-msgstr ""
-
-#: ../buffer.c:92
-msgid "[Location List]"
-msgstr ""
-
-#: ../buffer.c:93
-msgid "[Quickfix List]"
-msgstr ""
-
-#: ../buffer.c:94
-msgid "E855: Autocommands caused command to abort"
-msgstr ""
-
-#: ../buffer.c:135
msgid "E82: Cannot allocate any buffer, exiting..."
msgstr "E82: Không thể phân chia bộ nhớ thậm chí cho một bộ đệm, thoát..."
-#: ../buffer.c:138
msgid "E83: Cannot allocate buffer, using other one..."
msgstr "E83: Không thể phân chia bộ nhớ cho bộ đệm, sử dụng bộ đệm khác..."
-#: ../buffer.c:763
msgid "E515: No buffers were unloaded"
msgstr "E515: Không có bộ đệm nào được bỠnạp từ bộ nhớ"
-#: ../buffer.c:765
msgid "E516: No buffers were deleted"
msgstr "E516: Không có bộ đệm nào bị xóa"
-#: ../buffer.c:767
msgid "E517: No buffers were wiped out"
msgstr "E517: Không có bộ đệm nào được làm sạch"
-#: ../buffer.c:772
msgid "1 buffer unloaded"
msgstr "1 bộ đệm được bỠnạp từ bộ nhớ"
-#: ../buffer.c:774
#, c-format
msgid "%d buffers unloaded"
msgstr "%d bộ đệm được bỠnạp từ bộ nhớ"
-#: ../buffer.c:777
msgid "1 buffer deleted"
msgstr "1 bộ đệm bị xóa"
-#: ../buffer.c:779
#, c-format
msgid "%d buffers deleted"
msgstr "%d bộ đệm được bỠnạp"
-#: ../buffer.c:782
msgid "1 buffer wiped out"
msgstr "1 bộ đệm được làm sạch"
-#: ../buffer.c:784
#, c-format
msgid "%d buffers wiped out"
msgstr "%d bộ đệm được làm sạch"
-#: ../buffer.c:806
-msgid "E90: Cannot unload last buffer"
-msgstr "E90: Không thể bỠnạp từ bộ nhớ bộ đệm cuối cùng"
-
-#: ../buffer.c:874
msgid "E84: No modified buffer found"
msgstr "E84: Không tìm thấy bộ đệm có thay đổi"
-#. back where we started, didn't find anything.
-#: ../buffer.c:903
msgid "E85: There is no listed buffer"
msgstr "E85: Không có bộ đệm được liệt kê"
-#: ../buffer.c:913
#, c-format
-msgid "E86: Buffer %<PRId64> does not exist"
-msgstr "E86: Bộ đệm %<PRId64> không tồn tại"
+msgid "E86: Buffer %ld does not exist"
+msgstr "E86: Bộ đệm %ld không tồn tại"
-#: ../buffer.c:915
msgid "E87: Cannot go beyond last buffer"
msgstr "E87: Äây là bá»™ đệm cuối cùng"
-#: ../buffer.c:917
msgid "E88: Cannot go before first buffer"
msgstr "E88: Äây là bá»™ đệm đầu tiên"
-#: ../buffer.c:945
#, c-format
-msgid ""
-"E89: No write since last change for buffer %<PRId64> (add ! to override)"
+msgid "E89: No write since last change for buffer %ld (add ! to override)"
msgstr ""
-"E89: Thay đổi trong bộ đệm %<PRId64> chưa được ghi lại (thêm ! để thoát ra "
-"bằng má»i giá)"
+"E89: Thay đổi trong bộ đệm %ld chưa được ghi lại (thêm ! để thoát ra bằng "
+"má»i giá)"
+
+msgid "E90: Cannot unload last buffer"
+msgstr "E90: Không thể bỠnạp từ bộ nhớ bộ đệm cuối cùng"
-#. wrap around (may cause duplicates)
-#: ../buffer.c:1423
msgid "W14: Warning: List of file names overflow"
msgstr "W14: Cảnh báo: Danh sách tên tập tin quá đầy"
-#: ../buffer.c:1555 ../quickfix.c:3361
#, c-format
-msgid "E92: Buffer %<PRId64> not found"
-msgstr "E92: Bộ đệm %<PRId64> không được tìm thấy"
+msgid "E92: Buffer %ld not found"
+msgstr "E92: Bộ đệm %ld không được tìm thấy"
-#: ../buffer.c:1798
#, c-format
msgid "E93: More than one match for %s"
msgstr "E93: Tìm thấy vài tương ứng với %s"
-#: ../buffer.c:1800
#, c-format
msgid "E94: No matching buffer for %s"
msgstr "E94: Không có bộ đệm tương ứng với %s"
-#: ../buffer.c:2161
#, c-format
-msgid "line %<PRId64>"
-msgstr "dòng %<PRId64>"
+msgid "line %ld"
+msgstr "dòng %ld"
-#: ../buffer.c:2233
msgid "E95: Buffer with this name already exists"
msgstr "E95: Äã có bá»™ đệm vá»›i tên như vậy"
-#: ../buffer.c:2498
msgid " [Modified]"
msgstr " [Äã thay đổi]"
-#: ../buffer.c:2501
msgid "[Not edited]"
msgstr "[Chưa soạn thảo]"
-#: ../buffer.c:2504
msgid "[New file]"
msgstr "[Tập tin mới]"
-#: ../buffer.c:2505
msgid "[Read errors]"
msgstr "[Lá»—i Ä‘á»c]"
-#: ../buffer.c:2506 ../buffer.c:3217 ../fileio.c:1807 ../screen.c:4895
-msgid "[RO]"
-msgstr "[Chỉ Ä‘á»c]"
-
-#: ../buffer.c:2507 ../fileio.c:1807
msgid "[readonly]"
msgstr "[chỉ Ä‘á»c]"
-#: ../buffer.c:2524
#, c-format
msgid "1 line --%d%%--"
msgstr "1 dòng --%d%%--"
-#: ../buffer.c:2526
#, c-format
-msgid "%<PRId64> lines --%d%%--"
-msgstr "%<PRId64> dòng --%d%%--"
+msgid "%ld lines --%d%%--"
+msgstr "%ld dòng --%d%%--"
-#: ../buffer.c:2530
#, c-format
-msgid "line %<PRId64> of %<PRId64> --%d%%-- col "
-msgstr "dòng %<PRId64> của %<PRId64> --%d%%-- cột "
+msgid "line %ld of %ld --%d%%-- col "
+msgstr "dòng %ld của %ld --%d%%-- cột "
-#: ../buffer.c:2632 ../buffer.c:4292 ../memline.c:1554
-#, fuzzy
-msgid "[No Name]"
+msgid "[No file]"
msgstr "[Không có tập tin]"
-#. must be a help buffer
-#: ../buffer.c:2667
msgid "help"
msgstr "trợ giúp"
-#: ../buffer.c:3225 ../screen.c:4883
-#, fuzzy
-msgid "[Help]"
+msgid "[help]"
msgstr "[trợ giúp]"
-#: ../buffer.c:3254 ../screen.c:4887
msgid "[Preview]"
msgstr "[Xem trước]"
-#: ../buffer.c:3528
msgid "All"
msgstr "Tất cả"
-#: ../buffer.c:3528
msgid "Bot"
msgstr "Cuối"
-#: ../buffer.c:3531
msgid "Top"
msgstr "Äầu"
-#: ../buffer.c:4244
msgid ""
"\n"
"# Buffer list:\n"
@@ -224,11 +152,12 @@ msgstr ""
"\n"
"# Danh sách bộ đệm:\n"
-#: ../buffer.c:4289
-msgid "[Scratch]"
-msgstr ""
+msgid "[Error List]"
+msgstr "[Danh sách lỗi]"
+
+msgid "[No File]"
+msgstr "[Không có tập tin]"
-#: ../buffer.c:4529
msgid ""
"\n"
"--- Signs ---"
@@ -236,806 +165,300 @@ msgstr ""
"\n"
"--- Ký hiệu ---"
-#: ../buffer.c:4538
#, c-format
msgid "Signs for %s:"
msgstr "Ký hiệu cho %s:"
-#: ../buffer.c:4543
#, c-format
-msgid " line=%<PRId64> id=%d name=%s"
-msgstr " dòng=%<PRId64> id=%d tên=%s"
-
-#: ../cursor_shape.c:68
-msgid "E545: Missing colon"
-msgstr "E545: Thiếu dấu hai chấm"
-
-#: ../cursor_shape.c:70 ../cursor_shape.c:94
-msgid "E546: Illegal mode"
-msgstr "E546: Chế độ không cho phép"
-
-#: ../cursor_shape.c:134
-msgid "E548: digit expected"
-msgstr "E548: yêu cầu một số"
+msgid " line=%ld id=%d name=%s"
+msgstr " dòng=%ld id=%d tên=%s"
-#: ../cursor_shape.c:138
-msgid "E549: Illegal percentage"
-msgstr "E549: Tỷ lệ phần trăm không cho phép"
-
-#: ../diff.c:146
#, c-format
-msgid "E96: Can not diff more than %<PRId64> buffers"
-msgstr ""
-"E96: Chỉ có thể theo dõi sá»± khác nhau trong nhiá»u nhất %<PRId64> bá»™ đệm"
-
-#: ../diff.c:753
-#, fuzzy
-msgid "E810: Cannot read or write temp files"
-msgstr "E557: Không thể mở tập tin termcap"
+msgid "E96: Can not diff more than %ld buffers"
+msgstr "E96: Chỉ có thể theo dõi sá»± khác nhau trong nhiá»u nhất %ld bá»™ đệm"
-#: ../diff.c:755
msgid "E97: Cannot create diffs"
msgstr "E97: Không thể tạo tập tin khác biệt (diff)"
-#: ../diff.c:966
-#, fuzzy
-msgid "E816: Cannot read patch output"
-msgstr "E98: Không thể Ä‘á»c dữ liệu ra cá»§a lệnh diff"
+msgid "Patch file"
+msgstr "Tập tin vá lỗi (patch)"
-#: ../diff.c:1220
msgid "E98: Cannot read diff output"
msgstr "E98: Không thể Ä‘á»c dữ liệu ra cá»§a lệnh diff"
-#: ../diff.c:2081
msgid "E99: Current buffer is not in diff mode"
msgstr "E99: Bá»™ đệm hiện thá»i không nằm trong chế độ khác biệt (diff)"
-#: ../diff.c:2100
-#, fuzzy
-msgid "E793: No other buffer in diff mode is modifiable"
-msgstr "E100: Không còn bộ đệm trong chế độ khác biệt (diff) nào nữa"
-
-#: ../diff.c:2102
msgid "E100: No other buffer in diff mode"
msgstr "E100: Không còn bộ đệm trong chế độ khác biệt (diff) nào nữa"
-#: ../diff.c:2112
msgid "E101: More than two buffers in diff mode, don't know which one to use"
-msgstr ""
-"E101: Có nhiá»u hÆ¡n hai bá»™ đệm trong chế độ khác biệt (diff), không biết chá»n"
+msgstr "E101: Có nhiá»u hÆ¡n hai bá»™ đệm trong chế độ khác biệt (diff), không biết nên chá»n cái nào"
-#: ../diff.c:2141
#, c-format
msgid "E102: Can't find buffer \"%s\""
msgstr "E102: Không tìm thấy bộ đệm \"%s\""
-#: ../diff.c:2152
#, c-format
msgid "E103: Buffer \"%s\" is not in diff mode"
msgstr "E103: Bộ đệm \"%s\" không nằm trong chế độ khác biệt (diff)"
-#: ../diff.c:2193
-msgid "E787: Buffer changed unexpectedly"
-msgstr ""
-
-#: ../digraph.c:1598
msgid "E104: Escape not allowed in digraph"
msgstr "E104: Không cho phép dùng ký tự thoát Escape trong chữ ghép"
-#: ../digraph.c:1760
msgid "E544: Keymap file not found"
msgstr "E544: Không tìm thấy tập tin sơ đồ bàn phím"
-#: ../digraph.c:1785
msgid "E105: Using :loadkeymap not in a sourced file"
msgstr "E105: Câu lệnh :loadkeymap được sử dụng ngoài tập tin script"
-#: ../digraph.c:1821
-msgid "E791: Empty keymap entry"
-msgstr ""
-
-#: ../edit.c:82
msgid " Keyword completion (^N^P)"
msgstr " Tự động kết thúc cho từ khóa (^N^P)"
-#. ctrl_x_mode == 0, ^P/^N compl.
-#: ../edit.c:83
-#, fuzzy
-msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)"
+msgid " ^X mode (^E^Y^L^]^F^I^K^D^V^N^P)"
msgstr " Chế độ ^X (^E^Y^L^]^F^I^K^D^V^N^P)"
-#: ../edit.c:85
+msgid " Keyword Local completion (^N^P)"
+msgstr " Tự động kết thúc nội bộ cho từ khóa (^N^P)"
+
msgid " Whole line completion (^L^N^P)"
msgstr " Tự động kết thúc cho cả dòng (^L^N^P)"
-#: ../edit.c:86
msgid " File name completion (^F^N^P)"
msgstr " Tự động kết thúc tên tập tin (^F^N^P)"
-#: ../edit.c:87
msgid " Tag completion (^]^N^P)"
msgstr " Tự động kết thúc thẻ đánh dấu (^]^N^P)"
-#: ../edit.c:88
msgid " Path pattern completion (^N^P)"
msgstr " Tá»± động kết thúc mẫu đưá»ng dẫn (^N^P)"
-#: ../edit.c:89
msgid " Definition completion (^D^N^P)"
msgstr " Tự động kết thúc định nghĩa (^D^N^P)"
-#: ../edit.c:91
msgid " Dictionary completion (^K^N^P)"
msgstr " Tự động kết thúc theo từ điển (^K^N^P)"
-#: ../edit.c:92
msgid " Thesaurus completion (^T^N^P)"
msgstr " Tự động kết thúc từ đồng âm (^T^N^P)"
-#: ../edit.c:93
msgid " Command-line completion (^V^N^P)"
msgstr " Tự động kết thúc dòng lệnh (^V^N^P)"
-#: ../edit.c:94
-#, fuzzy
-msgid " User defined completion (^U^N^P)"
-msgstr " Tự động kết thúc cho cả dòng (^L^N^P)"
-
-#: ../edit.c:95
-#, fuzzy
-msgid " Omni completion (^O^N^P)"
-msgstr " Tự động kết thúc thẻ đánh dấu (^]^N^P)"
-
-#: ../edit.c:96
-#, fuzzy
-msgid " Spelling suggestion (^S^N^P)"
-msgstr " Tự động kết thúc cho cả dòng (^L^N^P)"
-
-#: ../edit.c:97
-msgid " Keyword Local completion (^N^P)"
-msgstr " Tự động kết thúc nội bộ cho từ khóa (^N^P)"
-
-#: ../edit.c:100
msgid "Hit end of paragraph"
msgstr "Kết thúc của đoạn văn"
-#: ../edit.c:101
-msgid "E839: Completion function changed window"
-msgstr ""
-
-#: ../edit.c:102
-msgid "E840: Completion function deleted text"
-msgstr ""
+msgid "'thesaurus' option is empty"
+msgstr "Không đưa ra giá trị cá»§a tùy chá»n 'thesaurus'"
-#: ../edit.c:1847
msgid "'dictionary' option is empty"
msgstr "Không đưa ra giá trị cá»§a tùy chá»n 'dictionary'"
-#: ../edit.c:1848
-msgid "'thesaurus' option is empty"
-msgstr "Không đưa ra giá trị cá»§a tùy chá»n 'thesaurus'"
-
-#: ../edit.c:2655
#, c-format
msgid "Scanning dictionary: %s"
msgstr "Quét từ điển: %s"
-#: ../edit.c:3079
msgid " (insert) Scroll (^E/^Y)"
msgstr " (chèn) Cuộn (^E/^Y)"
-#: ../edit.c:3081
msgid " (replace) Scroll (^E/^Y)"
msgstr " (thay thế) Cuộn (^E/^Y)"
-#: ../edit.c:3587
#, c-format
msgid "Scanning: %s"
msgstr "Quét: %s"
-#: ../edit.c:3614
msgid "Scanning tags."
msgstr "Tìm kiếm trong số thẻ đánh dấu."
-#: ../edit.c:4519
msgid " Adding"
msgstr " Thêm"
-#. 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.c:4562
msgid "-- Searching..."
msgstr "-- Tìm kiếm..."
-#: ../edit.c:4618
msgid "Back at original"
msgstr "Từ ban đầu"
-#: ../edit.c:4621
msgid "Word from other line"
msgstr "Từ của dòng khác"
-#: ../edit.c:4624
msgid "The only match"
msgstr "Tương ứng duy nhất"
-#: ../edit.c:4680
#, c-format
msgid "match %d of %d"
msgstr "Tương ứng %d của %d"
-#: ../edit.c:4684
#, c-format
msgid "match %d"
msgstr "Tương ứng %d"
-#: ../eval.c:137
-#, fuzzy
-msgid "E18: Unexpected characters in :let"
-msgstr "E18: Ở trước '=' có các ký tự không mong đợi"
-
-#: ../eval.c:138
-#, fuzzy, c-format
-msgid "E684: list index out of range: %<PRId64>"
-msgstr "E322: số thứ tự dòng vượt quá giới hạn : %<PRId64>"
-
-#: ../eval.c:139
#, c-format
-msgid "E121: Undefined variable: %s"
-msgstr "E121: Biến không xác định: %s"
-
-#: ../eval.c:140
-msgid "E111: Missing ']'"
-msgstr "E111: Thiếu ']'"
+msgid "E106: Unknown variable: \"%s\""
+msgstr "E106: Biến không biết: \"%s\""
-#: ../eval.c:141
-#, fuzzy, c-format
-msgid "E686: Argument of %s must be a List"
-msgstr "E487: Tham số phải là một số dương"
-
-#: ../eval.c:143
-#, fuzzy, c-format
-msgid "E712: Argument of %s must be a List or Dictionary"
-msgstr "E487: Tham số phải là một số dương"
-
-#: ../eval.c:144
-#, fuzzy
-msgid "E713: Cannot use empty key for Dictionary"
-msgstr "E214: Không tìm thấy tập tin tạm thá»i (temp) để ghi nhá»›"
-
-#: ../eval.c:145
-#, fuzzy
-msgid "E714: List required"
-msgstr "E471: Cần chỉ ra tham số"
-
-#: ../eval.c:146
-#, fuzzy
-msgid "E715: Dictionary required"
-msgstr "E129: Cần tên hàm số"
-
-#: ../eval.c:147
-#, c-format
-msgid "E118: Too many arguments for function: %s"
-msgstr "E118: Quá nhiá»u tham số cho hàm: %s"
-
-#: ../eval.c:148
-#, c-format
-msgid "E716: Key not present in Dictionary: %s"
-msgstr ""
-
-#: ../eval.c:150
-#, c-format
-msgid "E122: Function %s already exists, add ! to replace it"
-msgstr "E122: Hàm số %s đã có, hãy thêm ! để thay thế nó."
-
-#: ../eval.c:151
-#, fuzzy
-msgid "E717: Dictionary entry already exists"
-msgstr "E95: Äã có bá»™ đệm vá»›i tên như vậy"
-
-#: ../eval.c:152
-#, fuzzy
-msgid "E718: Funcref required"
-msgstr "E129: Cần tên hàm số"
-
-#: ../eval.c:153
-#, fuzzy
-msgid "E719: Cannot use [:] with a Dictionary"
-msgstr "E360: Không chạy được shell vá»›i tùy chá»n -f"
-
-#: ../eval.c:154
-#, c-format
-msgid "E734: Wrong variable type for %s="
-msgstr ""
-
-#: ../eval.c:155
-#, fuzzy, c-format
-msgid "E130: Unknown function: %s"
-msgstr "E117: Hàm số không biết: %s"
-
-#: ../eval.c:156
-#, c-format
-msgid "E461: Illegal variable name: %s"
-msgstr "E461: Tên biến không cho phép: %s"
-
-#: ../eval.c:157
-msgid "E806: using Float as a String"
-msgstr ""
-
-#: ../eval.c:1830
-msgid "E687: Less targets than List items"
-msgstr ""
-
-#: ../eval.c:1834
-msgid "E688: More targets than List items"
-msgstr ""
-
-#: ../eval.c:1906
-msgid "Double ; in list of variables"
-msgstr ""
-
-#: ../eval.c:2078
-#, fuzzy, c-format
-msgid "E738: Can't list variables for %s"
-msgstr "E138: Không thể ghi tập tin viminfo %s!"
-
-#: ../eval.c:2391
-msgid "E689: Can only index a List or Dictionary"
-msgstr ""
-
-#: ../eval.c:2396
-msgid "E708: [:] must come last"
-msgstr ""
-
-#: ../eval.c:2439
-msgid "E709: [:] requires a List value"
-msgstr ""
-
-#: ../eval.c:2674
-msgid "E710: List value has more items than target"
-msgstr ""
-
-#: ../eval.c:2678
-msgid "E711: List value has not enough items"
-msgstr ""
-
-#: ../eval.c:2867
-#, fuzzy
-msgid "E690: Missing \"in\" after :for"
-msgstr "E69: Thiếu ] sau %s%%["
-
-#: ../eval.c:3063
#, c-format
msgid "E107: Missing parentheses: %s"
msgstr "E107: Thiếu dấu ngoặc: %s"
-#: ../eval.c:3263
#, c-format
msgid "E108: No such variable: \"%s\""
msgstr "E108: Không có biến như vậy: \"%s\""
-#: ../eval.c:3333
-msgid "E743: variable nested too deep for (un)lock"
-msgstr ""
-
-#: ../eval.c:3630
msgid "E109: Missing ':' after '?'"
msgstr "E109: Thiếu ':' sau '?'"
-#: ../eval.c:3893
-msgid "E691: Can only compare List with List"
-msgstr ""
-
-#: ../eval.c:3895
-#, fuzzy
-msgid "E692: Invalid operation for Lists"
-msgstr "E449: Nhận được một biểu thức không cho phép"
-
-#: ../eval.c:3915
-msgid "E735: Can only compare Dictionary with Dictionary"
-msgstr ""
-
-#: ../eval.c:3917
-#, fuzzy
-msgid "E736: Invalid operation for Dictionary"
-msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
-
-#: ../eval.c:3932
-msgid "E693: Can only compare Funcref with Funcref"
-msgstr ""
-
-#: ../eval.c:3934
-#, fuzzy
-msgid "E694: Invalid operation for Funcrefs"
-msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
-
-#: ../eval.c:4277
-#, fuzzy
-msgid "E804: Cannot use '%' with Float"
-msgstr "E360: Không chạy được shell vá»›i tùy chá»n -f"
-
-#: ../eval.c:4478
msgid "E110: Missing ')'"
msgstr "E110: Thiếu ')'"
-#: ../eval.c:4609
-#, fuzzy
-msgid "E695: Cannot index a Funcref"
-msgstr "E90: Không thể bỠnạp từ bộ nhớ bộ đệm cuối cùng"
+msgid "E111: Missing ']'"
+msgstr "E111: Thiếu ']'"
-#: ../eval.c:4839
#, c-format
msgid "E112: Option name missing: %s"
msgstr "E112: Không đưa ra tên tùy chá»n: %s"
-#: ../eval.c:4855
#, c-format
msgid "E113: Unknown option: %s"
msgstr "E113: Tùy chá»n không biết: %s"
-#: ../eval.c:4904
#, c-format
msgid "E114: Missing quote: %s"
msgstr "E114: Thiếu ngoặc kép: %s"
-#: ../eval.c:5020
#, c-format
msgid "E115: Missing quote: %s"
msgstr "E115: Thiếu ngoặc kép: %s"
-#: ../eval.c:5084
-#, fuzzy, c-format
-msgid "E696: Missing comma in List: %s"
-msgstr "E405: Thiếu dấu bằng: %s"
-
-#: ../eval.c:5091
-#, fuzzy, c-format
-msgid "E697: Missing end of List ']': %s"
-msgstr "E398: Thiếu '=': %s"
-
-#: ../eval.c:6475
-#, fuzzy, c-format
-msgid "E720: Missing colon in Dictionary: %s"
-msgstr "E405: Thiếu dấu bằng: %s"
-
-#: ../eval.c:6499
-#, c-format
-msgid "E721: Duplicate key in Dictionary: \"%s\""
-msgstr ""
-
-#: ../eval.c:6517
-#, fuzzy, c-format
-msgid "E722: Missing comma in Dictionary: %s"
-msgstr "E527: Thiếu dấu phẩy"
-
-#: ../eval.c:6524
-#, fuzzy, c-format
-msgid "E723: Missing end of Dictionary '}': %s"
-msgstr "E126: Thiếu lệnh :endfunction"
-
-#: ../eval.c:6555
-#, fuzzy
-msgid "E724: variable nested too deep for displaying"
-msgstr "E22: Các script lồng vào nhau quá sâu"
-
-#: ../eval.c:7188
-#, fuzzy, c-format
-msgid "E740: Too many arguments for function %s"
-msgstr "E118: Quá nhiá»u tham số cho hàm: %s"
-
-#: ../eval.c:7190
#, c-format
msgid "E116: Invalid arguments for function %s"
msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
-#: ../eval.c:7377
#, c-format
msgid "E117: Unknown function: %s"
msgstr "E117: Hàm số không biết: %s"
-#: ../eval.c:7383
+#, c-format
+msgid "E118: Too many arguments for function: %s"
+msgstr "E118: Quá nhiá»u tham số cho hàm: %s"
+
#, c-format
msgid "E119: Not enough arguments for function: %s"
msgstr "E119: Không đủ tham số cho hàm: %s"
-#: ../eval.c:7387
#, c-format
msgid "E120: Using <SID> not in a script context: %s"
msgstr "E120: Sử dụng <SID> ngoài script: %s"
-#: ../eval.c:7391
-#, c-format
-msgid "E725: Calling dict function without Dictionary: %s"
-msgstr ""
-
-#: ../eval.c:7453
-#, fuzzy
-msgid "E808: Number or Float required"
-msgstr "E521: Sau dấu = cần đưa ra một số"
-
-#: ../eval.c:7503
-#, fuzzy
-msgid "add() argument"
-msgstr "Tham số không được phép cho"
-
-#: ../eval.c:7907
-#, fuzzy
-msgid "E699: Too many arguments"
-msgstr "Có quá nhiá»u tham số soạn thảo"
-
-#: ../eval.c:8073
-#, fuzzy
-msgid "E785: complete() can only be used in Insert mode"
-msgstr "E328: Trình đơn chỉ có trong chế độ khác"
-
-#: ../eval.c:8156
msgid "&Ok"
msgstr "&Ok"
-#: ../eval.c:8676
-#, fuzzy, c-format
-msgid "E737: Key already exists: %s"
-msgstr "E227: đã có ánh xạ cho %s"
-
-#: ../eval.c:8692
-msgid "extend() argument"
-msgstr ""
-
-#: ../eval.c:8915
-#, fuzzy
-msgid "map() argument"
-msgstr " vim [các tham số] "
-
-#: ../eval.c:8916
-msgid "filter() argument"
-msgstr ""
-
-#: ../eval.c:9229
#, c-format
msgid "+-%s%3ld lines: "
msgstr "+-%s%3ld dòng: "
-#: ../eval.c:9291
-#, fuzzy, c-format
-msgid "E700: Unknown function: %s"
-msgstr "E117: Hàm số không biết: %s"
+msgid ""
+"&OK\n"
+"&Cancel"
+msgstr ""
+"&OK\n"
+"&Há»§y bá»"
-#: ../eval.c:10729
msgid "called inputrestore() more often than inputsave()"
msgstr "Hàm số inputrestore() được gá»i nhiá»u hÆ¡n hàm inputsave()"
-#: ../eval.c:10771
-#, fuzzy
-msgid "insert() argument"
-msgstr "Có quá nhiá»u tham số soạn thảo"
-
-#: ../eval.c:10841
-#, fuzzy
-msgid "E786: Range not allowed"
-msgstr "E481: Không cho phép sử dụng phạm vi"
-
-#: ../eval.c:11140
-#, fuzzy
-msgid "E701: Invalid type for len()"
-msgstr "E596: Phông chữ không đúng"
-
-#: ../eval.c:11980
-msgid "E726: Stride is zero"
-msgstr ""
-
-#: ../eval.c:11982
-msgid "E727: Start past end"
-msgstr ""
-
-#: ../eval.c:12024 ../eval.c:15297
-msgid "<empty>"
-msgstr ""
-
-#: ../eval.c:12282
-msgid "remove() argument"
-msgstr ""
-
-#: ../eval.c:12466
msgid "E655: Too many symbolic links (cycle?)"
msgstr "E655: Quá nhiá»u liên kết tượng trưng (vòng lặp?)"
-#: ../eval.c:12593
-msgid "reverse() argument"
-msgstr ""
+msgid "E240: No connection to Vim server"
+msgstr "E240: Không có kết nối với máy chủ Vim"
-#: ../eval.c:13721
-msgid "sort() argument"
-msgstr ""
+msgid "E277: Unable to read a server reply"
+msgstr "E277: Máy chá»§ không trả lá»i"
-#: ../eval.c:13721
-#, fuzzy
-msgid "uniq() argument"
-msgstr "Tham số không được phép cho"
-
-#: ../eval.c:13776
-#, fuzzy
-msgid "E702: Sort compare function failed"
-msgstr "E237: Chá»n máy in không thành công"
+msgid "E258: Unable to send to client"
+msgstr "E258: Không thể trả lá»i cho máy con"
-#: ../eval.c:13806
-msgid "E882: Uniq compare function failed"
-msgstr ""
+#, c-format
+msgid "E241: Unable to send to %s"
+msgstr "E241: Không thể gửi tin nhắn tới %s"
-#: ../eval.c:14085
msgid "(Invalid)"
msgstr "(Không đúng)"
-#: ../eval.c:14590
-#, fuzzy
-msgid "E677: Error writing temp file"
-msgstr "E208: Lỗi ghi nhớ vào \"%s\""
-
-#: ../eval.c:16159
-msgid "E805: Using a Float as a Number"
-msgstr ""
-
-#: ../eval.c:16162
-msgid "E703: Using a Funcref as a Number"
-msgstr ""
-
-#: ../eval.c:16170
-msgid "E745: Using a List as a Number"
-msgstr ""
-
-#: ../eval.c:16173
-msgid "E728: Using a Dictionary as a Number"
-msgstr ""
-
-#: ../eval.c:16259
-msgid "E729: using Funcref as a String"
-msgstr ""
-
-#: ../eval.c:16262
-#, fuzzy
-msgid "E730: using List as a String"
-msgstr "E374: Thiếu ] trong chuỗi định dạng"
-
-#: ../eval.c:16265
-msgid "E731: using Dictionary as a String"
-msgstr ""
-
-#: ../eval.c:16619
-#, fuzzy, c-format
-msgid "E706: Variable type mismatch for: %s"
-msgstr "E93: Tìm thấy vài tương ứng với %s"
-
-#: ../eval.c:16705
-#, fuzzy, c-format
-msgid "E795: Cannot delete variable %s"
-msgstr "E46: Không thay đổi được biến chỉ Ä‘á»c \"%s\""
-
-#: ../eval.c:16724
-#, fuzzy, c-format
-msgid "E704: Funcref variable name must start with a capital: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-
-#: ../eval.c:16732
#, c-format
-msgid "E705: Variable name conflicts with existing function: %s"
-msgstr ""
+msgid "E121: Undefined variable: %s"
+msgstr "E121: Biến không xác định: %s"
-#: ../eval.c:16763
#, c-format
-msgid "E741: Value is locked: %s"
-msgstr ""
-
-#: ../eval.c:16764 ../eval.c:16769 ../message.c:1839
-msgid "Unknown"
-msgstr "Không rõ"
-
-#: ../eval.c:16768
-#, fuzzy, c-format
-msgid "E742: Cannot change value of %s"
-msgstr "E284: Không đặt được giá trị nội dung nhập vào (IC)"
+msgid "E461: Illegal variable name: %s"
+msgstr "E461: Tên biến không cho phép: %s"
-#: ../eval.c:16838
-msgid "E698: variable nested too deep for making a copy"
-msgstr ""
+#, c-format
+msgid "E122: Function %s already exists, add ! to replace it"
+msgstr "E122: Hàm số %s đã có, hãy thêm ! để thay thế nó."
-#: ../eval.c:17249
#, c-format
msgid "E123: Undefined function: %s"
msgstr "E123: Hàm số không xác định: %s"
-#: ../eval.c:17260
#, c-format
msgid "E124: Missing '(': %s"
msgstr "E124: Thiếu '(': %s"
-#: ../eval.c:17293
-#, fuzzy
-msgid "E862: Cannot use g: here"
-msgstr "E284: Không đặt được giá trị nội dung nhập vào (IC)"
-
-#: ../eval.c:17312
#, c-format
msgid "E125: Illegal argument: %s"
msgstr "E125: Tham số không cho phép: %s"
-#: ../eval.c:17323
-#, fuzzy, c-format
-msgid "E853: Duplicate argument name: %s"
-msgstr "E154: Thẻ ghi lặp lại \"%s\" trong tập tin %s"
-
-#: ../eval.c:17416
msgid "E126: Missing :endfunction"
msgstr "E126: Thiếu lệnh :endfunction"
-#: ../eval.c:17537
-#, fuzzy, c-format
-msgid "E707: Function name conflicts with variable: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-
-#: ../eval.c:17549
#, c-format
msgid "E127: Cannot redefine function %s: It is in use"
msgstr "E127: Không thể định nghĩa lại hàm số %s: hàm đang được sử dụng"
-#: ../eval.c:17604
-#, fuzzy, c-format
-msgid "E746: Function name does not match script file name: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-
-#: ../eval.c:17716
msgid "E129: Function name required"
msgstr "E129: Cần tên hàm số"
-#: ../eval.c:17824
-#, fuzzy, c-format
-msgid "E128: Function name must start with a capital or \"s:\": %s"
+#, c-format
+msgid "E128: Function name must start with a capital: %s"
msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
-#: ../eval.c:17833
-#, fuzzy, c-format
-msgid "E884: Function name cannot contain a colon: %s"
-msgstr "E128: Tên hàm số phải bắt đầu với một chữ cái hoa: %s"
+#, c-format
+msgid "E130: Undefined function: %s"
+msgstr "E130: Hàm số %s chưa xác định"
-#: ../eval.c:18336
#, c-format
msgid "E131: Cannot delete function %s: It is in use"
msgstr "E131: Không thể xóa hàm số %s: Hàm đang được sử dụng"
-#: ../eval.c:18441
msgid "E132: Function call depth is higher than 'maxfuncdepth'"
msgstr "E132: Äá»™ sâu cá»§a lá»i gá»i hàm số lá»›n hÆ¡n giá trị 'maxfuncdepth'"
-#: ../eval.c:18568
#, c-format
msgid "calling %s"
msgstr "lá»i gá»i %s"
-#: ../eval.c:18651
#, c-format
msgid "%s aborted"
msgstr "%s dừng"
-#: ../eval.c:18653
#, c-format
-msgid "%s returning #%<PRId64>"
-msgstr "%s trả lại #%<PRId64>"
+msgid "%s returning #%ld"
+msgstr "%s trả lại #%ld"
-#: ../eval.c:18670
-#, fuzzy, c-format
-msgid "%s returning %s"
+#, c-format
+msgid "%s returning \"%s\""
msgstr "%s trả lại \"%s\""
-#: ../eval.c:18691 ../ex_cmds2.c:2695
#, c-format
msgid "continuing in %s"
msgstr "tiếp tục trong %s"
-#: ../eval.c:18795
msgid "E133: :return not inside a function"
msgstr "E133: lệnh :return ở ngoài một hàm"
-#: ../eval.c:19159
msgid ""
"\n"
"# global variables:\n"
@@ -1043,114 +466,260 @@ msgstr ""
"\n"
"# biến toàn cầu:\n"
-#: ../eval.c:19254
-msgid ""
-"\n"
-"\tLast set from "
+msgid "Entering Debug mode. Type \"cont\" to continue."
+msgstr "Bật chế độ sửa lỗi (Debug). Gõ \"cont\" để tiếp tục."
+
+#, c-format
+msgid "line %ld: %s"
+msgstr "dòng %ld: %s"
+
+#, c-format
+msgid "cmd: %s"
+msgstr "câu lệnh: %s"
+
+#, c-format
+msgid "Breakpoint in \"%s%s\" line %ld"
+msgstr "Äiểm dừng trên \"%s%s\" dòng %ld"
+
+#, c-format
+msgid "E161: Breakpoint not found: %s"
+msgstr "E161: Không tìm thấy điểm dừng: %s"
+
+msgid "No breakpoints defined"
+msgstr "Äiểm dừng không được xác định"
+
+#, c-format
+msgid "%3d %s %s line %ld"
+msgstr "%3d %s %s dòng %ld"
+
+msgid "Save As"
+msgstr "Ghi nhớ như"
+
+#, c-format
+msgid "Save changes to \"%.*s\"?"
+msgstr "Ghi nhớ thay đổi vào \"%.*s\"?"
+
+msgid "Untitled"
+msgstr "Chưa đặt tên"
+
+#, c-format
+msgid "E162: No write since last change for buffer \"%s\""
+msgstr "E162: Thay đổi chưa được ghi nhớ trong bộ đệm \"%s\""
+
+msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
msgstr ""
-"\n"
-"\tLần cuối cùng tùy chá»n thay đổi vào "
+"Cảnh báo: Chuyển tới bộ đệm khác không theo ý muốn (hãy kiểm tra câu lệnh tự "
+"động)"
-#: ../eval.c:19272
-#, fuzzy
-msgid "No old files"
-msgstr "Không có tập tin được tính đến"
+msgid "E163: There is only one file to edit"
+msgstr "E163: Chỉ có một tập tin để soạn thảo"
+
+msgid "E164: Cannot go before first file"
+msgstr "E164: Äây là tập tin đầu tiên"
+
+msgid "E165: Cannot go beyond last file"
+msgstr "E165: Äây là tập tin cuối cùng"
+
+# TODO: Capitalise first word of message?
+msgid "E666: Compiler not supported: %s"
+msgstr "E666: trình biên dịch không được hỗ trợ: %s"
+
+#, c-format
+msgid "Searching for \"%s\" in \"%s\""
+msgstr "Tìm kiếm \"%s\" trong \"%s\""
+
+#, c-format
+msgid "Searching for \"%s\""
+msgstr "Tìm kiếm \"%s\""
+
+#, c-format
+msgid "not found in 'runtimepath': \"%s\""
+msgstr "không tìm thấy trong 'runtimepath': \"%s\""
+
+msgid "Source Vim script"
+msgstr "Thực hiện script của Vim"
+
+#, c-format
+msgid "Cannot source a directory: \"%s\""
+msgstr "Không thể thực hiện một thư mục: \"%s\""
+
+#, c-format
+msgid "could not source \"%s\""
+msgstr "không thực hiện được \"%s\""
+
+#, c-format
+msgid "line %ld: could not source \"%s\""
+msgstr "dòng %ld: không thực hiện được \"%s\""
+
+#, c-format
+msgid "sourcing \"%s\""
+msgstr "thực hiện \"%s\""
+
+#, c-format
+msgid "line %ld: sourcing \"%s\""
+msgstr "dòng %ld: thực hiện \"%s\""
+
+#, c-format
+msgid "finished sourcing %s"
+msgstr "thực hiện xong %s"
+
+msgid "W15: Warning: Wrong line separator, ^M may be missing"
+msgstr "W15: Cảnh báo: Ký tự phân cách dòng không đúng. Rất có thể thiếu ^M"
+
+msgid "E167: :scriptencoding used outside of a sourced file"
+msgstr "E167: Lệnh :scriptencoding sử dụng ngoài tập tin script"
+
+msgid "E168: :finish used outside of a sourced file"
+msgstr "E168: Lệnh :finish sử dụng ngoài tập tin script"
+
+#, c-format
+msgid "Page %d"
+msgstr "Trang %d"
+
+msgid "No text to be printed"
+msgstr "Không có gì để in"
+
+#, c-format
+msgid "Printing page %d (%d%%)"
+msgstr "In trang %d (%d%%)"
+
+#, c-format
+msgid " Copy %d of %d"
+msgstr " Sao chép %d của %d"
+
+#, c-format
+msgid "Printed: %s"
+msgstr "Äã in: %s"
+
+msgid "Printing aborted"
+msgstr "In bị dừng"
+
+msgid "E455: Error writing to PostScript output file"
+msgstr "E455: Lỗi ghi nhớ vào tập tin PostScript"
+
+#, c-format
+msgid "E624: Can't open file \"%s\""
+msgstr "E624: Không thể mở tập tin \"%s\""
+
+#, c-format
+msgid "E457: Can't read PostScript resource file \"%s\""
+msgstr "E457: Không thể Ä‘á»c tập tin tài nguyên PostScript \"%s\""
+
+# TODO: Capitalise first word of message?
+msgid "E618: File \"%s\" is not a PostScript resource file"
+msgstr "E618: \"%s\" không phải là tập tin tài nguyên PostScript"
+
+# TODO: Capitalise first word of message?
+msgid "E619: File \"%s\" is not a supported PostScript resource file"
+msgstr "E619: \"%s\" không phải là tập tin tài nguyên PostScript được hỗ trợ"
+
+#, c-format
+msgid "E621: \"%s\" resource file has wrong version"
+msgstr "E621: tập tin tài nguyên \"%s\" có phiên bản không đúng"
+
+msgid "E324: Can't open PostScript output file"
+msgstr "E324: Không thể mở tập tin PostScript"
+
+#, c-format
+msgid "E456: Can't open file \"%s\""
+msgstr "E456: Không thể mở tập tin \"%s\""
+
+msgid "E456: Can't find PostScript resource file \"prolog.ps\""
+msgstr "E456: Không tìm thấy tập tin tài nguyên PostScript \"prolog.ps\""
+
+#, c-format
+msgid "E456: Can't find PostScript resource file \"%s.ps\""
+msgstr "E456: Không tìm thấy tập tin tài nguyên PostScript \"%s.ps\""
+
+#, c-format
+msgid "E620: Unable to convert from multi-byte to \"%s\" encoding"
+msgstr "E620: Không thể chuyển từ các ký tá»± nhiá»u byte thành bảng mã \"%s\""
+
+msgid "Sending to printer..."
+msgstr "Gửi tới máy in..."
+
+msgid "E365: Failed to print PostScript file"
+msgstr "E365: In tập tin PostScript không thành công"
+
+msgid "Print job sent."
+msgstr "Äã gá»­i công việc in."
+
+#, c-format
+msgid "Current %slanguage: \"%s\""
+msgstr "Ngôn ngữ %shiện thá»i: \"%s\""
+
+#, c-format
+msgid "E197: Cannot set language to \"%s\""
+msgstr "E197: Không thể thay đổi ngôn ngữ thành \"%s\""
-#: ../ex_cmds.c:122
#, c-format
msgid "<%s>%s%s %d, Hex %02x, Octal %03o"
msgstr "<%s>%s%s %d, Hex %02x, Octal %03o"
-#: ../ex_cmds.c:145
#, c-format
msgid "> %d, Hex %04x, Octal %o"
msgstr "> %d, Hex %04x, Octal %o"
-#: ../ex_cmds.c:146
#, c-format
msgid "> %d, Hex %08x, Octal %o"
msgstr "> %d, Hex %08x, Octal %o"
-#: ../ex_cmds.c:684
msgid "E134: Move lines into themselves"
msgstr "E134: Di chuyển các dòng lên chính chúng"
-#: ../ex_cmds.c:747
msgid "1 line moved"
msgstr "Äã di chuyển 1 dòng"
-#: ../ex_cmds.c:749
#, c-format
-msgid "%<PRId64> lines moved"
-msgstr "Äã di chuyển %<PRId64> dòng"
+msgid "%ld lines moved"
+msgstr "Äã di chuyển %ld dòng"
-#: ../ex_cmds.c:1175
#, c-format
-msgid "%<PRId64> lines filtered"
-msgstr "Äã lá»c %<PRId64> dòng"
+msgid "%ld lines filtered"
+msgstr "Äã lá»c %ld dòng"
-#: ../ex_cmds.c:1194
msgid "E135: *Filter* Autocommands must not change current buffer"
msgstr "E135: Các lệnh tá»± động *Filter* không được thay đổi bá»™ đệm hiện thá»i"
-#: ../ex_cmds.c:1244
msgid "[No write since last change]\n"
msgstr "[Thay đổi chưa được ghi nhớ]\n"
-#: ../ex_cmds.c:1424
#, c-format
msgid "%sviminfo: %s in line: "
msgstr "%sviminfo: %s trên dòng: "
-#: ../ex_cmds.c:1431
msgid "E136: viminfo: Too many errors, skipping rest of file"
msgstr "E136: viminfo: Quá nhiá»u lá»—i, phần còn lại cá»§a tập tin sẽ được bá» qua"
-#: ../ex_cmds.c:1458
#, c-format
msgid "Reading viminfo file \"%s\"%s%s%s"
msgstr "Äá»c tập tin viminfo \"%s\"%s%s%s"
-#: ../ex_cmds.c:1460
msgid " info"
msgstr " thông tin"
-#: ../ex_cmds.c:1461
msgid " marks"
msgstr " dấu hiệu"
-#: ../ex_cmds.c:1462
-#, fuzzy
-msgid " oldfiles"
-msgstr "Không có tập tin được tính đến"
-
-#: ../ex_cmds.c:1463
msgid " FAILED"
msgstr " KHÔNG THÀNH CÔNG"
-#. avoid a wait_return for this message, it's annoying
-#: ../ex_cmds.c:1541
#, c-format
msgid "E137: Viminfo file is not writable: %s"
msgstr "E137: Thiếu quyá»n ghi lên tập tin viminfo: %s"
-#: ../ex_cmds.c:1626
#, c-format
msgid "E138: Can't write viminfo file %s!"
msgstr "E138: Không thể ghi tập tin viminfo %s!"
-#: ../ex_cmds.c:1635
#, c-format
msgid "Writing viminfo file \"%s\""
msgstr "Ghi tập tin viminfo \"%s\""
-#. Write the info:
-#: ../ex_cmds.c:1720
#, c-format
msgid "# This viminfo file was generated by Vim %s.\n"
msgstr "# Tập tin viminfo này được tự động tạo bởi Vim %s.\n"
-#: ../ex_cmds.c:1722
msgid ""
"# You may edit it if you're careful!\n"
"\n"
@@ -1158,137 +727,88 @@ msgstr ""
"# Bạn có thể sá»­a tập tin này, nhưng hãy thận trá»ng!\n"
"\n"
-#: ../ex_cmds.c:1723
msgid "# Value of 'encoding' when this file was written\n"
msgstr "# Giá trị cá»§a tùy chá»n 'encoding' vào thá»i Ä‘iểm ghi tập tin\n"
-#: ../ex_cmds.c:1800
msgid "Illegal starting char"
msgstr "Ký tự đầu tiên không cho phép"
-#: ../ex_cmds.c:2162
+msgid "E139: File is loaded in another buffer"
+msgstr "E139: Tập tin được nạp trong bộ đệm khác"
+
msgid "Write partial file?"
msgstr "Ghi nhớ một phần tập tin?"
-#: ../ex_cmds.c:2166
msgid "E140: Use ! to write partial buffer"
msgstr "E140: Sử dụng ! để ghi nhớ một phần bộ đệm"
-#: ../ex_cmds.c:2281
-#, fuzzy, c-format
-msgid "Overwrite existing file \"%s\"?"
-msgstr "Ghi đè lên tập tin đã có \"%.*s\"?"
-
-#: ../ex_cmds.c:2317
#, c-format
-msgid "Swap file \"%s\" exists, overwrite anyway?"
-msgstr ""
-
-#: ../ex_cmds.c:2326
-#, fuzzy, c-format
-msgid "E768: Swap file exists: %s (:silent! overrides)"
-msgstr "E13: Tập tin đã tồn tại (thêm ! để ghi chèn)"
+msgid "Overwrite existing file \"%.*s\"?"
+msgstr "Ghi đè lên tập tin đã có \"%.*s\"?"
-#: ../ex_cmds.c:2381
#, c-format
-msgid "E141: No file name for buffer %<PRId64>"
-msgstr "E141: Không có tên tập tin cho bộ đệm %<PRId64>"
+msgid "E141: No file name for buffer %ld"
+msgstr "E141: Không có tên tập tin cho bộ đệm %ld"
-#: ../ex_cmds.c:2412
msgid "E142: File not written: Writing is disabled by 'write' option"
msgstr "E142: Tập tin chưa được ghi nhá»›: Ghi nhá»› bị tắt bởi tùy chá»n 'write'"
-#: ../ex_cmds.c:2434
-#, fuzzy, c-format
+#, c-format
msgid ""
-"'readonly' option is set for \"%s\".\n"
+"'readonly' option is set for \"%.*s\".\n"
"Do you wish to write anyway?"
msgstr ""
"Tùy chá»n 'readonly' được đặt cho \"%.*s\".\n"
"Ghi nhá»› bằng má»i giá?"
-#: ../ex_cmds.c:2439
-#, c-format
-msgid ""
-"File permissions of \"%s\" are read-only.\n"
-"It may still be possible to write it.\n"
-"Do you wish to try?"
-msgstr ""
-
-#: ../ex_cmds.c:2451
-#, fuzzy, c-format
-msgid "E505: \"%s\" is read-only (add ! to override)"
-msgstr "là tập tin chỉ Ä‘á»c (thêm ! để ghi nhá»› bằng má»i giá)"
+msgid "Edit File"
+msgstr "Soạn thảo tập tin"
-#: ../ex_cmds.c:3120
#, c-format
msgid "E143: Autocommands unexpectedly deleted new buffer %s"
msgstr "E143: Các lệnh tự động xóa bộ đệm mới ngoài ý muốn %s"
-#: ../ex_cmds.c:3313
-msgid "E144: non-numeric argument to :z"
+# TODO: Capitalise first word of message?
+msgid "E144: Non-numeric argument to :z"
msgstr "E144: Tham số của lệnh :z phải là số"
-#: ../ex_cmds.c:3498
+msgid "E145: Shell commands not allowed in rvim"
+msgstr "E145: Không cho phép sử dụng lệnh shell trong rvim."
+
msgid "E146: Regular expressions can't be delimited by letters"
msgstr "E146: Không thể phân cách biểu thức chính quy bằng chữ cái"
-#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "thay thế bằng %s? (y/n/a/q/l/^E/^Y)"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "thay thế bằng %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
-#: ../ex_cmds.c:4379
msgid "(Interrupted) "
msgstr "(bị dừng)"
-#: ../ex_cmds.c:4384
-#, fuzzy
-msgid "1 match"
-msgstr "; tương ứng "
-
-#: ../ex_cmds.c:4384
msgid "1 substitution"
msgstr "1 thay thế"
-#: ../ex_cmds.c:4387
-#, fuzzy, c-format
-msgid "%<PRId64> matches"
-msgstr "%<PRId64> thay đổi"
-
-#: ../ex_cmds.c:4388
#, c-format
-msgid "%<PRId64> substitutions"
-msgstr "%<PRId64> thay thế"
+msgid "%ld substitutions"
+msgstr "%ld thay thế"
-#: ../ex_cmds.c:4392
msgid " on 1 line"
msgstr " trên 1 dòng"
-#: ../ex_cmds.c:4395
#, c-format
-msgid " on %<PRId64> lines"
-msgstr " trên %<PRId64> dòng"
+msgid " on %ld lines"
+msgstr " trên %ld dòng"
-#: ../ex_cmds.c:4438
msgid "E147: Cannot do :global recursive"
msgstr "E147: Không thực hiện được lệnh :global đệ qui"
-#: ../ex_cmds.c:4467
msgid "E148: Regular expression missing from global"
msgstr "E148: Thiếu biểu thức chính quy trong lệnh :global"
-#: ../ex_cmds.c:4508
#, c-format
msgid "Pattern found in every line: %s"
msgstr "Tìm thấy tương ứng trên má»i dòng: %s"
-#: ../ex_cmds.c:4510
-#, fuzzy, c-format
-msgid "Pattern not found: %s"
-msgstr "Không tìm thấy mẫu (pattern)"
-
-#: ../ex_cmds.c:4587
msgid ""
"\n"
"# Last Substitute String:\n"
@@ -1298,342 +818,138 @@ msgstr ""
"# Chuỗi thay thế cuối cùng:\n"
"$"
-#: ../ex_cmds.c:4679
msgid "E478: Don't panic!"
msgstr "E478: Hãy bình tĩnh, đừng hoảng hốt!"
-#: ../ex_cmds.c:4717
#, c-format
msgid "E661: Sorry, no '%s' help for %s"
msgstr "E661: Rất tiếc, không có trợ giúp '%s' cho %s"
-#: ../ex_cmds.c:4719
#, c-format
msgid "E149: Sorry, no help for %s"
msgstr "E149: Rất tiếc không có trợ giúp cho %s"
-#: ../ex_cmds.c:4751
#, c-format
msgid "Sorry, help file \"%s\" not found"
msgstr "Xin lỗi, không tìm thấy tập tin trợ giúp \"%s\""
-#: ../ex_cmds.c:5323
#, c-format
msgid "E150: Not a directory: %s"
msgstr "E150: %s không phải là một thư mục"
-#: ../ex_cmds.c:5446
#, c-format
msgid "E152: Cannot open %s for writing"
msgstr "E152: Không thể mở %s để ghi"
-#: ../ex_cmds.c:5471
#, c-format
msgid "E153: Unable to open %s for reading"
msgstr "E153: Không thể mở %s để Ä‘á»c"
-#: ../ex_cmds.c:5500
#, c-format
msgid "E670: Mix of help file encodings within a language: %s"
msgstr ""
"E670: Tập tin trợ giúp sá»­ dụng nhiá»u bảng mã khác nhau cho má»™t ngôn ngữ: %s"
-#: ../ex_cmds.c:5565
-#, fuzzy, c-format
-msgid "E154: Duplicate tag \"%s\" in file %s/%s"
+#, c-format
+msgid "E154: Duplicate tag \"%s\" in file %s"
msgstr "E154: Thẻ ghi lặp lại \"%s\" trong tập tin %s"
-#: ../ex_cmds.c:5687
#, c-format
msgid "E160: Unknown sign command: %s"
msgstr "E160: Câu lệnh ký hiệu không biết: %s"
-#: ../ex_cmds.c:5704
msgid "E156: Missing sign name"
msgstr "E156: Thiếu tên ký hiệu"
-#: ../ex_cmds.c:5746
msgid "E612: Too many signs defined"
msgstr "E612: Äịnh nghÄ©a quá nhiá»u ký hiệu"
-#: ../ex_cmds.c:5813
#, c-format
msgid "E239: Invalid sign text: %s"
msgstr "E239: Văn bản ký hiệu không thích hợp: %s"
-#: ../ex_cmds.c:5844 ../ex_cmds.c:6035
#, c-format
msgid "E155: Unknown sign: %s"
msgstr "E155: Ký hiệu không biết: %s"
-#: ../ex_cmds.c:5877
msgid "E159: Missing sign number"
msgstr "E159: Thiếu số của ký hiệu"
-#: ../ex_cmds.c:5971
#, c-format
msgid "E158: Invalid buffer name: %s"
msgstr "E158: Tên bộ đệm không đúng: %s"
-#: ../ex_cmds.c:6008
#, c-format
-msgid "E157: Invalid sign ID: %<PRId64>"
-msgstr "E157: ID của ký hiệu không đúng: %<PRId64>"
+msgid "E157: Invalid sign ID: %ld"
+msgstr "E157: ID của ký hiệu không đúng: %ld"
+
+msgid " (NOT FOUND)"
+msgstr " (KHÔNG TÌM THẤY)"
-#: ../ex_cmds.c:6066
msgid " (not supported)"
msgstr " (không được hỗ trợ)"
-#: ../ex_cmds.c:6169
msgid "[Deleted]"
msgstr "[bị xóa]"
-#: ../ex_cmds2.c:139
-msgid "Entering Debug mode. Type \"cont\" to continue."
-msgstr "Bật chế độ sửa lỗi (Debug). Gõ \"cont\" để tiếp tục."
-
-#: ../ex_cmds2.c:143 ../ex_docmd.c:759
-#, c-format
-msgid "line %<PRId64>: %s"
-msgstr "dòng %<PRId64>: %s"
-
-#: ../ex_cmds2.c:145
-#, c-format
-msgid "cmd: %s"
-msgstr "câu lệnh: %s"
-
-#: ../ex_cmds2.c:322
-#, c-format
-msgid "Breakpoint in \"%s%s\" line %<PRId64>"
-msgstr "Äiểm dừng trên \"%s%s\" dòng %<PRId64>"
-
-#: ../ex_cmds2.c:581
-#, c-format
-msgid "E161: Breakpoint not found: %s"
-msgstr "E161: Không tìm thấy điểm dừng: %s"
-
-#: ../ex_cmds2.c:611
-msgid "No breakpoints defined"
-msgstr "Äiểm dừng không được xác định"
-
-#: ../ex_cmds2.c:617
-#, c-format
-msgid "%3d %s %s line %<PRId64>"
-msgstr "%3d %s %s dòng %<PRId64>"
-
-#: ../ex_cmds2.c:942
-msgid "E750: First use \":profile start {fname}\""
-msgstr ""
-
-#: ../ex_cmds2.c:1269
-#, fuzzy, c-format
-msgid "Save changes to \"%s\"?"
-msgstr "Ghi nhớ thay đổi vào \"%.*s\"?"
-
-#: ../ex_cmds2.c:1271 ../ex_docmd.c:8851
-msgid "Untitled"
-msgstr "Chưa đặt tên"
-
-#: ../ex_cmds2.c:1421
-#, c-format
-msgid "E162: No write since last change for buffer \"%s\""
-msgstr "E162: Thay đổi chưa được ghi nhớ trong bộ đệm \"%s\""
-
-#: ../ex_cmds2.c:1480
-msgid "Warning: Entered other buffer unexpectedly (check autocommands)"
-msgstr ""
-"Cảnh báo: Chuyển tới bộ đệm khác không theo ý muốn (hãy kiểm tra câu lệnh tự "
-"động)"
-
-#: ../ex_cmds2.c:1826
-msgid "E163: There is only one file to edit"
-msgstr "E163: Chỉ có một tập tin để soạn thảo"
-
-#: ../ex_cmds2.c:1828
-msgid "E164: Cannot go before first file"
-msgstr "E164: Äây là tập tin đầu tiên"
-
-#: ../ex_cmds2.c:1830
-msgid "E165: Cannot go beyond last file"
-msgstr "E165: Äây là tập tin cuối cùng"
-
-#: ../ex_cmds2.c:2175
-#, c-format
-msgid "E666: compiler not supported: %s"
-msgstr "E666: trình biên dịch không được hỗ trợ: %s"
-
-#: ../ex_cmds2.c:2257
-#, c-format
-msgid "Searching for \"%s\" in \"%s\""
-msgstr "Tìm kiếm \"%s\" trong \"%s\""
-
-#: ../ex_cmds2.c:2284
-#, c-format
-msgid "Searching for \"%s\""
-msgstr "Tìm kiếm \"%s\""
-
-#: ../ex_cmds2.c:2307
-#, c-format
-msgid "not found in 'runtimepath': \"%s\""
-msgstr "không tìm thấy trong 'runtimepath': \"%s\""
-
-#: ../ex_cmds2.c:2472
-#, c-format
-msgid "Cannot source a directory: \"%s\""
-msgstr "Không thể thực hiện một thư mục: \"%s\""
-
-#: ../ex_cmds2.c:2518
-#, c-format
-msgid "could not source \"%s\""
-msgstr "không thực hiện được \"%s\""
-
-#: ../ex_cmds2.c:2520
-#, c-format
-msgid "line %<PRId64>: could not source \"%s\""
-msgstr "dòng %<PRId64>: không thực hiện được \"%s\""
-
-#: ../ex_cmds2.c:2535
-#, c-format
-msgid "sourcing \"%s\""
-msgstr "thực hiện \"%s\""
-
-#: ../ex_cmds2.c:2537
-#, c-format
-msgid "line %<PRId64>: sourcing \"%s\""
-msgstr "dòng %<PRId64>: thực hiện \"%s\""
-
-#: ../ex_cmds2.c:2693
-#, c-format
-msgid "finished sourcing %s"
-msgstr "thực hiện xong %s"
-
-#: ../ex_cmds2.c:2765
-#, fuzzy
-msgid "modeline"
-msgstr "Thêm 1 dòng"
-
-#: ../ex_cmds2.c:2767
-#, fuzzy
-msgid "--cmd argument"
-msgstr " vim [các tham số] "
-
-#: ../ex_cmds2.c:2769
-#, fuzzy
-msgid "-c argument"
-msgstr " vim [các tham số] "
-
-#: ../ex_cmds2.c:2771
-msgid "environment variable"
-msgstr ""
-
-#: ../ex_cmds2.c:2773
-#, fuzzy
-msgid "error handler"
-msgstr "Lỗi và sự gián đoạn"
-
-#: ../ex_cmds2.c:3020
-msgid "W15: Warning: Wrong line separator, ^M may be missing"
-msgstr "W15: Cảnh báo: Ký tự phân cách dòng không đúng. Rất có thể thiếu ^M"
-
-#: ../ex_cmds2.c:3139
-msgid "E167: :scriptencoding used outside of a sourced file"
-msgstr "E167: Lệnh :scriptencoding sử dụng ngoài tập tin script"
-
-#: ../ex_cmds2.c:3166
-msgid "E168: :finish used outside of a sourced file"
-msgstr "E168: Lệnh :finish sử dụng ngoài tập tin script"
-
-#: ../ex_cmds2.c:3389
-#, c-format
-msgid "Current %slanguage: \"%s\""
-msgstr "Ngôn ngữ %shiện thá»i: \"%s\""
-
-#: ../ex_cmds2.c:3404
-#, c-format
-msgid "E197: Cannot set language to \"%s\""
-msgstr "E197: Không thể thay đổi ngôn ngữ thành \"%s\""
-
-#. don't redisplay the window
-#. don't wait for return
-#: ../ex_docmd.c:387
msgid "Entering Ex mode. Type \"visual\" to go to Normal mode."
msgstr ""
"Chuyển vào chế độ Ex. Äể chuyển vá» chế độ Thông thưá»ng hãy gõ \"visual\""
-#: ../ex_docmd.c:428
msgid "E501: At end-of-file"
msgstr "E501: Ở cuối tập tin"
-#: ../ex_docmd.c:513
msgid "E169: Command too recursive"
msgstr "E169: Câu lệnh quá đệ quy"
-#: ../ex_docmd.c:1006
#, c-format
msgid "E605: Exception not caught: %s"
msgstr "E605: Trưá»ng hợp đặc biệt không được xá»­ lý: %s"
-#: ../ex_docmd.c:1085
msgid "End of sourced file"
msgstr "Kết thúc tập tin script"
-#: ../ex_docmd.c:1086
msgid "End of function"
msgstr "Kết thúc của hàm số"
-#: ../ex_docmd.c:1628
msgid "E464: Ambiguous use of user-defined command"
msgstr "E464: Sá»± sá»­ dụng không rõ ràng câu lệnh do ngưá»i dùng định nghÄ©a"
-#: ../ex_docmd.c:1638
msgid "E492: Not an editor command"
msgstr "E492: Không phải là câu lệnh của trình soạn thảo"
-#: ../ex_docmd.c:1729
msgid "E493: Backwards range given"
msgstr "E493: ÄÆ°a ra phạm vi ngược lại"
-#: ../ex_docmd.c:1733
msgid "Backwards range given, OK to swap"
msgstr "ÄÆ°a ra phạm vi ngược lại, thay đổi vị trí hai giá»›i hạn"
-#. append
-#. typed wrong
-#: ../ex_docmd.c:1787
msgid "E494: Use w or w>>"
msgstr "E494: Hãy sử dụng w hoặc w>>"
-#: ../ex_docmd.c:3454
-msgid "E319: The command is not available in this version"
+msgid "E319: Sorry, the command is not available in this version"
msgstr "E319: Xin lỗi, câu lệnh này không có trong phiên bản này"
-#: ../ex_docmd.c:3752
msgid "E172: Only one file name allowed"
msgstr "E172: Chỉ cho phép sử dụng một tên tập tin"
-#: ../ex_docmd.c:4238
msgid "1 more file to edit. Quit anyway?"
msgstr "Còn 1 tập tin nữa cần soạn thảo. Thoát?"
-#: ../ex_docmd.c:4242
#, c-format
msgid "%d more files to edit. Quit anyway?"
msgstr "Còn %d tập tin nữa chưa soạn thảo. Thoát?"
-#: ../ex_docmd.c:4248
msgid "E173: 1 more file to edit"
msgstr "E173: 1 tập tin nữa chỠsoạn thảo."
-#: ../ex_docmd.c:4250
#, c-format
-msgid "E173: %<PRId64> more files to edit"
-msgstr "E173: %<PRId64> tập tin nữa chưa soạn thảo."
+msgid "E173: %ld more files to edit"
+msgstr "E173: %ld tập tin nữa chưa soạn thảo."
-#: ../ex_docmd.c:4320
msgid "E174: Command already exists: add ! to replace it"
msgstr "E174: Äã có câu lệnh: Thêm ! để thay thế"
-#: ../ex_docmd.c:4432
msgid ""
"\n"
" Name Args Range Complete Definition"
@@ -1641,352 +957,252 @@ msgstr ""
"\n"
" Tên\t\tTham_số Phạm_vi Phần_phụ Äịnh_nghÄ©a"
-#: ../ex_docmd.c:4516
msgid "No user-defined commands found"
msgstr "Không tìm thấy câu lệnh do ngưá»i dùng định nghÄ©a"
-#: ../ex_docmd.c:4538
msgid "E175: No attribute specified"
msgstr "E175: Không có tham số được chỉ ra"
-#: ../ex_docmd.c:4583
msgid "E176: Invalid number of arguments"
msgstr "E176: Số lượng tham số không đúng"
-#: ../ex_docmd.c:4594
msgid "E177: Count cannot be specified twice"
msgstr "E177: Số đếm không thể được chỉ ra hai lần"
-#: ../ex_docmd.c:4603
msgid "E178: Invalid default value for count"
msgstr "E178: Giá trị của số đếm theo mặc định không đúng"
-#: ../ex_docmd.c:4625
-#, fuzzy
-msgid "E179: argument required for -complete"
+# TODO: Capitalise first word of message?
+msgid "E179: Argument required for complete"
msgstr "E179: yêu cầu đưa ra tham số để kết thúc"
-#: ../ex_docmd.c:4635
+#, c-format
+msgid "E180: Invalid complete value: %s"
+msgstr "E180: Giá trị phần phụ không đúng: %s"
+
+msgid "E468: Completion argument only allowed for custom completion"
+msgstr ""
+"E468: Tham số tự động kết thúc chỉ cho phép sử dụng với phần phụ đặc biệt"
+
+msgid "E467: Custom completion requires a function argument"
+msgstr "E467: Phần phục đặc biệt yêu cầu một tham số của hàm"
+
#, c-format
msgid "E181: Invalid attribute: %s"
msgstr "E181: Thuộc tính không đúng: %s"
-#: ../ex_docmd.c:4678
msgid "E182: Invalid command name"
msgstr "E182: Tên câu lệnh không đúng"
-#: ../ex_docmd.c:4691
msgid "E183: User defined commands must start with an uppercase letter"
msgstr "E183: Câu lệnh ngưá»i dùng định nghÄ©a phải bắt đầu vá»›i má»™t ký tá»± hoa"
-#: ../ex_docmd.c:4696
-#, fuzzy
-msgid "E841: Reserved name, cannot be used for user defined command"
-msgstr "E464: Sá»± sá»­ dụng không rõ ràng câu lệnh do ngưá»i dùng định nghÄ©a"
-
-#: ../ex_docmd.c:4751
#, c-format
msgid "E184: No such user-defined command: %s"
msgstr "E184: Không có câu lệnh ngưá»i dùng định nghÄ©a như vậy: %s"
-#: ../ex_docmd.c:5219
#, c-format
-msgid "E180: Invalid complete value: %s"
-msgstr "E180: Giá trị phần phụ không đúng: %s"
-
-#: ../ex_docmd.c:5225
-msgid "E468: Completion argument only allowed for custom completion"
-msgstr ""
-"E468: Tham số tự động kết thúc chỉ cho phép sử dụng với phần phụ đặc biệt"
-
-#: ../ex_docmd.c:5231
-msgid "E467: Custom completion requires a function argument"
-msgstr "E467: Phần phục đặc biệt yêu cầu một tham số của hàm"
-
-#: ../ex_docmd.c:5257
-#, fuzzy, c-format
-msgid "E185: Cannot find color scheme '%s'"
+msgid "E185: Cannot find color scheme %s"
msgstr "E185: Không tin thấy sơ đồ màu sắc %s"
-#: ../ex_docmd.c:5263
msgid "Greetings, Vim user!"
msgstr "Xin chào ngưá»i dùng Vim!"
-#: ../ex_docmd.c:5431
-#, fuzzy
-msgid "E784: Cannot close last tab page"
-msgstr "E444: Không được đóng cửa sổ cuối cùng"
-
-#: ../ex_docmd.c:5462
-#, fuzzy
-msgid "Already only one tab page"
-msgstr "Chỉ có một cửa sổ"
-
-#: ../ex_docmd.c:6004
-#, fuzzy, c-format
-msgid "Tab page %d"
-msgstr "Trang %d"
+msgid "Edit File in new window"
+msgstr "Soạn thảo tập tin trong cửa sổ mới"
-#: ../ex_docmd.c:6295
msgid "No swap file"
msgstr "Không có tập tin swap"
-#: ../ex_docmd.c:6478
-#, fuzzy
-msgid "E747: Cannot change directory, buffer is modified (add ! to override)"
-msgstr ""
-"E509: Không tạo được tập tin lưu trữ (thêm ! để bỠqua việc kiểm tra lại)"
+msgid "Append File"
+msgstr "Thêm tập tin"
-#: ../ex_docmd.c:6485
msgid "E186: No previous directory"
msgstr "E186: Không có thư mục trước"
-#: ../ex_docmd.c:6530
msgid "E187: Unknown"
msgstr "E187: Không rõ"
-#: ../ex_docmd.c:6610
msgid "E465: :winsize requires two number arguments"
msgstr "E465: câu lệnh :winsize yêu cầu hai tham số bằng số"
-#: ../ex_docmd.c:6655
+#, c-format
+msgid "Window position: X %d, Y %d"
+msgstr "Vị trí cửa sổ: X %d, Y %d"
+
msgid "E188: Obtaining window position not implemented for this platform"
msgstr "E188: Trên hệ thống này việc xác định vị trí cửa sổ không làm việc"
-#: ../ex_docmd.c:6662
msgid "E466: :winpos requires two number arguments"
msgstr "E466: câu lệnh :winpos yêu câu hai tham số bằng số"
-#: ../ex_docmd.c:7241
-#, fuzzy, c-format
-msgid "E739: Cannot create directory: %s"
-msgstr "Không thể thực hiện một thư mục: \"%s\""
+msgid "Save Redirection"
+msgstr "Chuyển hướng ghi nhớ"
+
+msgid "Save View"
+msgstr "Ghi nhớ vẻ ngoài"
+
+msgid "Save Session"
+msgstr "Ghi nhớ buổi làm việc"
+
+msgid "Save Setup"
+msgstr "Ghi nhớ cấu hình"
-#: ../ex_docmd.c:7268
#, c-format
msgid "E189: \"%s\" exists (add ! to override)"
msgstr "E189: \"%s\" đã có (thêm !, để ghi đè)"
-#: ../ex_docmd.c:7273
#, c-format
msgid "E190: Cannot open \"%s\" for writing"
msgstr "E190: Không mở được \"%s\" để ghi nhớ"
-#. set mark
-#: ../ex_docmd.c:7294
msgid "E191: Argument must be a letter or forward/backward quote"
msgstr "E191: Tham số phải là một chữ cái hoặc dấu ngoặc thẳng/ngược"
-#: ../ex_docmd.c:7333
msgid "E192: Recursive use of :normal too deep"
msgstr "E192: Sử dụng đệ quy lệnh :normal quá sâu"
-#: ../ex_docmd.c:7807
msgid "E194: No alternate file name to substitute for '#'"
msgstr "E194: Không có tên tập tin tương đương để thay thế '#'"
-#: ../ex_docmd.c:7841
-msgid "E495: no autocommand file name to substitute for \"<afile>\""
+# TODO: Capitalise first word of message?
+msgid "E495: No autocommand file name to substitute for \"<afile>\""
msgstr "E495: Không có tên tập tin câu lệnh tự động để thay thế \"<afile>\""
-#: ../ex_docmd.c:7850
-msgid "E496: no autocommand buffer number to substitute for \"<abuf>\""
+# TODO: Capitalise first word of message?
+msgid "E496: No autocommand buffer number to substitute for \"<abuf>\""
msgstr ""
"E496: Không có số thứ tự bộ đệm câu lệnh tự động để thay thế \"<abuf>\""
-#: ../ex_docmd.c:7861
-msgid "E497: no autocommand match name to substitute for \"<amatch>\""
+# TODO: Capitalise first word of message?
+msgid "E497: No autocommand match name to substitute for \"<amatch>\""
msgstr "E497: Không có tên tương ứng câu lệnh tự động để thay thế \"<amatch>\""
-#: ../ex_docmd.c:7870
-msgid "E498: no :source file name to substitute for \"<sfile>\""
-msgstr "E498: không có tên tập tin :source để thay thế \"<sfile>\""
-
-#: ../ex_docmd.c:7876
-#, fuzzy
-msgid "E842: no line number to use for \"<slnum>\""
+# TODO: Capitalise first word of message?
+msgid "E498: No :source file name to substitute for \"<sfile>\""
msgstr "E498: không có tên tập tin :source để thay thế \"<sfile>\""
-#: ../ex_docmd.c:7903
-#, fuzzy, c-format
+#, no-c-format
msgid "E499: Empty file name for '%' or '#', only works with \":p:h\""
msgstr "E499: Tên tập tin rỗng cho '%' hoặc '#', chỉ làm việc với \":p:h\""
-#: ../ex_docmd.c:7905
msgid "E500: Evaluates to an empty string"
msgstr "E500: Kết quả của biểu thức là một chuỗi rỗng"
-#: ../ex_docmd.c:8838
msgid "E195: Cannot open viminfo file for reading"
msgstr "E195: Không thể mở tập tin viminfo để Ä‘á»c"
-#: ../ex_eval.c:464
+msgid "E196: No digraphs in this version"
+msgstr "E196: Trong phiên bản này chữ ghép không được hỗ trợ"
+
msgid "E608: Cannot :throw exceptions with 'Vim' prefix"
msgstr ""
"E608: Không thể thá»±c hiện lệnh :throw cho những ngoại lệ vá»›i tiá»n tố 'Vim'"
-#. always scroll up, don't overwrite
-#: ../ex_eval.c:496
#, c-format
msgid "Exception thrown: %s"
msgstr "Trưá»ng hợp ngoại lệ: %s"
-#: ../ex_eval.c:545
#, c-format
msgid "Exception finished: %s"
msgstr "Kết thúc việc xá»­ lý trưá»ng hợp ngoại lệ: %s"
-#: ../ex_eval.c:546
#, c-format
msgid "Exception discarded: %s"
msgstr "Trưá»ng hợp ngoại lệ bị bá» qua: %s"
-#: ../ex_eval.c:588 ../ex_eval.c:634
#, c-format
-msgid "%s, line %<PRId64>"
-msgstr "%s, dòng %<PRId64>"
+msgid "%s, line %ld"
+msgstr "%s, dòng %ld"
-#. always scroll up, don't overwrite
-#: ../ex_eval.c:608
#, c-format
msgid "Exception caught: %s"
msgstr "Xá»­ lý trưá»ng hợp ngoại lệ: %s"
-#: ../ex_eval.c:676
#, c-format
msgid "%s made pending"
msgstr "%s thực hiện việc chỠđợi"
-#: ../ex_eval.c:679
#, c-format
msgid "%s resumed"
msgstr "%s được phục hồi lại"
-#: ../ex_eval.c:683
#, c-format
msgid "%s discarded"
msgstr "%s bị bỠqua"
-#: ../ex_eval.c:708
msgid "Exception"
msgstr "Trưá»ng hợp ngoại lệ"
-#: ../ex_eval.c:713
msgid "Error and interrupt"
msgstr "Lỗi và sự gián đoạn"
-#: ../ex_eval.c:715
msgid "Error"
msgstr "Lá»—i"
-#. if (pending & CSTP_INTERRUPT)
-#: ../ex_eval.c:717
msgid "Interrupt"
msgstr "Sự gián đoạn"
-#: ../ex_eval.c:795
+# TODO: Capitalise first word of message?
msgid "E579: :if nesting too deep"
msgstr "E579: :if xếp lồng vào nhau quá sâu"
-#: ../ex_eval.c:830
msgid "E580: :endif without :if"
msgstr "E580: :endif không có :if"
-#: ../ex_eval.c:873
msgid "E581: :else without :if"
msgstr "E581: :else không có :if"
-#: ../ex_eval.c:876
msgid "E582: :elseif without :if"
msgstr "E582: :elseif không có :if"
-#: ../ex_eval.c:880
-msgid "E583: multiple :else"
+# TODO: Capitalise first word of message?
+msgid "E583: Multiple :else"
msgstr "E583: phát hiện vài :else"
-#: ../ex_eval.c:883
msgid "E584: :elseif after :else"
msgstr "E584: :elseif sau :else"
-#: ../ex_eval.c:941
-#, fuzzy
-msgid "E585: :while/:for nesting too deep"
+msgid "E585: :while nesting too deep"
msgstr "E585: :while xếp lồng vào nhau quá sâu"
-#: ../ex_eval.c:1028
-#, fuzzy
-msgid "E586: :continue without :while or :for"
+msgid "E586: :continue without :while"
msgstr "E586: :continue không có :while"
-#: ../ex_eval.c:1061
-#, fuzzy
-msgid "E587: :break without :while or :for"
+msgid "E587: :break without :while"
msgstr "E587: :break không có :while"
-#: ../ex_eval.c:1102
-#, fuzzy
-msgid "E732: Using :endfor with :while"
-msgstr "E170: Thiếu câu lệnh :endwhile"
-
-#: ../ex_eval.c:1104
-#, fuzzy
-msgid "E733: Using :endwhile with :for"
-msgstr "E170: Thiếu câu lệnh :endwhile"
-
-#: ../ex_eval.c:1247
msgid "E601: :try nesting too deep"
msgstr "E601: :try xếp lồng vào nhau quá sâu"
-#: ../ex_eval.c:1317
msgid "E603: :catch without :try"
msgstr "E603: :catch không có :try"
-#. Give up for a ":catch" after ":finally" and ignore it.
-#. * Just parse.
-#: ../ex_eval.c:1332
msgid "E604: :catch after :finally"
msgstr "E604: :catch đứng sau :finally"
-#: ../ex_eval.c:1451
msgid "E606: :finally without :try"
msgstr "E606: :finally không có :try"
-#. Give up for a multiple ":finally" and ignore it.
-#: ../ex_eval.c:1467
-msgid "E607: multiple :finally"
+# TODO: Capitalise first word of message?
+msgid "E607: Multiple :finally"
msgstr "E607: phát hiện vài :finally"
-#: ../ex_eval.c:1571
msgid "E602: :endtry without :try"
msgstr "E602: :endtry không có :try"
-#: ../ex_eval.c:2026
msgid "E193: :endfunction not inside a function"
msgstr "E193: lệnh :endfunction chỉ được sử dụng trong một hàm số"
-#: ../ex_getln.c:1643
-#, fuzzy
-msgid "E788: Not allowed to edit another buffer now"
-msgstr "E48: Không cho phép trong hộp cát (sandbox)"
-
-#: ../ex_getln.c:1656
-#, fuzzy
-msgid "E811: Not allowed to change buffer information now"
-msgstr "E94: Không có bộ đệm tương ứng với %s"
-
-#: ../ex_getln.c:3178
msgid "tagname"
msgstr "tên thẻ ghi"
-#: ../ex_getln.c:3181
msgid " kind file\n"
msgstr " loại tập tin\n"
-#: ../ex_getln.c:4799
msgid "'history' option is zero"
msgstr "giá trị cá»§a tùy chá»n 'history' bằng không"
-#: ../ex_getln.c:5046
#, c-format
msgid ""
"\n"
@@ -1995,312 +1211,201 @@ msgstr ""
"\n"
"# %s, Lịch sử (bắt đầu từ mới nhất tới cũ nhất):\n"
-#: ../ex_getln.c:5047
msgid "Command Line"
msgstr "Dòng lệnh"
-#: ../ex_getln.c:5048
msgid "Search String"
msgstr "Chuỗi tìm kiếm"
-#: ../ex_getln.c:5049
msgid "Expression"
msgstr "Biểu thức"
-#: ../ex_getln.c:5050
msgid "Input Line"
msgstr "Dòng nhập"
-#: ../ex_getln.c:5117
msgid "E198: cmd_pchar beyond the command length"
msgstr "E198: cmd_pchar lá»›n hÆ¡n chiá»u dài câu lệnh"
-#: ../ex_getln.c:5279
msgid "E199: Active window or buffer deleted"
msgstr "E199: Cửa sổ hoặc bộ đệm hoạt động bị xóa"
-#: ../file_search.c:203
-msgid "E854: path too long for completion"
-msgstr ""
-
-#: ../file_search.c:446
-#, c-format
-msgid ""
-"E343: Invalid path: '**[number]' must be at the end of the path or be "
-"followed by '%s'."
-msgstr ""
-"E343: ÄÆ°á»ng dẫn đưa ra không đúng: '**[số]' phải ở cuối đưá»ng dẫn hoặc theo "
-"sau bởi '%s'"
-
-#: ../file_search.c:1505
-#, c-format
-msgid "E344: Can't find directory \"%s\" in cdpath"
-msgstr "E344: Không tìm thấy thư mục \"%s\" để chuyển thư mục"
-
-#: ../file_search.c:1508
-#, c-format
-msgid "E345: Can't find file \"%s\" in path"
-msgstr "E345: Không tìm thấy tập tin \"%s\" trong đưá»ng dẫn"
-
-#: ../file_search.c:1512
-#, c-format
-msgid "E346: No more directory \"%s\" found in cdpath"
-msgstr "E346: Trong đưá»ng dẫn thay đổi thư mục không còn có thư mục \"%s\" nữa"
-
-#: ../file_search.c:1515
-#, c-format
-msgid "E347: No more file \"%s\" found in path"
-msgstr "E347: Trong đưá»ng dẫn path không còn có tập tin \"%s\" nữa"
-
-#: ../fileio.c:137
-#, fuzzy
-msgid "E812: Autocommands changed buffer or buffer name"
-msgstr "E135: Các lệnh tá»± động *Filter* không được thay đổi bá»™ đệm hiện thá»i"
-
-#: ../fileio.c:368
msgid "Illegal file name"
msgstr "Tên tập tin không cho phép"
-#: ../fileio.c:395 ../fileio.c:476 ../fileio.c:2543 ../fileio.c:2578
msgid "is a directory"
msgstr "là một thư mục"
-#: ../fileio.c:397
msgid "is not a file"
msgstr "không phải là một tập tin"
-#: ../fileio.c:508 ../fileio.c:3522
msgid "[New File]"
msgstr "[Tập tin mới]"
-#: ../fileio.c:511
-msgid "[New DIRECTORY]"
-msgstr ""
-
-#: ../fileio.c:529 ../fileio.c:532
-msgid "[File too big]"
-msgstr ""
-
-#: ../fileio.c:534
msgid "[Permission Denied]"
msgstr "[Truy cập bị từ chối]"
-#: ../fileio.c:653
msgid "E200: *ReadPre autocommands made the file unreadable"
msgstr ""
"E200: Câu lệnh tá»± động *ReadPre làm cho tập tin trở thành không thể Ä‘á»c"
-#: ../fileio.c:655
msgid "E201: *ReadPre autocommands must not change current buffer"
msgstr "E201: Câu lệnh tự động *ReadPre không được thay đổi bộ đệm hoạt động"
-#: ../fileio.c:672
-msgid "Nvim: Reading from stdin...\n"
+msgid "Vim: Reading from stdin...\n"
msgstr "Vim: Äá»c từ đầu vào tiêu chuẩn stdin...\n"
-#. Re-opening the original file failed!
-#: ../fileio.c:909
+msgid "Reading from stdin..."
+msgstr "Äá»c từ đầu vào tiêu chuẩn stdin..."
+
msgid "E202: Conversion made file unreadable!"
msgstr "E202: Sá»± biến đổi làm cho tập tin trở thành không thể Ä‘á»c!"
-#. fifo or socket
-#: ../fileio.c:1782
msgid "[fifo/socket]"
msgstr "[fifo/socket]"
-#. fifo
-#: ../fileio.c:1788
msgid "[fifo]"
msgstr "[fifo]"
-#. or socket
-#: ../fileio.c:1794
msgid "[socket]"
msgstr "[socket]"
-#. or character special
-#: ../fileio.c:1801
-#, fuzzy
-msgid "[character special]"
-msgstr "1 ký tự"
+msgid "[RO]"
+msgstr "[Chỉ Ä‘á»c]"
-#: ../fileio.c:1815
msgid "[CR missing]"
msgstr "[thiếu ký tự CR]"
-#: ../fileio.c:1819
+msgid "[NL found]"
+msgstr "[tìm thấy ký tự NL]"
+
msgid "[long lines split]"
msgstr "[dòng dài được chia nhá»]"
-#: ../fileio.c:1823 ../fileio.c:3512
msgid "[NOT converted]"
msgstr "[KHÔNG được chuyển đổi]"
-#: ../fileio.c:1826 ../fileio.c:3515
msgid "[converted]"
msgstr "[đã chuyển bảng mã]"
-#: ../fileio.c:1831
-#, fuzzy, c-format
-msgid "[CONVERSION ERROR in line %<PRId64>]"
-msgstr "[BYTE KHÔNG CHO PHÉP trên dòng %<PRId64>]"
+msgid "[crypted]"
+msgstr "[đã mã hóa]"
+
+msgid "[CONVERSION ERROR]"
+msgstr "[LỖI CHUYỂN BẢNG MÃ]"
-#: ../fileio.c:1835
#, c-format
-msgid "[ILLEGAL BYTE in line %<PRId64>]"
-msgstr "[BYTE KHÔNG CHO PHÉP trên dòng %<PRId64>]"
+msgid "[ILLEGAL BYTE in line %ld]"
+msgstr "[BYTE KHÔNG CHO PHÉP trên dòng %ld]"
-#: ../fileio.c:1838
msgid "[READ ERRORS]"
msgstr "[Lá»–I ÄỌC]"
-#: ../fileio.c:2104
msgid "Can't find temp file for conversion"
msgstr "Không tìm thấy tập tin tạm thá»i (temp) để chuyển bảng mã"
-#: ../fileio.c:2110
msgid "Conversion with 'charconvert' failed"
msgstr "Chuyển đổi nhỠ'charconvert' không được thực hiện"
-#: ../fileio.c:2113
msgid "can't read output of 'charconvert'"
msgstr "không Ä‘á»c được đầu ra cá»§a 'charconvert'"
-#: ../fileio.c:2437
-#, fuzzy
-msgid "E676: No matching autocommands for acwrite buffer"
-msgstr "Không có câu lệnh tự động tương ứng"
-
-#: ../fileio.c:2466
msgid "E203: Autocommands deleted or unloaded buffer to be written"
msgstr "E203: Câu lệnh tự động đã xóa hoặc bỠnạp bộ đệm cần ghi nhớ"
-#: ../fileio.c:2486
msgid "E204: Autocommand changed number of lines in unexpected way"
msgstr "E204: Câu lệnh tự động đã thay đổ số dòng theo cách không mong muốn"
-#: ../fileio.c:2548 ../fileio.c:2565
+msgid "NetBeans disallows writes of unmodified buffers"
+msgstr "NetBeans không cho phép ghi nhớ bộ đệm chưa có thay đổi nào"
+
+msgid "Partial writes disallowed for NetBeans buffers"
+msgstr "Ghi nhớ một phần bộ đệm NetBeans không được cho phép"
+
msgid "is not a file or writable device"
msgstr "không phải là một tập tin thay một thiết bị có thể ghi nhớ"
-#: ../fileio.c:2601
msgid "is read-only (add ! to override)"
msgstr "là tập tin chỉ Ä‘á»c (thêm ! để ghi nhá»› bằng má»i giá)"
-#: ../fileio.c:2886
msgid "E506: Can't write to backup file (add ! to override)"
msgstr ""
"E506: Không thể ghi nhá»› vào tập tin lưu trữ (thêm ! để ghi nhá»› bằng má»i giá"
-#: ../fileio.c:2898
msgid "E507: Close error for backup file (add ! to override)"
msgstr "E507: Lỗi đóng tập tin lưu trữ (thêm ! để bỠqua việc kiểm tra lại)"
-#: ../fileio.c:2901
msgid "E508: Can't read file for backup (add ! to override)"
msgstr ""
"E508: Không Ä‘á»c được tập tin lưu trữ (thêm ! để bá» qua việc kiểm tra lại)"
-#: ../fileio.c:2923
msgid "E509: Cannot create backup file (add ! to override)"
msgstr ""
"E509: Không tạo được tập tin lưu trữ (thêm ! để bỠqua việc kiểm tra lại)"
-#: ../fileio.c:3008
msgid "E510: Can't make backup file (add ! to override)"
msgstr ""
"E510: Không tạo được tập tin lưu trữ (thêm ! để bỠqua việc kiểm tra lại)"
-#. Can't write without a tempfile!
-#: ../fileio.c:3121
msgid "E214: Can't find temp file for writing"
msgstr "E214: Không tìm thấy tập tin tạm thá»i (temp) để ghi nhá»›"
-#: ../fileio.c:3134
msgid "E213: Cannot convert (add ! to write without conversion)"
msgstr ""
"E213: Không thể chuyển đổi bảng mã (thêm ! để ghi nhớ mà không chuyển đổi)"
-#: ../fileio.c:3169
msgid "E166: Can't open linked file for writing"
msgstr "E166: Không thể mở tập tin liên kết để ghi nhớ"
-#: ../fileio.c:3173
msgid "E212: Can't open file for writing"
msgstr "E212: Không thể mở tập tin để ghi nhớ"
-#: ../fileio.c:3363
msgid "E667: Fsync failed"
msgstr "E667: Không thực hiện thành công hàm số fsync()"
-#: ../fileio.c:3398
msgid "E512: Close failed"
msgstr "E512: Thao tác đóng không thành công"
-#: ../fileio.c:3436
-#, fuzzy
-msgid "E513: write error, conversion failed (make 'fenc' empty to override)"
+# TODO: Capitalise first word of message?
+msgid "E513: Write error, conversion failed"
msgstr "E513: Lỗi ghi nhớ, biến đổi không thành công"
-#: ../fileio.c:3441
-#, c-format
-msgid ""
-"E513: write error, conversion failed in line %<PRId64> (make 'fenc' empty to "
-"override)"
-msgstr ""
-
-#: ../fileio.c:3448
-msgid "E514: write error (file system full?)"
+# TODO: Capitalise first word of message?
+msgid "E514: Write error (file system full?)"
msgstr "E514: lỗi ghi nhớ (không còn chỗ trống?)"
-#: ../fileio.c:3506
msgid " CONVERSION ERROR"
msgstr " Lá»–I BIẾN Äá»”I"
-#: ../fileio.c:3509
-#, fuzzy, c-format
-msgid " in line %<PRId64>;"
-msgstr "dòng %<PRId64>"
-
-#: ../fileio.c:3519
msgid "[Device]"
msgstr "[Thiết bị]"
-#: ../fileio.c:3522
msgid "[New]"
msgstr "[Má»›i]"
-#: ../fileio.c:3535
msgid " [a]"
msgstr " [a]"
-#: ../fileio.c:3535
msgid " appended"
msgstr " đã thêm"
-#: ../fileio.c:3537
msgid " [w]"
msgstr " [w]"
-#: ../fileio.c:3537
msgid " written"
msgstr " đã ghi"
-#: ../fileio.c:3579
msgid "E205: Patchmode: can't save original file"
msgstr "E205: Chế độ vá lỗi (patch): không thể ghi nhớ tập tin gốc"
-#: ../fileio.c:3602
-msgid "E206: patchmode: can't touch empty original file"
+# TODO: Capitalise first word of message?
+msgid "E206: Patchmode: can't touch empty original file"
msgstr ""
"E206: Chế độ vá lỗi (patch): không thể thay đổi tham số của tập tin gốc "
"trống rỗng"
-#: ../fileio.c:3616
msgid "E207: Can't delete backup file"
msgstr "E207: Không thể xóa tập tin lưu trữ (backup)"
-#: ../fileio.c:3672
msgid ""
"\n"
"WARNING: Original file may be lost or damaged\n"
@@ -2308,97 +1413,73 @@ msgstr ""
"\n"
"CẢNH BÃO: Tập tin gốc có thể bị mất hoặc bị há»ng\n"
-#: ../fileio.c:3675
msgid "don't quit the editor until the file is successfully written!"
msgstr ""
"đừng thoát khởi trình soạn thảo, khi tập tin còn chưa được ghi nhớ thành cồng"
-#: ../fileio.c:3795
msgid "[dos]"
msgstr "[dos]"
-#: ../fileio.c:3795
msgid "[dos format]"
msgstr "[định dạng dos]"
-#: ../fileio.c:3801
msgid "[mac]"
msgstr "[mac]"
-#: ../fileio.c:3801
msgid "[mac format]"
msgstr "[định dạng mac]"
-#: ../fileio.c:3807
msgid "[unix]"
msgstr "[unix]"
-#: ../fileio.c:3807
msgid "[unix format]"
msgstr "[định dạng unix]"
-#: ../fileio.c:3831
msgid "1 line, "
msgstr "1 dòng, "
-#: ../fileio.c:3833
#, c-format
-msgid "%<PRId64> lines, "
-msgstr "%<PRId64> dòng, "
+msgid "%ld lines, "
+msgstr "%ld dòng, "
-#: ../fileio.c:3836
msgid "1 character"
msgstr "1 ký tự"
-#: ../fileio.c:3838
#, c-format
-msgid "%<PRId64> characters"
-msgstr "%<PRId64> ký tự"
+msgid "%ld characters"
+msgstr "%ld ký tự"
-#: ../fileio.c:3849
msgid "[noeol]"
msgstr "[noeol]"
-#: ../fileio.c:3849
msgid "[Incomplete last line]"
msgstr "[Dòng cuối cùng không đầy đủ]"
-#. don't overwrite messages here
-#. must give this prompt
-#. don't use emsg() here, don't want to flush the buffers
-#: ../fileio.c:3865
msgid "WARNING: The file has been changed since reading it!!!"
msgstr "CẢNH BÃO: Tập tin đã thay đổi so vá»›i thá»i Ä‘iểm Ä‘á»c!!!"
-#: ../fileio.c:3867
msgid "Do you really want to write to it"
msgstr "Bạn có chắc muốn ghi nhớ vào tập tin này"
-#: ../fileio.c:4648
#, c-format
msgid "E208: Error writing to \"%s\""
msgstr "E208: Lỗi ghi nhớ vào \"%s\""
-#: ../fileio.c:4655
#, c-format
msgid "E209: Error closing \"%s\""
msgstr "E209: Lỗi đóng \"%s\""
-#: ../fileio.c:4657
#, c-format
msgid "E210: Error reading \"%s\""
msgstr "E210: Lá»—i Ä‘á»c \"%s\""
-#: ../fileio.c:4883
msgid "E246: FileChangedShell autocommand deleted buffer"
msgstr "E246: Bộ đệm bị xóa khi thực hiện câu lệnh tự động FileChangedShell"
-#: ../fileio.c:4894
-#, fuzzy, c-format
-msgid "E211: File \"%s\" no longer available"
+#, c-format
+msgid "E211: Warning: File \"%s\" no longer available"
msgstr "E211: Cảnh báo: Tập tin \"%s\" không còn truy cập được nữa"
-#: ../fileio.c:4906
#, c-format
msgid ""
"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as "
@@ -2407,44 +1488,28 @@ msgstr ""
"W12: Cảnh báo: Tập tin \"%s\" và bộ đệm Vim đã thay đổi không phụ thuộc vào "
"nhau"
-#: ../fileio.c:4907
-#, fuzzy
-msgid "See \":help W12\" for more info."
-msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
-
-#: ../fileio.c:4910
#, c-format
msgid "W11: Warning: File \"%s\" has changed since editing started"
msgstr ""
"W11: Cảnh báo: Tập tin \"%s\" đã thay đổi sau khi việc soạn thảo bắt đầu"
-#: ../fileio.c:4911
-msgid "See \":help W11\" for more info."
-msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
-
-#: ../fileio.c:4914
#, c-format
msgid "W16: Warning: Mode of file \"%s\" has changed since editing started"
msgstr ""
"W16: Cảnh báo: chế độ truy cập tới tập tin \"%s\" đã thay đổi sau khi bắt "
"đầu soạn thảo"
-#: ../fileio.c:4915
-#, fuzzy
-msgid "See \":help W16\" for more info."
-msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
-
-#: ../fileio.c:4927
#, c-format
msgid "W13: Warning: File \"%s\" has been created after editing started"
msgstr ""
"W13: Cảnh báo: tập tin \"%s\" được tạo ra sau khi việc soạn thảo bắt đầu"
-#: ../fileio.c:4947
+msgid "See \":help W11\" for more info."
+msgstr "Hãy xem thông tin chi tiết trong \":help W11\"."
+
msgid "Warning"
msgstr "Cảnh báo"
-#: ../fileio.c:4948
msgid ""
"&OK\n"
"&Load File"
@@ -2452,48 +1517,33 @@ msgstr ""
"&OK\n"
"&Nạp tập tin"
-#: ../fileio.c:5065
#, c-format
msgid "E462: Could not prepare for reloading \"%s\""
msgstr "E462: Không thể chuẩn bị để nạp lại \"%s\""
-#: ../fileio.c:5078
#, c-format
msgid "E321: Could not reload \"%s\""
msgstr "E321: Không thể nạp lại \"%s\""
-#: ../fileio.c:5601
msgid "--Deleted--"
msgstr "--Bị xóa--"
-#: ../fileio.c:5732
-#, c-format
-msgid "auto-removing autocommand: %s <buffer=%d>"
-msgstr ""
-
-#. the group doesn't exist
-#: ../fileio.c:5772
#, c-format
msgid "E367: No such group: \"%s\""
msgstr "E367: Nhóm \"%s\" không tồn tại"
-#: ../fileio.c:5897
#, c-format
msgid "E215: Illegal character after *: %s"
msgstr "E215: Ký tự không cho phép sau *: %s"
-#: ../fileio.c:5905
#, c-format
msgid "E216: No such event: %s"
msgstr "E216: Sự kiện không có thật: %s"
-#: ../fileio.c:5907
#, c-format
msgid "E216: No such group or event: %s"
msgstr "E216: Nhóm hoặc sự kiện không có thật: %s"
-#. Highlight title
-#: ../fileio.c:6090
msgid ""
"\n"
"--- Autocommands ---"
@@ -2501,681 +1551,400 @@ msgstr ""
"\n"
"--- Câu lệnh tự động ---"
-#: ../fileio.c:6293
-#, fuzzy, c-format
-msgid "E680: <buffer=%d>: invalid buffer number "
-msgstr "số của bộ đệm không đúng"
-
-#: ../fileio.c:6370
msgid "E217: Can't execute autocommands for ALL events"
msgstr "E217: Không thể thực hiện câu lệnh tự động cho MỌI sự kiện"
-#: ../fileio.c:6393
msgid "No matching autocommands"
msgstr "Không có câu lệnh tự động tương ứng"
-#: ../fileio.c:6831
-msgid "E218: autocommand nesting too deep"
+# TODO: Capitalise first word of message?
+msgid "E218: Autocommand nesting too deep"
msgstr "E218: câu lệnh tự động xếp lồng vào nhau quá xâu"
-#: ../fileio.c:7143
#, c-format
msgid "%s Autocommands for \"%s\""
msgstr "%s câu lệnh tự động cho \"%s\""
-#: ../fileio.c:7149
#, c-format
msgid "Executing %s"
msgstr "Thực hiện %s"
-#: ../fileio.c:7211
#, c-format
msgid "autocommand %s"
msgstr "câu lệnh tự động %s"
-#: ../fileio.c:7795
msgid "E219: Missing {."
msgstr "E219: Thiếu {."
-#: ../fileio.c:7797
msgid "E220: Missing }."
msgstr "E220: Thiếu }."
-#: ../fold.c:93
msgid "E490: No fold found"
msgstr "E490: Không tìm thấy nếp gấp"
-#: ../fold.c:544
msgid "E350: Cannot create fold with current 'foldmethod'"
msgstr ""
"E350: Không thể tạo nếp gấp vá»›i giá trị hiện thá»i cá»§a tùy chá»n 'foldmethod'"
-#: ../fold.c:546
msgid "E351: Cannot delete fold with current 'foldmethod'"
msgstr ""
"E351: Không thể xóa nếp gấp vá»›i giá trị hiện thá»i cá»§a tùy chá»n 'foldmethod'"
-#: ../fold.c:1784
-#, c-format
-msgid "+--%3ld lines folded "
-msgstr "+--%3ld dòng được gấp"
-
-#. buffer has already been read
-#: ../getchar.c:273
msgid "E222: Add to read buffer"
msgstr "E222: Thêm vào bá»™ đệm Ä‘ang Ä‘á»c"
-#: ../getchar.c:2040
-msgid "E223: recursive mapping"
+# TODO: Capitalise first word of message?
+msgid "E223: Recursive mapping"
msgstr "E223: ánh xạ đệ quy"
-#: ../getchar.c:2849
-#, c-format
-msgid "E224: global abbreviation already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E224: Global abbreviation already exists for %s"
msgstr "E224: đã có sự viết tắt toàn cầu cho %s"
-#: ../getchar.c:2852
-#, c-format
-msgid "E225: global mapping already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E225: Global mapping already exists for %s"
msgstr "E225: đã có ánh xạ toàn cầu cho %s"
-#: ../getchar.c:2952
-#, c-format
-msgid "E226: abbreviation already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E226: Abbreviation already exists for %s"
msgstr "E226: đã có sự viết tắt cho %s"
-#: ../getchar.c:2955
-#, c-format
-msgid "E227: mapping already exists for %s"
+# TODO: Capitalise first word of message?
+msgid "E227: Mapping already exists for %s"
msgstr "E227: đã có ánh xạ cho %s"
-#: ../getchar.c:3008
msgid "No abbreviation found"
msgstr "Không tìm thấy viết tắt"
-#: ../getchar.c:3010
msgid "No mapping found"
msgstr "Không tìm thấy ánh xạ"
-#: ../getchar.c:3974
msgid "E228: makemap: Illegal mode"
msgstr "E228: makemap: Chế độ không cho phép"
-#. key value of 'cedit' option
-#. type of cmdline window or 0
-#. result of cmdline window or 0
-#: ../globals.h:924
-msgid "--No lines in buffer--"
-msgstr "-- Không có dòng nào trong bộ đệm --"
-
-#.
-#. * The error messages that can be shared are included here.
-#. * Excluded are errors that are only used once and debugging messages.
-#.
-#: ../globals.h:996
-msgid "E470: Command aborted"
-msgstr "E470: Câu lệnh bị dừng"
-
-#: ../globals.h:997
-msgid "E471: Argument required"
-msgstr "E471: Cần chỉ ra tham số"
-
-#: ../globals.h:998
-msgid "E10: \\ should be followed by /, ? or &"
-msgstr "E10: Sau \\ phải là các ký tự /, ? hoặc &"
-
-#: ../globals.h:1000
-msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits"
-msgstr "E11: Lỗi trong cửa sổ dòng lệnh; <CR> thực hiện, CTRL-C thoát"
-
-#: ../globals.h:1002
-msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search"
-msgstr ""
-"E12: Câu lệnh không cho phép từ exrc/vimrc trong thư mục hiện thá»i hoặc "
-"trong tìm kiếm thẻ ghi"
+msgid "<cannot open> "
+msgstr "<không thể mở> "
-#: ../globals.h:1003
-msgid "E171: Missing :endif"
-msgstr "E171: Thiếu câu lệnh :endif"
-
-#: ../globals.h:1004
-msgid "E600: Missing :endtry"
-msgstr "E600: Thiếu câu lệnh :endtry"
-
-#: ../globals.h:1005
-msgid "E170: Missing :endwhile"
-msgstr "E170: Thiếu câu lệnh :endwhile"
+#, c-format
+msgid "E616: vim_SelFile: can't get font %s"
+msgstr "E616: vim_SelFile: không tìm thấy phông chữ %s"
-#: ../globals.h:1006
-#, fuzzy
-msgid "E170: Missing :endfor"
-msgstr "E171: Thiếu câu lệnh :endif"
+msgid "E614: vim_SelFile: can't return to current directory"
+msgstr "E614: vim_SelFile: không trở lại được thư mục hiện thá»i"
-#: ../globals.h:1007
-msgid "E588: :endwhile without :while"
-msgstr "E588: Câu lệnh :endwhile không có lệnh :while (1 cặp)"
+msgid "Pathname:"
+msgstr "ÄÆ°á»ng dẫn tá»›i tập tin:"
-#: ../globals.h:1008
-#, fuzzy
-msgid "E588: :endfor without :for"
-msgstr "E580: :endif không có :if"
+msgid "E615: vim_SelFile: can't get current directory"
+msgstr "E615: vim_SelFile: không tìm thấy thư mục hiện thá»i"
-#: ../globals.h:1009
-msgid "E13: File exists (add ! to override)"
-msgstr "E13: Tập tin đã tồn tại (thêm ! để ghi chèn)"
+msgid "OK"
+msgstr "Äồng ý"
-#: ../globals.h:1010
-msgid "E472: Command failed"
-msgstr "E472: Không thực hiện thành công câu lệnh"
+msgid "Cancel"
+msgstr "Há»§y bá»"
-#: ../globals.h:1011
-msgid "E473: Internal error"
-msgstr "E473: Lá»—i ná»™i bá»™"
+msgid "Vim dialog"
+msgstr "Hộp thoại Vim"
-#: ../globals.h:1012
-msgid "Interrupted"
-msgstr "Bị gián đoạn"
+msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
+msgstr "Thanh cuá»™n: Không thể xác định hình há»c cá»§a thanh cuá»™n."
-#: ../globals.h:1013
-msgid "E14: Invalid address"
-msgstr "E14: Äịa chỉ không cho phép"
+msgid "E232: Cannot create BalloonEval with both message and callback"
+msgstr "E232: Không tạo được BalloonEval vá»›i cả thông báo và lá»i gá»i ngược lại"
-#: ../globals.h:1014
-msgid "E474: Invalid argument"
-msgstr "E474: Tham số không cho phép"
+msgid "E229: Cannot start the GUI"
+msgstr "E229: Không chạy được giao diện đồ há»a GUI"
-#: ../globals.h:1015
#, c-format
-msgid "E475: Invalid argument: %s"
-msgstr "E475: Tham số không cho phép: %s"
+msgid "E230: Cannot read from \"%s\""
+msgstr "E230: Không Ä‘á»c được từ \"%s\""
-#: ../globals.h:1016
-#, c-format
-msgid "E15: Invalid expression: %s"
-msgstr "E15: Biểu thức không cho phép: %s"
+msgid "E665: Cannot start GUI, no valid font found"
+msgstr ""
+"E665: Không chạy được giao diện đồ há»a GUI, đưa ra phông chữ không đúng"
-#: ../globals.h:1017
-msgid "E16: Invalid range"
-msgstr "E16: Vùng không cho phép"
+msgid "E231: 'guifontwide' invalid"
+msgstr "E231: 'guifontwide' có giá trị không đúng"
-#: ../globals.h:1018
-msgid "E476: Invalid command"
-msgstr "E476: Câu lệnh không cho phép"
+msgid "E599: Value of 'imactivatekey' is invalid"
+msgstr "E599: Giá trị của 'imactivatekey' không đúng"
-#: ../globals.h:1019
#, c-format
-msgid "E17: \"%s\" is a directory"
-msgstr "E17: \"%s\" là mộ thư mục"
+msgid "E254: Cannot allocate color %s"
+msgstr "E254: Không chỉ định được màu %s"
-#: ../globals.h:1020
-#, fuzzy
-msgid "E900: Invalid job id"
-msgstr "E49: Kích thước thanh cuộn không cho phép"
+msgid "Vim dialog..."
+msgstr "Hộp thoại Vim..."
-#: ../globals.h:1021
-msgid "E901: Job table is full"
+msgid ""
+"&Yes\n"
+"&No\n"
+"&Cancel"
msgstr ""
+"&Có\n"
+"&Không\n"
+"&Dừng"
-#: ../globals.h:1024
-#, c-format
-msgid "E364: Library call failed for \"%s()\""
-msgstr "E364: Gá»i hàm số \"%s()\" cá»§a thư viện không thành công"
+msgid "Input _Methods"
+msgstr "Phương pháp _nhập liệu"
-#: ../globals.h:1026
-msgid "E19: Mark has invalid line number"
-msgstr "E19: Dấu hiệu chỉ đến một số thứ tự dòng không đúng"
+msgid "VIM - Search and Replace..."
+msgstr "VIM - Tìm kiếm và thay thế..."
-#: ../globals.h:1027
-msgid "E20: Mark not set"
-msgstr "E20: Dấu hiệu không được xác định"
+msgid "VIM - Search..."
+msgstr "VIM - Tìm kiếm..."
-#: ../globals.h:1029
-msgid "E21: Cannot make changes, 'modifiable' is off"
-msgstr "E21: Không thể thay đổi, vì tùy chá»n 'modifiable' bị tắt"
+msgid "Find what:"
+msgstr "Tìm kiếm gì:"
-#: ../globals.h:1030
-msgid "E22: Scripts nested too deep"
-msgstr "E22: Các script lồng vào nhau quá sâu"
+msgid "Replace with:"
+msgstr "Thay thế bởi:"
-#: ../globals.h:1031
-msgid "E23: No alternate file"
-msgstr "E23: Không có tập tin xen kẽ"
+msgid "Match whole word only"
+msgstr "Chỉ tìm tương ứng hoàn toàn với từ"
-#: ../globals.h:1032
-msgid "E24: No such abbreviation"
-msgstr "E24: Không có chữ viết tắt như vậy"
+msgid "Match case"
+msgstr "Có tính kiểu chữ"
-#: ../globals.h:1033
-msgid "E477: No ! allowed"
-msgstr "E477: Không cho phép !"
+msgid "Direction"
+msgstr "Hướng"
-#: ../globals.h:1035
-msgid "E25: Nvim does not have a built-in GUI"
-msgstr "E25: Không sá»­ dụng được giao diện đồ há»a vì không chá»n khi biên dịch"
+msgid "Up"
+msgstr "Lên"
-#: ../globals.h:1036
-#, c-format
-msgid "E28: No such highlight group name: %s"
-msgstr "E28: Nhóm chiếu sáng cú pháp %s không tồn tại"
+msgid "Down"
+msgstr "Xuống"
-#: ../globals.h:1037
-msgid "E29: No inserted text yet"
-msgstr "E29: Tạm thá»i chưa có văn bản được chèn"
+msgid "Find Next"
+msgstr "Tìm tiếp"
-#: ../globals.h:1038
-msgid "E30: No previous command line"
-msgstr "E30: Không có dòng lệnh trước"
+msgid "Replace"
+msgstr "Thay thế"
-#: ../globals.h:1039
-msgid "E31: No such mapping"
-msgstr "E31: Không có ánh xạ (mapping) như vậy"
+msgid "Replace All"
+msgstr "Thay thế tất cả"
-#: ../globals.h:1040
-msgid "E479: No match"
-msgstr "E479: Không có tương ứng"
+msgid "Vim: Received \"die\" request from session manager\n"
+msgstr "Vim: Nhận được yêu cầu \"chết\" (dừng) từ trình quản lý màn hình\n"
-#: ../globals.h:1041
-#, c-format
-msgid "E480: No match: %s"
-msgstr "E480: Không có tương ứng: %s"
+msgid "Vim: Main window unexpectedly destroyed\n"
+msgstr "Vim: Cửa sổ chính đã bị đóng đột ngột\n"
-#: ../globals.h:1042
-msgid "E32: No file name"
-msgstr "E32: Không có tên tập tin"
+msgid "Font Selection"
+msgstr "Chá»n phông chữ"
-#: ../globals.h:1044
-msgid "E33: No previous substitute regular expression"
-msgstr "E33: Không có biểu thức chính quy trước để thay thế"
+msgid "Used CUT_BUFFER0 instead of empty selection"
+msgstr "Sá»­ dụng CUT_BUFFER0 thay cho lá»±a chá»n trống rá»—ng"
-#: ../globals.h:1045
-msgid "E34: No previous command"
-msgstr "E34: Không có câu lệnh trước"
+msgid "Filter"
+msgstr "Äầu lá»c"
-#: ../globals.h:1046
-msgid "E35: No previous regular expression"
-msgstr "E35: Không có biểu thức chính quy trước"
+msgid "Directories"
+msgstr "Thư mục"
-#: ../globals.h:1047
-msgid "E481: No range allowed"
-msgstr "E481: Không cho phép sử dụng phạm vi"
+msgid "Help"
+msgstr "Trợ giúp"
-#: ../globals.h:1048
-msgid "E36: Not enough room"
-msgstr "E36: Không đủ chỗ trống"
+msgid "Files"
+msgstr "Tập tin"
-#: ../globals.h:1049
-#, c-format
-msgid "E482: Can't create file %s"
-msgstr "E482: Không tạo được tập tin %s"
+msgid "Selection"
+msgstr "Lá»±a chá»n"
-#: ../globals.h:1050
-msgid "E483: Can't get temp file name"
-msgstr "E483: Không nhận được tên tập tin tạm thá»i (temp)"
+msgid "Undo"
+msgstr "Hủy thao tác"
-#: ../globals.h:1051
#, c-format
-msgid "E484: Can't open file %s"
-msgstr "E484: Không mở được tập tin %s"
+msgid "E671: Cannot find window title \"%s\""
+msgstr "E671: Không tìm được tiêu đỠcửa sổ \"%s\""
-#: ../globals.h:1052
#, c-format
-msgid "E485: Can't read file %s"
-msgstr "E485: Không Ä‘á»c được tập tin %s"
+msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
+msgstr "E243: Tham số không được hỗ trợ: \"-%s\"; Hãy sử dụng phiên bản OLE."
-#: ../globals.h:1054
-msgid "E37: No write since last change (add ! to override)"
-msgstr "E37: Thay đổi chưa được ghi nhớ (thêm ! để bỠqua ghi nhớ)"
+msgid "E672: Unable to open window inside MDI application"
+msgstr "E672: Không mở được cửa sổ bên trong ứng dụng MDI"
-#: ../globals.h:1055
-#, fuzzy
-msgid "E37: No write since last change"
-msgstr "[Thay đổi chưa được ghi nhớ]\n"
+msgid "Find string (use '\\\\' to find a '\\')"
+msgstr "Tìm kiếm chuỗi (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-#: ../globals.h:1056
-msgid "E38: Null argument"
-msgstr "E38: Tham sô bằng 0"
+msgid "Find & Replace (use '\\\\' to find a '\\')"
+msgstr "Tìm kiếm và Thay thế (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-#: ../globals.h:1057
-msgid "E39: Number expected"
-msgstr "E39: Yêu cầu một số"
+msgid "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
+msgstr ""
+"Vim E458: Không chỉ định được bản ghi trong bảng màu, một vài màu có thể "
+"hiển thị không chính xác"
-#: ../globals.h:1058
#, c-format
-msgid "E40: Can't open errorfile %s"
-msgstr "E40: Không mở được tập tin lỗi %s"
+msgid "E250: Fonts for the following charsets are missing in fontset %s:"
+msgstr "E250: Trong bộ phông chữ %s thiếu phông cho các bảng mã sau:"
-#: ../globals.h:1059
-msgid "E41: Out of memory!"
-msgstr "E41: Không đủ bộ nhớ!"
-
-#: ../globals.h:1060
-msgid "Pattern not found"
-msgstr "Không tìm thấy mẫu (pattern)"
+#, c-format
+msgid "E252: Fontset name: %s"
+msgstr "E252: Bộ phông chữ: %s"
-#: ../globals.h:1061
#, c-format
-msgid "E486: Pattern not found: %s"
-msgstr "E486: Không tìm thấy mẫu (pattern): %s"
+msgid "Font '%s' is not fixed-width"
+msgstr "Phông chữ '%s' không phải là phông có độ rộng cố định (fixed-width)"
-#: ../globals.h:1062
-msgid "E487: Argument must be positive"
-msgstr "E487: Tham số phải là một số dương"
+#, c-format
+msgid "E253: Fontset name: %s\n"
+msgstr "E253: Bộ phông chữ: %s\n"
-#: ../globals.h:1064
-msgid "E459: Cannot go back to previous directory"
-msgstr "E459: Không quay lại được thư mục trước đó"
+#, c-format
+msgid "Font0: %s\n"
+msgstr "Font0: %s\n"
-#: ../globals.h:1066
-msgid "E42: No Errors"
-msgstr "E42: Không có lỗi"
+#, c-format
+msgid "Font1: %s\n"
+msgstr "Font1: %s\n"
-#: ../globals.h:1067
-msgid "E776: No location list"
+#, c-format
+msgid "Font%ld width is not twice that of font0\n"
msgstr ""
+"Chiá»u rá»™ng phông chữ font%ld phải lá»›n hÆ¡n hai lần so vá»›i chiá»u rá»™ng font0\n"
-#: ../globals.h:1068
-msgid "E43: Damaged match string"
-msgstr "E43: Chuá»—i tương ứng bị há»ng"
-
-#: ../globals.h:1069
-msgid "E44: Corrupted regexp program"
-msgstr "E44: Chương trình xá»­ lý biểu thức chính quy bị há»ng"
-
-#: ../globals.h:1071
-msgid "E45: 'readonly' option is set (add ! to override)"
-msgstr "E45: Tùy chá»n 'readonly' được bật (Hãy thêm ! để lá» Ä‘i)"
-
-#: ../globals.h:1073
-#, fuzzy, c-format
-msgid "E46: Cannot change read-only variable \"%s\""
-msgstr "E46: Không thay đổi được biến chỉ Ä‘á»c \"%s\""
-
-#: ../globals.h:1075
-#, fuzzy, c-format
-msgid "E794: Cannot set variable in the sandbox: \"%s\""
-msgstr "E46: Không thay đổi được biến chỉ Ä‘á»c \"%s\""
-
-#: ../globals.h:1076
-msgid "E47: Error while reading errorfile"
-msgstr "E47: Lá»—i khi Ä‘á»c tập tin lá»—i"
-
-#: ../globals.h:1078
-msgid "E48: Not allowed in sandbox"
-msgstr "E48: Không cho phép trong hộp cát (sandbox)"
-
-#: ../globals.h:1080
-msgid "E523: Not allowed here"
-msgstr "E523: Không cho phép ở đây"
-
-#: ../globals.h:1082
-msgid "E359: Screen mode setting not supported"
-msgstr "E359: Chế độ màn hình không được hỗ trợ"
-
-#: ../globals.h:1083
-msgid "E49: Invalid scroll size"
-msgstr "E49: Kích thước thanh cuộn không cho phép"
-
-#: ../globals.h:1084
-msgid "E91: 'shell' option is empty"
-msgstr "E91: Tùy chá»n 'shell' là má»™t chuá»—i rá»—ng"
-
-#: ../globals.h:1085
-msgid "E255: Couldn't read in sign data!"
-msgstr "E255: Không Ä‘á»c được dữ liệu vá» ký tá»±!"
-
-#: ../globals.h:1086
-msgid "E72: Close error on swap file"
-msgstr "E72: Lỗi đóng tập tin trao đổi (swap)"
-
-#: ../globals.h:1087
-msgid "E73: tag stack empty"
-msgstr "E73: đống thẻ ghi rỗng"
-
-#: ../globals.h:1088
-msgid "E74: Command too complex"
-msgstr "E74: Câu lệnh quá phức tạp"
-
-#: ../globals.h:1089
-msgid "E75: Name too long"
-msgstr "E75: Tên quá dài"
-
-#: ../globals.h:1090
-msgid "E76: Too many ["
-msgstr "E76: Quá nhiá»u ký tá»± ["
-
-#: ../globals.h:1091
-msgid "E77: Too many file names"
-msgstr "E77: Quá nhiá»u tên tập tin"
-
-#: ../globals.h:1092
-msgid "E488: Trailing characters"
-msgstr "E488: Ký tự thừa ở đuôi"
-
-#: ../globals.h:1093
-msgid "E78: Unknown mark"
-msgstr "E78: Dấu hiệu không biết"
-
-#: ../globals.h:1094
-msgid "E79: Cannot expand wildcards"
-msgstr "E79: Không thực hiện được phép thế theo wildcard"
-
-#: ../globals.h:1096
-msgid "E591: 'winheight' cannot be smaller than 'winminheight'"
-msgstr "E591: giá trị của 'winheight' không thể nhỠhơn 'winminheight'"
-
-#: ../globals.h:1098
-msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'"
-msgstr "E592: giá trị của 'winwidth' không thể nhỠhơn 'winminwidth'"
-
-#: ../globals.h:1099
-msgid "E80: Error while writing"
-msgstr "E80: Lá»—i khi ghi nhá»›"
-
-#: ../globals.h:1100
-msgid "Zero count"
-msgstr "Giá trị của bộ đếm bằng 0"
-
-#: ../globals.h:1101
-msgid "E81: Using <SID> not in a script context"
-msgstr "E81: Sử dụng <SID> ngoài phạm vi script"
-
-#: ../globals.h:1102
-#, fuzzy, c-format
-msgid "E685: Internal error: %s"
-msgstr "E473: Lá»—i ná»™i bá»™"
+#, c-format
+msgid "Font0 width: %ld\n"
+msgstr "Chiá»u rá»™ng font0: %ld\n"
-#: ../globals.h:1104
-msgid "E363: pattern uses more memory than 'maxmempattern'"
+#, c-format
+msgid ""
+"Font1 width: %ld\n"
+"\n"
msgstr ""
+"Chiá»u rá»™ng font1: %ld\n"
+"\n"
-#: ../globals.h:1105
-#, fuzzy
-msgid "E749: empty buffer"
-msgstr "E279: Äây không phải là bá»™ đệm SNiFF+"
-
-#: ../globals.h:1108
-#, fuzzy
-msgid "E682: Invalid search pattern or delimiter"
-msgstr "E383: Chuỗi tìm kiếm không đúng: %s"
-
-#: ../globals.h:1109
-msgid "E139: File is loaded in another buffer"
-msgstr "E139: Tập tin được nạp trong bộ đệm khác"
-
-#: ../globals.h:1110
-#, fuzzy, c-format
-msgid "E764: Option '%s' is not set"
-msgstr "E236: Phông chữ \"%s\" không có độ rộng cố định (fixed-width)"
-
-#: ../globals.h:1111
-#, fuzzy
-msgid "E850: Invalid register name"
-msgstr "E354: Tên sổ đăng ký không cho phép: '%s'"
-
-#: ../globals.h:1114
-msgid "search hit TOP, continuing at BOTTOM"
-msgstr "tìm kiếm sẽ được tiếp tục từ CUá»I tài liệu"
+msgid "E256: Hangul automata ERROR"
+msgstr "E256: LỖI máy tự động Hangual (tiếng Hàn)"
-#: ../globals.h:1115
-msgid "search hit BOTTOM, continuing at TOP"
-msgstr "tìm kiếm sẽ được tiếp tục từ ÄẦU tài liệu"
-
-#: ../if_cscope.c:85
msgid "Add a new database"
msgstr "Thêm một cơ sở dữ liệu mới"
-#: ../if_cscope.c:87
msgid "Query for a pattern"
msgstr "Yêu cầu theo một mẫu"
-#: ../if_cscope.c:89
msgid "Show this message"
msgstr "Hiển thị thông báo này"
-#: ../if_cscope.c:91
msgid "Kill a connection"
msgstr "Hủy kết nối"
-#: ../if_cscope.c:93
msgid "Reinit all connections"
msgstr "Khởi đầu lại tất cả các kết nối"
-#: ../if_cscope.c:95
msgid "Show connections"
msgstr "Hiển thị kết nối"
-#: ../if_cscope.c:101
#, c-format
msgid "E560: Usage: cs[cope] %s"
msgstr "E560: Sử dụng: cs[cope] %s"
-#: ../if_cscope.c:225
msgid "This cscope command does not support splitting the window.\n"
msgstr "Câu lệnh cscope này không hỗ trợ việc chia (split) cửa sổ.\n"
-#: ../if_cscope.c:266
msgid "E562: Usage: cstag <ident>"
msgstr "E562: Sử dụng: cstag <tên>"
-#: ../if_cscope.c:313
-msgid "E257: cstag: tag not found"
+# TODO: Capitalise first word of message?
+msgid "E257: cstag: Tag not found"
msgstr "E257: cstag: không tìm thấy thẻ ghi"
-#: ../if_cscope.c:461
#, c-format
msgid "E563: stat(%s) error: %d"
msgstr "E563: lá»—i stat(%s): %d"
-#: ../if_cscope.c:551
+msgid "E563: stat error"
+msgstr "E563: lá»—i stat"
+
#, c-format
msgid "E564: %s is not a directory or a valid cscope database"
msgstr ""
"E564: %s không phải là một thư mục hoặc một cơ sở dữ liệu cscope thích hợp"
-#: ../if_cscope.c:566
#, c-format
msgid "Added cscope database %s"
msgstr "Äã thêm cÆ¡ sở dữ liệu cscope %s"
-#: ../if_cscope.c:616
-#, c-format
-msgid "E262: error reading cscope connection %<PRId64>"
-msgstr "E262: lỗi lấy thông tin từ kết nối cscope %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E262: Error reading cscope connection %ld"
+msgstr "E262: lỗi lấy thông tin từ kết nối cscope %ld"
-#: ../if_cscope.c:711
-msgid "E561: unknown cscope search type"
+# TODO: Capitalise first word of message?
+msgid "E561: Unknown cscope search type"
msgstr "E561: không rõ loại tìm kiếm cscope"
-#: ../if_cscope.c:752 ../if_cscope.c:789
msgid "E566: Could not create cscope pipes"
msgstr "E566: Không tạo được đưá»ng ống (pipe) cho cscope"
-#: ../if_cscope.c:767
msgid "E622: Could not fork for cscope"
msgstr "E622: Không thực hiện được fork() cho cscope"
-#: ../if_cscope.c:849
-#, fuzzy
-msgid "cs_create_connection setpgid failed"
-msgstr "thực hiện cs_create_connection không thành công"
-
-#: ../if_cscope.c:853 ../if_cscope.c:889
msgid "cs_create_connection exec failed"
msgstr "thực hiện cs_create_connection không thành công"
-#: ../if_cscope.c:863 ../if_cscope.c:902
+msgid "E623: Could not spawn cscope process"
+msgstr "E623: Chạy tiến trình cscope không thành công"
+
msgid "cs_create_connection: fdopen for to_fp failed"
msgstr "cs_create_connection: thực hiện fdopen cho to_fp không thành công"
-#: ../if_cscope.c:865 ../if_cscope.c:906
msgid "cs_create_connection: fdopen for fr_fp failed"
msgstr "cs_create_connection: thực hiện fdopen cho fr_fp không thành công"
-#: ../if_cscope.c:890
-msgid "E623: Could not spawn cscope process"
-msgstr "E623: Chạy tiến trình cscope không thành công"
-
-#: ../if_cscope.c:932
-msgid "E567: no cscope connections"
+# TODO: Capitalise first word of message?
+msgid "E567: No cscope connections"
msgstr "E567: không có kết nối với cscope"
-#: ../if_cscope.c:1009
-#, c-format
-msgid "E469: invalid cscopequickfix flag %c for %c"
-msgstr "E469: cỠcscopequickfix %c cho %c không chính xác"
-
-#: ../if_cscope.c:1058
-#, c-format
-msgid "E259: no matches found for cscope query %s of %s"
+# TODO: Capitalise first word of message?
+msgid "E259: No matches found for cscope query %s of %s"
msgstr "E259: không tìm thấy tương ứng với yêu cầu cscope %s cho %s"
-#: ../if_cscope.c:1142
+# TODO: Capitalise first word of message?
+msgid "E469: Invalid cscopequickfix flag %c for %c"
+msgstr "E469: cỠcscopequickfix %c cho %c không chính xác"
+
msgid "cscope commands:\n"
msgstr "các lệnh cscope:\n"
-#: ../if_cscope.c:1150
-#, fuzzy, c-format
-msgid "%-5s: %s%*s (Usage: %s)"
+#, c-format
+msgid "%-5s: %-30s (Usage: %s)"
msgstr "%-5s: %-30s (Sử dụng: %s)"
-#: ../if_cscope.c:1155
-msgid ""
-"\n"
-" c: Find functions calling this function\n"
-" d: Find functions called by this function\n"
-" e: Find this egrep pattern\n"
-" f: Find this file\n"
-" g: Find this definition\n"
-" i: Find files #including this file\n"
-" s: Find this C symbol\n"
-" t: Find this text string\n"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E625: Cannot open cscope database: %s"
+msgstr "E625: không mở được cơ sở dữ liệu cscope: %s"
-#: ../if_cscope.c:1226
-msgid "E568: duplicate cscope database not added"
+# TODO: Capitalise first word of message?
+msgid "E626: Cannot get cscope database information"
+msgstr "E626: không lấy được thông tin vỠcơ sở dữ liệu cscope"
+
+# TODO: Capitalise first word of message?
+msgid "E568: Duplicate cscope database not added"
msgstr "E568: cơ sở dữ liệu này của cscope đã được gắn vào từ trước"
-#: ../if_cscope.c:1335
-#, c-format
-msgid "E261: cscope connection %s not found"
+msgid "E569: maximum number of cscope connections reached"
+msgstr "E569: đã đạt tới số kết nối lớn nhất cho phép với cscope"
+
+# TODO: Capitalise first word of message?
+msgid "E261: Cscope connection %s not found"
msgstr "E261: kết nối với cscope %s không được tìm thấy"
-#: ../if_cscope.c:1364
#, c-format
msgid "cscope connection %s closed"
msgstr "kết nối %s với cscope đã bị đóng"
-#. should not reach here
-#: ../if_cscope.c:1486
-msgid "E570: fatal error in cs_manage_matches"
+# TODO: Capitalise first word of message?
+msgid "E570: Fatal error in cs_manage_matches"
msgstr "E570: lỗi nặng trong cs_manage_matches"
-#: ../if_cscope.c:1693
#, c-format
msgid "Cscope tag: %s"
msgstr "Thẻ ghi cscope: %s"
-#: ../if_cscope.c:1711
msgid ""
"\n"
" # line"
@@ -3183,90 +1952,305 @@ msgstr ""
"\n"
" # dòng"
-#: ../if_cscope.c:1713
msgid "filename / context / line\n"
msgstr "tên tập tin / nội dung / dòng\n"
-#: ../if_cscope.c:1809
#, c-format
msgid "E609: Cscope error: %s"
msgstr "E609: Lá»—i cscope: %s"
-#: ../if_cscope.c:2053
msgid "All cscope databases reset"
msgstr "Khởi động lại tất cả cơ sở dữ liệu cscope"
-#: ../if_cscope.c:2123
msgid "no cscope connections\n"
msgstr "không có kết nối với cscope\n"
-#: ../if_cscope.c:2126
msgid " # pid database name prepend path\n"
msgstr " # pid tên cÆ¡ sở dữ liệu đưá»ng dẫn ban đầu\n"
-#: ../main.c:144
-#, fuzzy
-msgid "Unknown option argument"
+msgid ""
+"E263: Sorry, this command is disabled, the Python library could not be "
+"loaded."
+msgstr ""
+"E263: Rất tiếc câu lệnh này không làm việc, vì thư viện Python chưa được nạp."
+
+msgid "E659: Cannot invoke Python recursively"
+msgstr "E659: Không thể gá»i Python má»™t cách đệ quy"
+
+msgid "can't delete OutputObject attributes"
+msgstr "Không xóa được thuộc tính OutputObject"
+
+msgid "softspace must be an integer"
+msgstr "giá trị softspace phải là một số nguyên"
+
+msgid "invalid attribute"
+msgstr "thuộc tính không đúng"
+
+msgid "writelines() requires list of strings"
+msgstr "writelines() yêu cầu một danh sách các chuỗi"
+
+msgid "E264: Python: Error initialising I/O objects"
+msgstr "E264: Python: Lỗi khi bắt đầu sử dụng vật thể I/O"
+
+msgid "invalid expression"
+msgstr "biểu thức không đúng"
+
+msgid "expressions disabled at compile time"
+msgstr "biểu thức bị tắt khi biên dịch"
+
+msgid "attempt to refer to deleted buffer"
+msgstr "cố chỉ đến bộ đệm đã bị xóa"
+
+msgid "line number out of range"
+msgstr "số thứ tự của dòng vượt quá giới hạn"
+
+#, c-format
+msgid "<buffer object (deleted) at %8lX>"
+msgstr "<vật thể của bộ đệm (bị xóa) tại %8lX>"
+
+msgid "invalid mark name"
+msgstr "tên dấu hiệu không đúng"
+
+msgid "no such buffer"
+msgstr "không có bộ đệm như vậy"
+
+msgid "attempt to refer to deleted window"
+msgstr "cố chỉ đến cửa sổ đã bị đóng"
+
+msgid "readonly attribute"
+msgstr "thuá»™c tính chỉ Ä‘á»c"
+
+msgid "cursor position outside buffer"
+msgstr "vị trí con trỠnằm ngoài bộ đệm"
+
+#, c-format
+msgid "<window object (deleted) at %.8lX>"
+msgstr "<vật thể của cửa sổ (bị xóa) tại %.8lX>"
+
+#, c-format
+msgid "<window object (unknown) at %.8lX>"
+msgstr "<vật thể của cửa sổ (không rõ) tại %.8lX>"
+
+#, c-format
+msgid "<window %d>"
+msgstr "<cửa sổ %d>"
+
+msgid "no such window"
+msgstr "không có cửa sổ như vậy"
+
+msgid "cannot save undo information"
+msgstr "không ghi được thông tin vỠviệc hủy thao tác"
+
+msgid "cannot delete line"
+msgstr "không xóa được dòng"
+
+msgid "cannot replace line"
+msgstr "không thay thế được dòng"
+
+msgid "cannot insert line"
+msgstr "không chèn được dòng"
+
+msgid "string cannot contain newlines"
+msgstr "chuỗi không thể chứa ký tự dòng mới"
+
+msgid ""
+"E266: Sorry, this command is disabled, the Ruby library could not be loaded."
+msgstr ""
+"E266: Rất tiếc câu lệnh này không làm việc, vì thư viện Ruby chưa đượcnạp."
+
+# TODO: Capitalise first word of message?
+msgid "E273: Unknown longjmp status %d"
+msgstr "E273: không rõ trạng thái của longjmp %d"
+
+msgid "Toggle implementation/definition"
+msgstr "Bật tắt giữa thi hành/định nghĩa"
+
+msgid "Show base class of"
+msgstr "Hiển thị hạng cơ bản của"
+
+msgid "Show overridden member function"
+msgstr "Hiển thị hàm số bị nạp đè lên"
+
+msgid "Retrieve from file"
+msgstr "Nhận từ tập tin"
+
+msgid "Retrieve from project"
+msgstr "Nhận từ dự án"
+
+msgid "Retrieve from all projects"
+msgstr "Nhận từ tất cả các dự án"
+
+msgid "Retrieve"
+msgstr "Nhận"
+
+msgid "Show source of"
+msgstr "Hiển thị mã nguồn"
+
+msgid "Find symbol"
+msgstr "Tìm ký hiệu"
+
+msgid "Browse class"
+msgstr "Duyệt hạng"
+
+msgid "Show class in hierarchy"
+msgstr "Hiển thị hạng trong hệ thống cấp bậc"
+
+msgid "Show class in restricted hierarchy"
+msgstr "Hiển thị hạng trong hệ thống cấp bậc giới hạn"
+
+msgid "Xref refers to"
+msgstr "Xref chỉ đến"
+
+msgid "Xref referred by"
+msgstr "Liên kết đến xref từ"
+
+msgid "Xref has a"
+msgstr "Xref có một"
+
+msgid "Xref used by"
+msgstr "Xref được sử dụng bởi"
+
+msgid "Show docu of"
+msgstr "Hiển thị docu của"
+
+msgid "Generate docu for"
+msgstr "Tạo docu cho"
+
+msgid "not "
+msgstr "không "
+
+msgid "connected"
+msgstr "được kết nối"
+
+
+msgid "invalid buffer number"
+msgstr "số của bộ đệm không đúng"
+
+msgid "not implemented yet"
+msgstr "tạm thá»i chưa được thá»±c thi"
+
+msgid "unknown option"
+msgstr "tùy chá»n không rõ"
+
+msgid "cannot set line(s)"
+msgstr "không thể đặt (các) dòng"
+
+msgid "mark not set"
+msgstr "dấu hiệu chưa được đặt"
+
+#, c-format
+msgid "row %d column %d"
+msgstr "hàng %d cột %d"
+
+msgid "cannot insert/append line"
+msgstr "không thể chèn hoặc thêm dòng"
+
+msgid "unknown flag: "
+msgstr "cỠkhông biết: "
+
+msgid "unknown vimOption"
+msgstr "không rõ tùy chá»n vimOption"
+
+msgid "keyboard interrupt"
+msgstr "sự gián đoạn của bàn phím"
+
+msgid "Vim error"
+msgstr "lá»—i cá»§a vim"
+
+msgid "cannot create buffer/window command: object is being deleted"
+msgstr "không tạo được câu lệnh của bộ đệm hay của cửa sổ: vật thể đang bị xóa"
+
+msgid ""
+"cannot register callback command: buffer/window is already being deleted"
+msgstr "không đăng ký được câu lệnh gá»i ngược: bá»™ đệm hoặc cá»­a sổ Ä‘ang bị xóa"
+
+msgid ""
+"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim."
+"org"
+msgstr ""
+"E280: Lá»–I NẶNG CỦA TCL: bị há»ng danh sách liên kết!? Hãy thông báo việc "
+"nàyđến danh sách thư (mailing list) vim-dev@vim.org"
+
+msgid "cannot register callback command: buffer/window reference not found"
+msgstr ""
+"không đăng ký được câu lệnh gá»i ngược: không tìm thấy liên kết đến bá»™ đệm "
+"hoặc cửa sổ"
+
+msgid ""
+"E571: Sorry, this command is disabled: the Tcl library could not be loaded."
+msgstr ""
+"E571: Rất tiếc là câu lệnh này không làm việc, vì thư viện Tcl chưa được nạp"
+
+msgid ""
+"E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim.org"
+msgstr ""
+"E281: Lá»–I TCL: mã thoát ra không phải là má»™t số nguyên!? Hãy thông báo Ä‘iá»u "
+"này đến danh sách thư (mailing list) vim-dev@vim.org"
+
+msgid "cannot get line"
+msgstr "không nhận được dòng"
+
+msgid "Unable to register a command server name"
+msgstr "Không đăng ký được một tên cho máy chủ câu lệnh"
+
+msgid "E248: Failed to send command to the destination program"
+msgstr "E248: Gửi câu lệnh vào chương trình khác không thành công"
+
+#, c-format
+msgid "E573: Invalid server id used: %s"
+msgstr "E573: Sử dụng id máy chủ không đúng: %s"
+
+msgid "E251: VIM instance registry property is badly formed. Deleted!"
+msgstr "E251: Thuộc tính đăng ký của Vim được định dạng không đúng. Xóa!"
+
+msgid "Unknown option"
msgstr "Tùy chá»n không biết"
-#: ../main.c:146
msgid "Too many edit arguments"
msgstr "Có quá nhiá»u tham số soạn thảo"
-#: ../main.c:148
msgid "Argument missing after"
msgstr "Thiếu tham số sau"
-#: ../main.c:150
-#, fuzzy
-msgid "Garbage after option argument"
+msgid "Garbage after option"
msgstr "Rác sau tùy chá»n"
-#: ../main.c:152
msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments"
msgstr ""
"Quá nhiá»u tham số \"+câu lệnh\", \"-c câu lệnh\" hoặc \"--cmd câu lệnh\""
-#: ../main.c:154
msgid "Invalid argument for"
msgstr "Tham số không được phép cho"
-#: ../main.c:294
-#, c-format
-msgid "%d files to edit\n"
-msgstr "%d tập tin để soạn thảo\n"
+msgid "This Vim was not compiled with the diff feature."
+msgstr "Vim không được biên dịch với tính năng hỗ trợ xem khác biệt (diff)."
-#: ../main.c:1342
msgid "Attempt to open script file again: \""
msgstr "Thử mở tập tin script một lần nữa: \""
-#: ../main.c:1350
msgid "Cannot open for reading: \""
msgstr "Không mở để Ä‘á»c được: \""
-#: ../main.c:1393
msgid "Cannot open for script output: \""
msgstr "Không mở cho đầu ra script được: \""
-#: ../main.c:1622
+#, c-format
+msgid "%d files to edit\n"
+msgstr "%d tập tin để soạn thảo\n"
+
msgid "Vim: Warning: Output is not to a terminal\n"
msgstr "Vim: Cảnh báo: Äầu ra không hướng tá»›i má»™t terminal\n"
-#: ../main.c:1624
msgid "Vim: Warning: Input is not from a terminal\n"
msgstr "Vim: Cảnh báo: Äầu vào không phải đến từ má»™t terminal\n"
-#. just in case..
-#: ../main.c:1891
msgid "pre-vimrc command line"
msgstr "dòng lệnh chạy trước khi thực hiện vimrc"
-#: ../main.c:1964
#, c-format
msgid "E282: Cannot read from \"%s\""
msgstr "E282: Không Ä‘á»c được từ \"%s\""
-#: ../main.c:2149
msgid ""
"\n"
"More info with: \"vim -h\"\n"
@@ -3274,23 +2258,18 @@ msgstr ""
"\n"
"Xem thông tin chi tiết với: \"vim -h\"\n"
-#: ../main.c:2178
msgid "[file ..] edit specified file(s)"
msgstr "[tập tin ..] soạn thảo (các) tập tin chỉ ra"
-#: ../main.c:2179
msgid "- read text from stdin"
msgstr "- Ä‘á»c văn bản từ đầu vào stdin"
-#: ../main.c:2180
msgid "-t tag edit file where tag is defined"
msgstr "-t thẻ ghi soạn thảo tập tin từ chỗ thẻ ghi chỉ ra"
-#: ../main.c:2181
msgid "-q [errorfile] edit file with first error"
msgstr "-q [tập tin lỗi] soạn thảo tập tin với lỗi đầu tiên"
-#: ../main.c:2187
msgid ""
"\n"
"\n"
@@ -3300,11 +2279,9 @@ msgstr ""
"\n"
"Sử dụng:"
-#: ../main.c:2189
msgid " vim [arguments] "
msgstr " vim [các tham số] "
-#: ../main.c:2193
msgid ""
"\n"
" or:"
@@ -3312,7 +2289,6 @@ msgstr ""
"\n"
" hoặc:"
-#: ../main.c:2196
msgid ""
"\n"
"\n"
@@ -3322,187 +2298,322 @@ msgstr ""
"\n"
"Tham số:\n"
-#: ../main.c:2197
msgid "--\t\t\tOnly file names after this"
msgstr "--\t\t\tSau tham số chỉ đưa ra tên tập tin"
-#: ../main.c:2199
msgid "--literal\t\tDon't expand wildcards"
msgstr "--literal\t\tKhông thực hiện việc mở rộng wildcard"
-#: ../main.c:2201
+msgid "-register\t\tRegister this gvim for OLE"
+msgstr "-register\t\tÄăng ký gvim này cho OLE"
+
+msgid "-unregister\t\tUnregister gvim for OLE"
+msgstr "-unregister\t\tBỠđăng ký gvim này cho OLE"
+
+msgid "-g\t\t\tRun using GUI (like \"gvim\")"
+msgstr "-g\t\t\tSá»­ dụng giao diện đồ há»a GUI (giống \"gvim\")"
+
+msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
+msgstr ""
+"-f hoặc --nofork\tTrong chương trình hoạt động: Không thực hiện fork khi "
+"chạy GUI"
+
msgid "-v\t\t\tVi mode (like \"vi\")"
msgstr "-v\t\t\tChế độ Vi (giống \"vi\")"
-#: ../main.c:2202
msgid "-e\t\t\tEx mode (like \"ex\")"
msgstr "-e\t\t\tChế độ Ex (giống \"ex\")"
-#: ../main.c:2203
-msgid "-E\t\t\tImproved Ex mode"
-msgstr ""
-
-#: ../main.c:2204
msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")"
msgstr "-s\t\t\tChế độ ít đưa thông báo (gói) (chỉ dành cho \"ex\")"
-#: ../main.c:2205
msgid "-d\t\t\tDiff mode (like \"vimdiff\")"
msgstr "-d\t\t\tChế độ khác biệt, diff (giống \"vimdiff\")"
-#: ../main.c:2206
msgid "-y\t\t\tEasy mode (like \"evim\", modeless)"
msgstr "-y\t\t\tChế độ đơn giản (giống \"evim\", không có chế độ)"
-#: ../main.c:2207
msgid "-R\t\t\tReadonly mode (like \"view\")"
msgstr "-R\t\t\tChế độ chỉ Ä‘á»c (giống \"view\")"
-#: ../main.c:2209
+msgid "-Z\t\t\tRestricted mode (like \"rvim\")"
+msgstr "-Z\t\t\tChế độ hạn chế (giống \"rvim\")"
+
msgid "-m\t\t\tModifications (writing files) not allowed"
msgstr "-m\t\t\tKhông có khả năng ghi nhớ thay đổi (ghi nhớ tập tin)"
-#: ../main.c:2210
msgid "-M\t\t\tModifications in text not allowed"
msgstr "-M\t\t\tKhông có khả năng thay đổi văn bản"
-#: ../main.c:2211
msgid "-b\t\t\tBinary mode"
msgstr "-b\t\t\tChế độ nhị phân (binary)"
-#: ../main.c:2212
msgid "-l\t\t\tLisp mode"
msgstr "-l\t\t\tChế độ Lisp"
-#: ../main.c:2213
msgid "-C\t\t\tCompatible with Vi: 'compatible'"
msgstr "-C\t\t\tChế độ tương thích với Vi: 'compatible'"
-#: ../main.c:2214
msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'"
msgstr "-N\t\t\tChế độ không tương thích hoàn toàn với Vi: 'nocompatible'"
-#: ../main.c:2215
-msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]"
-msgstr ""
+msgid "-V[N]\t\tVerbose level"
+msgstr "-V[N]\t\tMức độ chi tiết của thông báo"
-#: ../main.c:2216
msgid "-D\t\t\tDebugging mode"
msgstr "-D\t\t\tChế độ sửa lỗi (debug)"
-#: ../main.c:2217
msgid "-n\t\t\tNo swap file, use memory only"
msgstr "-n\t\t\tKhông sử dụng tập tin swap, chỉ sử dụng bộ nhớ"
-#: ../main.c:2218
msgid "-r\t\t\tList swap files and exit"
msgstr "-r\t\t\tLiệt kê các tập tin swap rồi thoát"
-#: ../main.c:2219
msgid "-r (with file name)\tRecover crashed session"
msgstr "-r (với tên tập tin)\tPhục hồi lần soạn thảo gặp sự cố"
-#: ../main.c:2220
msgid "-L\t\t\tSame as -r"
msgstr "-L\t\t\tGiống với -r"
-#: ../main.c:2221
-msgid "-A\t\t\tstart in Arabic mode"
+msgid "-f\t\t\tDon't use newcli to open window"
+msgstr "-f\t\t\tKhông sử dụng newcli để mở cửa sổ"
+
+msgid "-dev <device>\t\tUse <device> for I/O"
+msgstr "-dev <thiết bị>\t\tSử dụng <thiết bị> cho I/O"
+
+msgid "-A\t\t\tStart in Arabic mode"
msgstr "-A\t\t\tKhởi động vào chế độ Ả Rập"
-#: ../main.c:2222
msgid "-H\t\t\tStart in Hebrew mode"
msgstr "-H\t\t\tKhởi động vào chế độ Do thái"
-#: ../main.c:2223
msgid "-F\t\t\tStart in Farsi mode"
msgstr "-F\t\t\tKhởi động vào chế độ Farsi"
-#: ../main.c:2224
msgid "-T <terminal>\tSet terminal type to <terminal>"
msgstr "-T <terminal>\tÄặt loại terminal thành <terminal>"
-#: ../main.c:2225
msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc"
msgstr "-u <vimrc>\t\tSá»­ dụng <vimrc> thay thế cho má»i .vimrc"
-#: ../main.c:2226
+msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
+msgstr "-U <gvimrc>\t\tSá»­ dụng <gvimrc> thay thế cho má»i .gvimrc"
+
msgid "--noplugin\t\tDon't load plugin scripts"
msgstr "--noplugin\t\tKhông nạp bất kỳ script môđun nào"
-#: ../main.c:2227
-#, fuzzy
-msgid "-p[N]\t\tOpen N tab pages (default: one for each file)"
-msgstr "-o[N]\t\tMở N cửa sổ (theo mặc định: mỗi cửa sổ cho một tập tin)"
-
-#: ../main.c:2228
msgid "-o[N]\t\tOpen N windows (default: one for each file)"
msgstr "-o[N]\t\tMở N cửa sổ (theo mặc định: mỗi cửa sổ cho một tập tin)"
-#: ../main.c:2229
msgid "-O[N]\t\tLike -o but split vertically"
msgstr "-O[N]\t\tGiống vá»›i -o nhưng phân chia theo đưá»ng thẳng đứng"
-#: ../main.c:2230
msgid "+\t\t\tStart at end of file"
msgstr "+\t\t\tBắt đầu soạn thảo từ cuối tập tin"
-#: ../main.c:2231
msgid "+<lnum>\t\tStart at line <lnum>"
msgstr "+<lnum>\t\tBắt đầu soạn thảo từ dòng thứ <lnum> (số thứ tự của dòng)"
-#: ../main.c:2232
msgid "--cmd <command>\tExecute <command> before loading any vimrc file"
msgstr "--cmd <câu lệnh>\tThực hiện <câu lệnh> trước khi nạp tập tin vimrc"
-#: ../main.c:2233
msgid "-c <command>\t\tExecute <command> after loading the first file"
msgstr "-c <câu lệnh>\t\tThực hiện <câu lệnh> sau khi nạp tập tin đầu tiên"
-#: ../main.c:2235
msgid "-S <session>\t\tSource file <session> after loading the first file"
msgstr "-S <session>\t\tThực hiện <session> sau khi nạp tập tin đầu tiên"
-#: ../main.c:2236
msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>"
msgstr ""
"-s <scriptin>\tÄá»c các lệnh cá»§a chế độ Thông thưá»ng từ tập tin <scriptin>"
-#: ../main.c:2237
msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>"
msgstr "-w <scriptout>\tThêm tất cả các lệnh đã gõ vào tập tin <scriptout>"
-#: ../main.c:2238
msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>"
msgstr "-W <scriptout>\tGhi nhớ tất cả các lệnh đã gõ vào tập tin <scriptout>"
-#: ../main.c:2240
-msgid "--startuptime <file>\tWrite startup timing messages to <file>"
+msgid "-x\t\t\tEdit encrypted files"
+msgstr "-x\t\t\tSoạn thảo tập tin đã mã hóa"
+
+msgid "-display <display>\tConnect Vim to this particular X-server"
+msgstr "-display <màn hình>\tKết nối Vim tới máy chủ X đã chỉ ra"
+
+msgid "-X\t\t\tDo not connect to X server"
+msgstr "-X\t\t\tKhông thực hiện việc kết nối tới máy chủ X"
+
+msgid "--remote <files>\tEdit <files> in a Vim server if possible"
+msgstr "--remote <tập tin>\tSoạn thảo <tập tin> trên máy chủ Vim nếu có thể"
+
+msgid "--remote-silent <files> Same, don't complain if there is no server"
+msgstr ""
+"--remote-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có máy chủ"
+
+msgid ""
+"--remote-wait <files> As --remote but wait for files to have been edited"
+msgstr "--remote-wait <tập tin> Cũng như --remote, nhưng chỠsự kết thúc"
+
+msgid ""
+"--remote-wait-silent <files> Same, don't complain if there is no server"
+msgstr ""
+"--remote-wait-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có máy "
+"chá»§"
+
+msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
+msgstr "--remote-send <phím>\tGửi <phím> lên máy chủ Vim và thoát"
+
+msgid "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
msgstr ""
+"--remote-expr <biểu thức>\tTính <biểu thức> trên máy chủ Vim và in ra kết quả"
+
+msgid "--serverlist\t\tList available Vim server names and exit"
+msgstr "--serverlist\t\tHiển thị danh sách máy chủ Vim và thoát"
+
+msgid "--servername <name>\tSend to/become the Vim server <name>"
+msgstr "--servername <tên>\tGửi lên (hoặc trở thành) máy chủ Vim với <tên>"
-#: ../main.c:2242
msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo"
msgstr "-i <viminfo>\t\tSử dụng tập tin <viminfo> thay cho .viminfo"
-#: ../main.c:2243
msgid "-h or --help\tPrint Help (this message) and exit"
msgstr "-h hoặc --help\tHiển thị Trợ giúp (thông tin này) và thoát"
-#: ../main.c:2244
msgid "--version\t\tPrint version information and exit"
msgstr "--version\t\tÄÆ°a ra thông tin vá» phiên bản Vim và thoát"
-#: ../mark.c:676
+msgid ""
+"\n"
+"Arguments recognised by gvim (Motif version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản Motif):\n"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (neXtaw version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản neXtaw):\n"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (Athena version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản Athena):\n"
+
+msgid "-display <display>\tRun Vim on <display>"
+msgstr "-display <màn hình>\tChạy Vim trong <màn hình> đã chỉ ra"
+
+msgid "-iconic\t\tStart Vim iconified"
+msgstr "-iconic\t\tChạy Vim ở dạng thu nhá»"
+
+msgid "-name <name>\t\tUse resource as if vim was <name>"
+msgstr "-name <tên>\t\tSử dụng tài nguyên giống như khi vim có <tên>"
+
+msgid "\t\t\t (Unimplemented)\n"
+msgstr "\t\t\t (Chưa được thực thi)\n"
+
+msgid "-background <color>\tUse <color> for the background (also: -bg)"
+msgstr "-background <màu>\tSá»­ dụng <màu> chỉ ra cho ná»n (cÅ©ng như: -bg)"
+
+msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
+msgstr ""
+"-foreground <màu>\tSá»­ dụng <màu> cho văn bản thông thưá»ng (cÅ©ng như: -fg)"
+
+msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
+msgstr ""
+"-font <phông>\t\tSá»­ dụng <phông> chữ cho văn bản thông thưá»ng (cÅ©ng như: -fn)"
+
+msgid "-boldfont <font>\tUse <font> for bold text"
+msgstr "-boldfont <phông>\tSử dụng <phông> chữ cho văn bản in đậm"
+
+msgid "-italicfont <font>\tUse <font> for italic text"
+msgstr "-italicfont <phông>\tSử dụng <phông> chữ cho văn bản in nghiêng"
+
+msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
+msgstr "-geometry <kích thước>\tSử dụng <kích thước> ban đầu (cũng như: -geom)"
+
+msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
+msgstr ""
+"-borderwidth <rá»™ng>\tSá»­ dụng đưá»ng viá»n có chiá»u <rá»™ng> (cÅ©ng như: -bw)"
+
+msgid "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
+msgstr ""
+"-scrollbarwidth <rá»™ng> Sá»­ dụng thanh cuá»™n vá»›i chiá»u <rá»™ng> (cÅ©ng như: -sw)"
+
+msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
+msgstr ""
+"-menuheight <cao>\tSá»­ dụng thanh trình đơn vá»›i chiá»u <cao> (cÅ©ng như: -mh)"
+
+msgid "-reverse\t\tUse reverse video (also: -rv)"
+msgstr "-reverse\t\tSử dụng chế độ video đảo ngược (cũng như: -rv)"
+
+msgid "+reverse\t\tDon't use reverse video (also: +rv)"
+msgstr "+reverse\t\tKhông sử dụng chế độ video đảo ngược (cũng như: +rv)"
+
+msgid "-xrm <resource>\tSet the specified resource"
+msgstr "-xrm <tài nguyên>\tÄặt <tài nguyên> chỉ ra"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (RISC OS version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản RISC OS):\n"
+
+msgid "--columns <number>\tInitial width of window in columns"
+msgstr "--columns <số>\tChiá»u rá»™ng ban đầu cá»§a cá»­a sổ tính theo số cá»™t"
+
+msgid "--rows <number>\tInitial height of window in rows"
+msgstr "--rows <số>\tChiá»u cao ban đầu cá»§a cá»­a sổ tính theo số dòng"
+
+msgid ""
+"\n"
+"Arguments recognised by gvim (GTK+ version):\n"
+msgstr ""
+"\n"
+"Tham số cho gvim (phiên bản GTK+):\n"
+
+msgid "-display <display>\tRun Vim on <display> (also: --display)"
+msgstr ""
+"-display <màn hình>\tChạy Vim trên <màn hình> chỉ ra (cũng như: --display)"
+
+msgid "--role <role>\tSet a unique role to identify the main window"
+msgstr "--role <vai trò>\tÄặt <vai trò> duy nhất để nhận diện cá»­a sổ chính"
+
+msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
+msgstr "--socketid <xid>\tMở Vim bên trong thành phần GTK khác"
+
+msgid "-P <parent title>\tOpen Vim inside parent application"
+msgstr "-P <tiêu đỠcủa mẹ>\tMở Vim bên trong ứng dụng mẹ"
+
+msgid "No display"
+msgstr "Không có màn hình"
+
+msgid ": Send failed.\n"
+msgstr ": Gửi không thành công.\n"
+
+msgid ": Send failed. Trying to execute locally\n"
+msgstr ": Gửi không thành công. Thử thực hiện nội bộ\n"
+
+#, c-format
+msgid "%d of %d edited"
+msgstr "đã soạn thảo %d từ %d"
+
+msgid "No display: Send expression failed.\n"
+msgstr "Không có màn hình: gửi biểu thức không thành công.\n"
+
+msgid ": Send expression failed.\n"
+msgstr ": Gửi biểu thức không thành công.\n"
+
msgid "No marks set"
msgstr "Không có dấu hiệu nào được đặt."
-#: ../mark.c:678
#, c-format
msgid "E283: No marks matching \"%s\""
msgstr "E283: Không có dấu hiệu tương ứng với \"%s\""
-#. Highlight title
-#: ../mark.c:687
msgid ""
"\n"
"mark line col file/text"
@@ -3510,8 +2621,6 @@ msgstr ""
"\n"
"nhãn dòng cột tập tin/văn bản"
-#. Highlight title
-#: ../mark.c:789
msgid ""
"\n"
" jump line col file/text"
@@ -3519,8 +2628,6 @@ msgstr ""
"\n"
" bước_nhảy dòng cột tập tin/văn bản"
-#. Highlight title
-#: ../mark.c:831
msgid ""
"\n"
"change line col text"
@@ -3528,7 +2635,6 @@ msgstr ""
"\n"
"thay_đổi dòng cột văn_bản"
-#: ../mark.c:1238
msgid ""
"\n"
"# File marks:\n"
@@ -3536,8 +2642,6 @@ msgstr ""
"\n"
"# Nhãn của tập tin:\n"
-#. Write the jumplist with -'
-#: ../mark.c:1271
msgid ""
"\n"
"# Jumplist (newest first):\n"
@@ -3545,7 +2649,6 @@ msgstr ""
"\n"
"# Danh sách bước nhảy (mới hơn đứng trước):\n"
-#: ../mark.c:1352
msgid ""
"\n"
"# History of marks within files (newest to oldest):\n"
@@ -3553,88 +2656,94 @@ msgstr ""
"\n"
"# Lịch sử các nhãn trong tập tin (từ mới nhất đến cũ nhất):\n"
-#: ../mark.c:1431
msgid "Missing '>'"
msgstr "Thiếu '>'"
-#: ../memfile.c:426
-msgid "E293: block was not locked"
+msgid "E543: Not a valid codepage"
+msgstr "E543: Bảng mã không cho phép"
+
+msgid "E284: Cannot set IC values"
+msgstr "E284: Không đặt được giá trị nội dung nhập vào (IC)"
+
+msgid "E285: Failed to create input context"
+msgstr "E285: Không tạo được nội dung nhập vào"
+
+msgid "E286: Failed to open input method"
+msgstr "E286: Việc thử mở phương pháp nhập không thành công"
+
+msgid "E287: Warning: Could not set destroy callback to IM"
+msgstr ""
+"E287: Cảnh báo: không đặt được sá»± gá»i ngược há»§y diệt thành phương pháp nhập"
+
+# TODO: Capitalise first word of message?
+msgid "E288: Input method doesn't support any style"
+msgstr "E288: phương pháp nhập không hỗ trợ bất kỳ phong cách (style) nào"
+
+# TODO: Capitalise first word of message?
+msgid "E289: Input method doesn't support my preedit type"
+msgstr "E289: phương pháp nhập không hỗ trợ loại soạn thảo trước của Vim"
+
+msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
+msgstr "E291: GTK+ cũ hơn 1.2.3. Vùng chỉ trạng thái không làm việc"
+
+# TODO: Capitalise first word of message?
+msgid "E293: Block was not locked"
msgstr "E293: khối chưa bị khóa"
-#: ../memfile.c:799
msgid "E294: Seek error in swap file read"
msgstr "E294: Lá»—i tìm kiếm khi Ä‘á»c tập tin trao đổi (swap)"
-#: ../memfile.c:803
msgid "E295: Read error in swap file"
msgstr "E295: Lá»—i Ä‘á»c tập tin trao đổi (swap)"
-#: ../memfile.c:849
msgid "E296: Seek error in swap file write"
msgstr "E296: Lỗi tìm kiếm khi ghi nhớ tập tin trao đổi (swap)"
-#: ../memfile.c:865
msgid "E297: Write error in swap file"
msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
-#: ../memfile.c:1036
msgid "E300: Swap file already exists (symlink attack?)"
msgstr ""
"E300: Tập tin trao đổi (swap) đã tồn tại (sá»­ dụng liên kết má»m tấn công?)"
-#: ../memline.c:318
msgid "E298: Didn't get block nr 0?"
msgstr "E298: Chưa lấy khối số 0?"
-#: ../memline.c:361
msgid "E298: Didn't get block nr 1?"
msgstr "E298: Chưa lấy khối số 12?"
-#: ../memline.c:377
msgid "E298: Didn't get block nr 2?"
msgstr "E298: Chưa lấy khối số 2?"
-#. could not (re)open the swap file, what can we do????
-#: ../memline.c:465
msgid "E301: Oops, lost the swap file!!!"
msgstr "E301: á»i, mất tập tin trao đổi (swap)!!!"
-#: ../memline.c:477
msgid "E302: Could not rename swap file"
msgstr "E302: Không đổi được tên tập tin trao đổi (swap)"
-#: ../memline.c:554
#, c-format
msgid "E303: Unable to open swap file for \"%s\", recovery impossible"
msgstr ""
"E303: Không mở được tập tin trao đổi (swap) cho \"%s\", nên không thể phục "
"hồi"
-#: ../memline.c:666
-#, fuzzy
-msgid "E304: ml_upd_block0(): Didn't get block 0??"
+msgid "E304: ml_timestamp: Didn't get block 0??"
msgstr "E304: ml_timestamp: Chưa lấy khối số 0??"
-#. no swap files found
-#: ../memline.c:830
#, c-format
msgid "E305: No swap file found for %s"
msgstr "E305: Không tìm thấy tập tin trao đổi (swap) cho %s"
-#: ../memline.c:839
msgid "Enter number of swap file to use (0 to quit): "
msgstr "Hãy nhập số của tập tin trao đổi (swap) muốn sử dụng (0 để thoát): "
-#: ../memline.c:879
#, c-format
msgid "E306: Cannot open %s"
msgstr "E306: Không mở được %s"
-#: ../memline.c:897
msgid "Unable to read block 0 from "
msgstr "Không thể Ä‘á»c khối số 0 từ "
-#: ../memline.c:900
msgid ""
"\n"
"Maybe no changes were made or Vim did not update the swap file."
@@ -3642,28 +2751,22 @@ msgstr ""
"\n"
"Chưa có thay đổi nào hoặc Vim không thể cập nhật tập tin trao đổi (swap)"
-#: ../memline.c:909
msgid " cannot be used with this version of Vim.\n"
msgstr " không thể sử dụng trong phiên bản Vim này.\n"
-#: ../memline.c:911
msgid "Use Vim version 3.0.\n"
msgstr "Hãy sử dụng Vim phiên bản 3.0.\n"
-#: ../memline.c:916
#, c-format
msgid "E307: %s does not look like a Vim swap file"
msgstr "E307: %s không phải là tập tin trao đổi (swap) của Vim"
-#: ../memline.c:922
msgid " cannot be used on this computer.\n"
msgstr " không thể sử dụng trên máy tính này.\n"
-#: ../memline.c:924
msgid "The file was created on "
msgstr "Tập tin đã được tạo trên "
-#: ../memline.c:928
msgid ""
",\n"
"or the file has been damaged."
@@ -3671,85 +2774,63 @@ msgstr ""
",\n"
"hoặc tập tin đã bị há»ng."
-#: ../memline.c:945
-msgid " has been damaged (page size is smaller than minimum value).\n"
-msgstr ""
-
-#: ../memline.c:974
#, c-format
msgid "Using swap file \"%s\""
msgstr "Äang sá»­ dụng tập tin trao đổi (swap) \"%s\""
-#: ../memline.c:980
#, c-format
msgid "Original file \"%s\""
msgstr "Tập tin gốc \"%s\""
-#: ../memline.c:995
msgid "E308: Warning: Original file may have been changed"
msgstr "E308: Cảnh báo: Tập tin gốc có thể đã bị thay đổi"
-#: ../memline.c:1061
#, c-format
msgid "E309: Unable to read block 1 from %s"
msgstr "E309: Không Ä‘á»c được khối số 1 từ %s"
-#: ../memline.c:1065
msgid "???MANY LINES MISSING"
msgstr "???THIẾU NHIỀU DÒNG"
-#: ../memline.c:1076
msgid "???LINE COUNT WRONG"
msgstr "???GIà TRỊ CỦA Sá» ÄẾM DÃ’NG BỊ SAI"
-#: ../memline.c:1082
msgid "???EMPTY BLOCK"
msgstr "???KHá»I Rá»–NG"
-#: ../memline.c:1103
msgid "???LINES MISSING"
msgstr "???THIẾU DÒNG"
-#: ../memline.c:1128
#, c-format
msgid "E310: Block 1 ID wrong (%s not a .swp file?)"
msgstr "E310: Khối 1 ID sai (%s không phải là tập tin .swp?)"
-#: ../memline.c:1133
msgid "???BLOCK MISSING"
msgstr "???THIẾU KHá»I"
-#: ../memline.c:1147
msgid "??? from here until ???END lines may be messed up"
msgstr "??? từ đây tá»›i ???CUá»I, các dòng có thể đã bị há»ng"
-#: ../memline.c:1164
msgid "??? from here until ???END lines may have been inserted/deleted"
msgstr "??? từ đây tá»›i ???CUá»I, các dòng có thể đã bị chèn hoặc xóa"
-#: ../memline.c:1181
msgid "???END"
msgstr "???CUá»I"
-#: ../memline.c:1238
msgid "E311: Recovery Interrupted"
msgstr "E311: Việc phục hồi bị gián đoạn"
-#: ../memline.c:1243
msgid ""
"E312: Errors detected while recovering; look for lines starting with ???"
msgstr ""
"E312: Phát hiện ra lỗi trong khi phục hồi; hãy xem những dòng bắt đầu với ???"
-#: ../memline.c:1245
msgid "See \":help E312\" for more information."
msgstr "Hãy xem thông tin bổ sung trong trợ giúp \":help E312\""
-#: ../memline.c:1249
msgid "Recovery completed. You should check if everything is OK."
msgstr "Việc phục hồi đã hoàn thành. Nên kiểm tra xem má»i thứ có ổn không."
-#: ../memline.c:1251
msgid ""
"\n"
"(You might want to write out this file under another name\n"
@@ -3757,71 +2838,49 @@ msgstr ""
"\n"
"(Có thể ghi nhớ tập tin với tên khác và so sánh với tập\n"
-#: ../memline.c:1252
-#, fuzzy
-msgid "and run diff with the original file to check for changes)"
+msgid "and run diff with the original file to check for changes)\n"
msgstr "gốc bằng chương trình diff).\n"
-#: ../memline.c:1254
-msgid "Recovery completed. Buffer contents equals file contents."
-msgstr ""
-
-#: ../memline.c:1255
-#, fuzzy
msgid ""
-"\n"
-"You may want to delete the .swp file now.\n"
+"Delete the .swp file afterwards.\n"
"\n"
msgstr ""
"Sau đó hãy xóa tập tin .swp.\n"
"\n"
-#. use msg() to start the scrolling properly
-#: ../memline.c:1327
msgid "Swap files found:"
msgstr "Tìm thấy tập tin trao đổi (swap):"
-#: ../memline.c:1446
msgid " In current directory:\n"
msgstr " Trong thư mục hiện thá»i:\n"
-#: ../memline.c:1448
msgid " Using specified name:\n"
msgstr " Với tên chỉ ra:\n"
-#: ../memline.c:1450
msgid " In directory "
msgstr " Trong thư mục "
-#: ../memline.c:1465
msgid " -- none --\n"
msgstr " -- không --\n"
-#: ../memline.c:1527
msgid " owned by: "
msgstr " ngưá»i sở hữu: "
-#: ../memline.c:1529
msgid " dated: "
msgstr " ngày: "
-#: ../memline.c:1532 ../memline.c:3231
msgid " dated: "
msgstr " ngày: "
-#: ../memline.c:1548
msgid " [from Vim version 3.0]"
msgstr " [từ Vim phiên bản 3.0]"
-#: ../memline.c:1550
msgid " [does not look like a Vim swap file]"
msgstr " [không phải là tập tin trao đổi (swap) của Vim]"
-#: ../memline.c:1552
msgid " file name: "
msgstr " tên tập tin: "
-#: ../memline.c:1558
msgid ""
"\n"
" modified: "
@@ -3829,15 +2888,12 @@ msgstr ""
"\n"
" thay đổi: "
-#: ../memline.c:1559
msgid "YES"
msgstr "CÓ"
-#: ../memline.c:1559
msgid "no"
msgstr "không"
-#: ../memline.c:1562
msgid ""
"\n"
" user name: "
@@ -3845,11 +2901,9 @@ msgstr ""
"\n"
" tên ngưá»i dùng: "
-#: ../memline.c:1568
msgid " host name: "
msgstr " tên máy: "
-#: ../memline.c:1570
msgid ""
"\n"
" host name: "
@@ -3857,7 +2911,6 @@ msgstr ""
"\n"
" tên máy: "
-#: ../memline.c:1575
msgid ""
"\n"
" process ID: "
@@ -3865,11 +2918,16 @@ msgstr ""
"\n"
" ID tiến trình: "
-#: ../memline.c:1579
msgid " (still running)"
msgstr " (vẫn đang chạy)"
-#: ../memline.c:1586
+msgid ""
+"\n"
+" [not usable with this version of Vim]"
+msgstr ""
+"\n"
+" [không sử dụng được với phiên bản này của Vim]"
+
msgid ""
"\n"
" [not usable on this computer]"
@@ -3877,97 +2935,75 @@ msgstr ""
"\n"
" [không sử dụng được trên máy tính này]"
-#: ../memline.c:1590
msgid " [cannot be read]"
msgstr " [không Ä‘á»c được]"
-#: ../memline.c:1593
msgid " [cannot be opened]"
msgstr " [không mở được]"
-#: ../memline.c:1698
msgid "E313: Cannot preserve, there is no swap file"
msgstr "E313: Không cập nhật được tập tin trao đổi (swap) vì không tìm thấy nó"
-#: ../memline.c:1747
msgid "File preserved"
msgstr "Äã cập nhật tập tin trao đổi (swap)"
-#: ../memline.c:1749
msgid "E314: Preserve failed"
msgstr "E314: Cập nhật không thành công"
-#: ../memline.c:1819
-#, c-format
-msgid "E315: ml_get: invalid lnum: %<PRId64>"
-msgstr "E315: ml_get: giá trị lnum không đúng: %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E315: ml_get: Invalid lnum: %ld"
+msgstr "E315: ml_get: giá trị lnum không đúng: %ld"
-#: ../memline.c:1851
-#, c-format
-msgid "E316: ml_get: cannot find line %<PRId64>"
-msgstr "E316: ml_get: không tìm được dòng %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E316: ml_get: Cannot find line %ld"
+msgstr "E316: ml_get: không tìm được dòng %ld"
-#: ../memline.c:2236
-msgid "E317: pointer block id wrong 3"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong 3"
msgstr "E317: Giá trị của pointer khối số 3 không đúng"
-#: ../memline.c:2311
msgid "stack_idx should be 0"
msgstr "giá trị stack_idx phải bằng 0"
-#: ../memline.c:2369
msgid "E318: Updated too many blocks?"
msgstr "E318: Äã cập nhật quá nhiá»u khối?"
-#: ../memline.c:2511
-msgid "E317: pointer block id wrong 4"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong 4"
msgstr "E317: Giá trị của pointer khối số 4 không đúng"
-#: ../memline.c:2536
msgid "deleted block 1?"
msgstr "đã xóa khối số 1?"
-#: ../memline.c:2707
#, c-format
-msgid "E320: Cannot find line %<PRId64>"
-msgstr "E320: Không tìm được dòng %<PRId64>"
+msgid "E320: Cannot find line %ld"
+msgstr "E320: Không tìm được dòng %ld"
-#: ../memline.c:2916
-msgid "E317: pointer block id wrong"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong"
msgstr "E317: giá trị của pointer khối không đúng"
-#: ../memline.c:2930
msgid "pe_line_count is zero"
msgstr "giá trị pe_line_count bằng không"
-#: ../memline.c:2955
-#, c-format
-msgid "E322: line number out of range: %<PRId64> past the end"
-msgstr "E322: số thứ tự dòng vượt quá giới hạn : %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E322: Line number out of range: %ld past the end"
+msgstr "E322: số thứ tự dòng vượt quá giới hạn : %ld"
-#: ../memline.c:2959
-#, c-format
-msgid "E323: line count wrong in block %<PRId64>"
-msgstr "E323: giá trị đếm dòng không đúng trong khối %<PRId64>"
+# TODO: Capitalise first word of message?
+msgid "E323: Line count wrong in block %ld"
+msgstr "E323: giá trị đếm dòng không đúng trong khối %ld"
-#: ../memline.c:2999
msgid "Stack size increases"
msgstr "Kích thước của đống tăng lên"
-#: ../memline.c:3038
-msgid "E317: pointer block id wrong 2"
+# TODO: Capitalise first word of message?
+msgid "E317: Pointer block id wrong 2"
msgstr "E317: Giá trị của cái chỉ (pointer) khối số 2 không đúng"
-#: ../memline.c:3070
-#, c-format
-msgid "E773: Symlink loop for \"%s\""
-msgstr ""
-
-#: ../memline.c:3221
msgid "E325: ATTENTION"
msgstr "E325: CHÚ Ã"
-#: ../memline.c:3222
msgid ""
"\n"
"Found a swap file by the name \""
@@ -3975,45 +3011,37 @@ msgstr ""
"\n"
"Tìm thấy một tập tin trao đổi (swap) với tên \""
-#: ../memline.c:3226
msgid "While opening file \""
msgstr "Khi mở tập tin: \""
-#: ../memline.c:3239
msgid " NEWER than swap file!\n"
msgstr " MỚI hơn so với tập tin trao đổi (swap)\n"
-#: ../memline.c:3244
-#, fuzzy
msgid ""
"\n"
-"(1) Another program may be editing the same file. If this is the case,\n"
-" be careful not to end up with two different instances of the same\n"
-" file when making changes."
+"(1) Another program may be editing the same file.\n"
+" If this is the case, be careful not to end up with two\n"
+" different instances of the same file when making changes.\n"
msgstr ""
"\n"
"(1) Rất có thể một chương trình khác đang soạn thảo tập tin.\n"
" Nếu như vậy, hãy cẩn thận khi thay đổi, làm sao để không thu\n"
" được hai phương án khác nhau của cùng một tập tin.\n"
-#: ../memline.c:3245
-#, fuzzy
-msgid " Quit, or continue with caution.\n"
+msgid " Quit, or continue with caution.\n"
msgstr " Thoát hoặc tiếp tục với sự cẩn thận.\n"
-#: ../memline.c:3246
-#, fuzzy
-msgid "(2) An edit session for this file crashed.\n"
+msgid ""
+"\n"
+"(2) An edit session for this file crashed.\n"
msgstr ""
"\n"
"(2) Lần soạn thảo trước của tập tin này gặp sự cố.\n"
-#: ../memline.c:3247
-msgid " If this is the case, use \":recover\" or \"nvim -r "
+msgid " If this is the case, use \":recover\" or \"vim -r "
msgstr ""
-" Trong trưá»ng hợp này, hãy sá»­ dụng câu lệnh \":recover\" hoặc \"nvim -r "
+" Trong trưá»ng hợp này, hãy sá»­ dụng câu lệnh \":recover\" hoặc \"vim -r "
-#: ../memline.c:3249
msgid ""
"\"\n"
" to recover the changes (see \":help recovery\").\n"
@@ -4021,12 +3049,10 @@ msgstr ""
"\"\n"
" để phục hồi những thay đổi (hãy xem \":help recovery\").\n"
-#: ../memline.c:3250
msgid " If you did this already, delete the swap file \""
msgstr ""
" Nếu đã thực hiện thao tác này rồi, thì hãy xóa tập tin trao đổi (swap) \""
-#: ../memline.c:3252
msgid ""
"\"\n"
" to avoid this message.\n"
@@ -4034,23 +3060,18 @@ msgstr ""
"\"\n"
" để tránh sự xuất hiện của thông báo này trong tương lai.\n"
-#: ../memline.c:3450 ../memline.c:3452
msgid "Swap file \""
msgstr "Tập tin trao đổi (swap) \""
-#: ../memline.c:3451 ../memline.c:3455
msgid "\" already exists!"
msgstr "\" đã có rồi!"
-#: ../memline.c:3457
msgid "VIM - ATTENTION"
msgstr "VIM - CHÚ Ã"
-#: ../memline.c:3459
msgid "Swap file already exists!"
msgstr "Tập tin trao đổi (swap) đã rồi!"
-#: ../memline.c:3464
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
@@ -4064,75 +3085,44 @@ msgstr ""
"&Q Thoát\n"
"&A Gián đoạn"
-#: ../memline.c:3467
-#, fuzzy
msgid ""
"&Open Read-Only\n"
"&Edit anyway\n"
"&Recover\n"
-"&Delete it\n"
"&Quit\n"
-"&Abort"
+"&Abort\n"
+"&Delete it"
msgstr ""
"&O Mở chỉ để Ä‘á»c\n"
"&E Vẫn soạn thảo\n"
"&R Phục hồi\n"
"&Q Thoát\n"
-"&A Gián đoạn"
+"&A Gián đoạn&D Xóa nó"
-#.
-#. * Change the ".swp" extension to find another file that can be used.
-#. * First decrement the last char: ".swo", ".swn", etc.
-#. * If that still isn't enough decrement the last but one char: ".svz"
-#. * Can happen when editing many "No Name" buffers.
-#.
-#. ".s?a"
-#. ".saa": tried enough, give up
-#: ../memline.c:3528
msgid "E326: Too many swap files found"
msgstr "E326: Tìm thấy quá nhiá»u tập tin trao đổi (swap)"
-#: ../memory.c:227
-#, c-format
-msgid "E342: Out of memory! (allocating %<PRIu64> bytes)"
-msgstr "E342: Không đủ bộ nhớ! (phân chia %<PRIu64> byte)"
-
-#: ../menu.c:62
msgid "E327: Part of menu-item path is not sub-menu"
msgstr ""
"E327: Má»™t phần cá»§a đưá»ng dẫn tá»›i phần tá»­ cá»§a trình đơn không phải là trình "
"đơn con"
-#: ../menu.c:63
msgid "E328: Menu only exists in another mode"
msgstr "E328: Trình đơn chỉ có trong chế độ khác"
-#: ../menu.c:64
-#, fuzzy, c-format
-msgid "E329: No menu \"%s\""
+msgid "E329: No menu of that name"
msgstr "E329: Không có trình đơn với tên như vậy"
-#. Only a mnemonic or accelerator is not valid.
-#: ../menu.c:329
-msgid "E792: Empty menu name"
-msgstr ""
-
-#: ../menu.c:340
msgid "E330: Menu path must not lead to a sub-menu"
msgstr "E330: ÄÆ°á»ng dẫn tá»›i trình đơn không được đưa tá»›i trình đơn con"
-#: ../menu.c:365
msgid "E331: Must not add menu items directly to menu bar"
msgstr ""
"E331: Các phần tử của trình đơn không thể thêm trực tiếp vào thanh trình đơn"
-#: ../menu.c:370
msgid "E332: Separator cannot be part of a menu path"
msgstr "E332: Cái phân chia không thể là má»™t phần cá»§a đưá»ng dẫn tá»›i trình đơn"
-#. Now we have found the matching menu, and we list the mappings
-#. Highlight title
-#: ../menu.c:762
msgid ""
"\n"
"--- Menus ---"
@@ -4140,70 +3130,62 @@ msgstr ""
"\n"
"--- Trình đơn ---"
-#: ../menu.c:1313
+msgid "Tear off this menu"
+msgstr "Chia cắt trình đơn này"
+
msgid "E333: Menu path must lead to a menu item"
msgstr "E333: ÄÆ°á»ng dẫn tá»›i trình đơn phải đưa tá»›i má»™t phần tá»­ cuả trình đơn"
-#: ../menu.c:1330
#, c-format
msgid "E334: Menu not found: %s"
msgstr "E334: Không tìm thấy trình đơn: %s"
-#: ../menu.c:1396
#, c-format
msgid "E335: Menu not defined for %s mode"
msgstr "E335: Trình đơn không được định nghĩa cho chế độ %s"
-#: ../menu.c:1426
msgid "E336: Menu path must lead to a sub-menu"
msgstr "E336: ÄÆ°á»ng dẫn tá»›i trình đơn phải đưa tá»›i má»™t trình đơn con"
-#: ../menu.c:1447
msgid "E337: Menu not found - check menu names"
msgstr "E337: Không tìm thấy trình đơn - hãy kiểm tra tên trình đơn"
-#: ../message.c:423
#, c-format
msgid "Error detected while processing %s:"
msgstr "Phát hiện lỗi khi xử lý %s:"
-#: ../message.c:445
#, c-format
msgid "line %4ld:"
msgstr "dòng %4ld:"
-#: ../message.c:617
-#, c-format
-msgid "E354: Invalid register name: '%s'"
-msgstr "E354: Tên sổ đăng ký không cho phép: '%s'"
+msgid "[string too long]"
+msgstr "[chuỗi quá dài]"
+
+msgid "Messages maintainer: The Vim Project"
+msgstr ""
+"Bản dịch các thông báo sang tiếng Việt: Phan Vĩnh Thịnh <teppi@vnlinux.org>"
-#: ../message.c:986
msgid "Interrupt: "
msgstr "Gián đoạn: "
-#: ../message.c:988
-#, fuzzy
-msgid "Press ENTER or type command to continue"
-msgstr "Nhấn phím ENTER hoặc nhập câu lệnh để tiếp tục"
+msgid "Hit ENTER to continue"
+msgstr "Nhấn phím ENTER để tiếp tục"
-#: ../message.c:1843
-#, fuzzy, c-format
-msgid "%s line %<PRId64>"
-msgstr "%s, dòng %<PRId64>"
+msgid "Hit ENTER or type command to continue"
+msgstr "Nhấn phím ENTER hoặc nhập câu lệnh để tiếp tục"
-#: ../message.c:2392
msgid "-- More --"
msgstr "-- Còn nữa --"
-#: ../message.c:2398
-msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
-msgstr ""
+msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
+msgstr " (RET/BS: dòng, SPACE/b: trang, d/u: nửa trang, q: thoát)"
+
+msgid " (RET: line, SPACE: page, d: half page, q: quit)"
+msgstr " (RET: dòng, SPACE: trang, d: nửa trang, q: thoát)"
-#: ../message.c:3021 ../message.c:3031
msgid "Question"
msgstr "Câu há»i"
-#: ../message.c:3023
msgid ""
"&Yes\n"
"&No"
@@ -4211,17 +3193,6 @@ msgstr ""
"&Có\n"
"&Không"
-#: ../message.c:3033
-msgid ""
-"&Yes\n"
-"&No\n"
-"&Cancel"
-msgstr ""
-"&Có\n"
-"&Không\n"
-"&Dừng"
-
-#: ../message.c:3045
msgid ""
"&Yes\n"
"&No\n"
@@ -4234,181 +3205,233 @@ msgstr ""
"&Vứt bỠtất cả\n"
"&Dừng lại"
-#: ../message.c:3058
-#, fuzzy
-msgid "E766: Insufficient arguments for printf()"
-msgstr "E116: Tham số cho hàm %s đưa ra không đúng"
+msgid "Save File dialog"
+msgstr "Ghi nhớ tập tin"
-#: ../message.c:3119
-msgid "E807: Expected Float argument for printf()"
-msgstr ""
+msgid "Open File dialog"
+msgstr "Mở tập tin"
-#: ../message.c:3873
-#, fuzzy
-msgid "E767: Too many arguments to printf()"
-msgstr "E118: Quá nhiá»u tham số cho hàm: %s"
+msgid "E338: Sorry, no file browser in console mode"
+msgstr ""
+"E338: Xin lỗi nhưng không có trình duyệt tập tin trong chế độ kênh giao tác "
+"(console)"
-#: ../misc1.c:2256
msgid "W10: Warning: Changing a readonly file"
msgstr "W10: Cảnh báo: Thay đổi má»™t tập tin chỉ có quyá»n Ä‘á»c"
-#: ../misc1.c:2537
-msgid "Type number and <Enter> or click with mouse (empty cancels): "
-msgstr ""
-
-#: ../misc1.c:2539
-msgid "Type number and <Enter> (empty cancels): "
-msgstr ""
-
-#: ../misc1.c:2585
msgid "1 more line"
msgstr "Thêm 1 dòng"
-#: ../misc1.c:2588
msgid "1 line less"
msgstr "Bớt 1 dòng"
-#: ../misc1.c:2593
#, c-format
-msgid "%<PRId64> more lines"
-msgstr "Thêm %<PRId64> dòng"
+msgid "%ld more lines"
+msgstr "Thêm %ld dòng"
-#: ../misc1.c:2596
#, c-format
-msgid "%<PRId64> fewer lines"
-msgstr "Bớt %<PRId64> dòng"
+msgid "%ld fewer lines"
+msgstr "Bớt %ld dòng"
-#: ../misc1.c:2599
msgid " (Interrupted)"
msgstr " (Bị gián đoạn)"
-#: ../misc1.c:2635
-msgid "Beep!"
+msgid "Vim: preserving files...\n"
+msgstr "Vim: ghi nhớ các tập tin...\n"
+
+msgid "Vim: Finished.\n"
+msgstr "Vim: Äã xong.\n"
+
+msgid "ERROR: "
+msgstr "Lá»–I: "
+
+#, c-format
+msgid ""
+"\n"
+"[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n"
msgstr ""
+"\n"
+"[byte] tổng phân phối-còn trống %lu-%lu, sử dụng %lu, píc sử dụng %lu\n"
+
+#, c-format
+msgid ""
+"[calls] total re/malloc()'s %lu, total free()'s %lu\n"
+"\n"
+msgstr ""
+"[gá»i] tổng re/malloc() %lu, tổng free() %lu\n"
+"\n"
+
+msgid "E340: Line is becoming too long"
+msgstr "E340: Dòng đang trở thành quá dài"
+
+#, c-format
+msgid "E341: Internal error: lalloc(%ld, )"
+msgstr "E341: Lá»—i ná»™i bá»™: lalloc(%ld, )"
+
+#, c-format
+msgid "E342: Out of memory! (allocating %lu bytes)"
+msgstr "E342: Không đủ bộ nhớ! (phân chia %lu byte)"
-#: ../misc2.c:738
#, c-format
msgid "Calling shell to execute: \"%s\""
msgstr "Gá»i shell để thá»±c hiện: \"%s\""
-#: ../normal.c:183
-msgid "E349: No identifier under cursor"
-msgstr "E349: Không có tên ở vị trí con trá»"
+msgid "E545: Missing colon"
+msgstr "E545: Thiếu dấu hai chấm"
-#: ../normal.c:1866
-#, fuzzy
-msgid "E774: 'operatorfunc' is empty"
-msgstr "E91: Tùy chá»n 'shell' là má»™t chuá»—i rá»—ng"
+msgid "E546: Illegal mode"
+msgstr "E546: Chế độ không cho phép"
+
+msgid "E547: Illegal mouseshape"
+msgstr "E547: Dạng trỠchuột không cho phép"
+
+# TODO: Capitalise first word of message?
+msgid "E548: Digit expected"
+msgstr "E548: yêu cầu một số"
+
+msgid "E549: Illegal percentage"
+msgstr "E549: Tỷ lệ phần trăm không cho phép"
+
+msgid "Enter encryption key: "
+msgstr "Nhập mật khẩu để mã hóa: "
+
+msgid "Enter same key again: "
+msgstr " Nhập lại mật khẩu:"
+
+msgid "Keys don't match!"
+msgstr "Hai mật khẩu không trùng nhau!"
+
+#, c-format
+msgid ""
+"E343: Invalid path: '**[number]' must be at the end of the path or be "
+"followed by '%s'."
+msgstr ""
+"E343: ÄÆ°á»ng dẫn đưa ra không đúng: '**[số]' phải ở cuối đưá»ng dẫn hoặc theo "
+"sau bởi '%s'"
+
+#, c-format
+msgid "E344: Can't find directory \"%s\" in cdpath"
+msgstr "E344: Không tìm thấy thư mục \"%s\" để chuyển thư mục"
+
+#, c-format
+msgid "E345: Can't find file \"%s\" in path"
+msgstr "E345: Không tìm thấy tập tin \"%s\" trong đưá»ng dẫn"
+
+#, c-format
+msgid "E346: No more directory \"%s\" found in cdpath"
+msgstr "E346: Trong đưá»ng dẫn thay đổi thư mục không còn có thư mục \"%s\" nữa"
+
+#, c-format
+msgid "E347: No more file \"%s\" found in path"
+msgstr "E347: Trong đưá»ng dẫn path không còn có tập tin \"%s\" nữa"
+
+msgid "E550: Missing colon"
+msgstr "E550: Thiếu dấu hai chấm"
+
+msgid "E551: Illegal component"
+msgstr "E551: Thành phần không cho phép"
+
+# TODO: Capitalise first word of message?
+msgid "E552: Digit expected"
+msgstr "E552: Cần chỉ ra một số"
+
+msgid "Cannot connect to Netbeans #2"
+msgstr "Không kết nối được với Netbeans #2"
+
+msgid "Cannot connect to Netbeans"
+msgstr "Không kết nối được với NetBeans"
+
+#, c-format
+msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
+msgstr ""
+"E668: Chế độ truy cập thông tin vỠliên kết với NetBeans không đúng: \"%s\""
+
+msgid "read from Netbeans socket"
+msgstr "Ä‘á»c từ socket NetBeans"
+
+#, c-format
+msgid "E658: NetBeans connection lost for buffer %ld"
+msgstr "E658: Bị mất liên kết với NetBeans cho bộ đệm %ld"
-#: ../normal.c:2637
msgid "Warning: terminal cannot highlight"
msgstr "Cảnh báo: terminal không thực hiện được sự chiếu sáng"
-#: ../normal.c:2807
msgid "E348: No string under cursor"
msgstr "E348: Không có chuá»—i ở vị trí con trá»"
-#: ../normal.c:3937
+msgid "E349: No identifier under cursor"
+msgstr "E349: Không có tên ở vị trí con trá»"
+
msgid "E352: Cannot erase folds with current 'foldmethod'"
msgstr ""
"E352: Không thể tẩy xóa nếp gấp vá»›i giá trị hiện thá»i cá»§a tùy chá»n "
"'foldmethod'"
-#: ../normal.c:5897
-msgid "E664: changelist is empty"
+# TODO: Capitalise first word of message?
+msgid "E664: Changelist is empty"
msgstr "E664: danh sách những thay đổi trống rỗng"
-#: ../normal.c:5899
msgid "E662: At start of changelist"
msgstr "E662: Ở đầu danh sách những thay đổi"
-#: ../normal.c:5901
msgid "E663: At end of changelist"
msgstr "E663: Ở cuối danh sách những thay đổi"
-#: ../normal.c:7053
-msgid "Type :quit<Enter> to exit Nvim"
+msgid "Type :quit<Enter> to exit Vim"
msgstr "Gõ :quit<Enter> để thoát khá»i Vim"
-#: ../ops.c:248
#, c-format
msgid "1 line %sed 1 time"
msgstr "Trên 1 dòng %s 1 lần"
-#: ../ops.c:250
#, c-format
msgid "1 line %sed %d times"
msgstr "Trên 1 dòng %s %d lần"
-#: ../ops.c:253
#, c-format
-msgid "%<PRId64> lines %sed 1 time"
-msgstr "Trên %<PRId64> dòng %s 1 lần"
+msgid "%ld lines %sed 1 time"
+msgstr "Trên %ld dòng %s 1 lần"
-#: ../ops.c:256
#, c-format
-msgid "%<PRId64> lines %sed %d times"
-msgstr "Trên %<PRId64> dòng %s %d lần"
+msgid "%ld lines %sed %d times"
+msgstr "Trên %ld dòng %s %d lần"
-#: ../ops.c:592
#, c-format
-msgid "%<PRId64> lines to indent... "
-msgstr "Thụt đầu %<PRId64> dòng..."
+msgid "%ld lines to indent... "
+msgstr "Thụt đầu %ld dòng..."
-#: ../ops.c:634
msgid "1 line indented "
msgstr "Äã thụt đầu 1 dòng"
-#: ../ops.c:636
#, c-format
-msgid "%<PRId64> lines indented "
-msgstr "%<PRId64> dòng đã thụt đầu"
-
-#: ../ops.c:938
-#, fuzzy
-msgid "E748: No previously used register"
-msgstr "E186: Không có thư mục trước"
+msgid "%ld lines indented "
+msgstr "%ld dòng đã thụt đầu"
-#. must display the prompt
-#: ../ops.c:1433
msgid "cannot yank; delete anyway"
msgstr "sao chép không thành công; đã xóa"
-#: ../ops.c:1929
msgid "1 line changed"
msgstr "1 dòng đã thay đổi"
-#: ../ops.c:1931
#, c-format
-msgid "%<PRId64> lines changed"
-msgstr "%<PRId64> đã thay đổi"
+msgid "%ld lines changed"
+msgstr "%ld đã thay đổi"
-#: ../ops.c:2521
-#, fuzzy
-msgid "block of 1 line yanked"
-msgstr "đã sao chép 1 dòng"
+#, c-format
+msgid "freeing %ld lines"
+msgstr "đã làm sạch %ld dòng"
-#: ../ops.c:2523
msgid "1 line yanked"
msgstr "đã sao chép 1 dòng"
-#: ../ops.c:2525
-#, fuzzy, c-format
-msgid "block of %<PRId64> lines yanked"
-msgstr "đã sao chép %<PRId64> dòng"
-
-#: ../ops.c:2528
#, c-format
-msgid "%<PRId64> lines yanked"
-msgstr "đã sao chép %<PRId64> dòng"
+msgid "%ld lines yanked"
+msgstr "đã sao chép %ld dòng"
-#: ../ops.c:2710
#, c-format
msgid "E353: Nothing in register %s"
msgstr "E353: Trong sổ đăng ký %s không có gì hết"
-#. Highlight title
-#: ../ops.c:3185
msgid ""
"\n"
"--- Registers ---"
@@ -4416,11 +3439,9 @@ msgstr ""
"\n"
"--- Sổ đăng ký ---"
-#: ../ops.c:4455
msgid "Illegal register name"
msgstr "Tên sổ đăng ký không cho phép"
-#: ../ops.c:4533
msgid ""
"\n"
"# Registers:\n"
@@ -4428,194 +3449,154 @@ msgstr ""
"\n"
"# Sổ đăng ký:\n"
-#: ../ops.c:4575
#, c-format
msgid "E574: Unknown register type %d"
msgstr "E574: Loại sổ đăng ký không biết %d"
-#: ../ops.c:5089
#, c-format
-msgid "%<PRId64> Cols; "
-msgstr "%<PRId64> Cá»™t; "
+msgid "E354: Invalid register name: '%s'"
+msgstr "E354: Tên sổ đăng ký không cho phép: '%s'"
-#: ../ops.c:5097
#, c-format
-msgid ""
-"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
-"%<PRId64> of %<PRId64> Bytes"
-msgstr ""
-"Chá»n %s%<PRId64> cá»§a %<PRId64> Dòng; %<PRId64> cá»§a %<PRId64> Từ; %<PRId64> "
-"cá»§a %<PRId64> Byte"
-
-#: ../ops.c:5105
-#, fuzzy, c-format
-msgid ""
-"Selected %s%<PRId64> of %<PRId64> Lines; %<PRId64> of %<PRId64> Words; "
-"%<PRId64> of %<PRId64> Chars; %<PRId64> of %<PRId64> Bytes"
-msgstr ""
-"Chá»n %s%<PRId64> cá»§a %<PRId64> Dòng; %<PRId64> cá»§a %<PRId64> Từ; %<PRId64> "
-"cá»§a %<PRId64> Byte"
+msgid "%ld Cols; "
+msgstr "%ld Cá»™t; "
-#: ../ops.c:5123
#, c-format
-msgid ""
-"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Byte "
-"%<PRId64> of %<PRId64>"
-msgstr ""
-"Cột %s của %s; Dòng %<PRId64> của %<PRId64>; Từ %<PRId64> của %<PRId64>; "
-"Byte %<PRId64> cá»§a %<PRId64>"
+msgid "Selected %s%ld of %ld Lines; %ld of %ld Words; %ld of %ld Bytes"
+msgstr "Chá»n %s%ld cá»§a %ld Dòng; %ld cá»§a %ld Từ; %ld cá»§a %ld Byte"
-#: ../ops.c:5133
-#, fuzzy, c-format
-msgid ""
-"Col %s of %s; Line %<PRId64> of %<PRId64>; Word %<PRId64> of %<PRId64>; Char "
-"%<PRId64> of %<PRId64>; Byte %<PRId64> of %<PRId64>"
-msgstr ""
-"Cột %s của %s; Dòng %<PRId64> của %<PRId64>; Từ %<PRId64> của %<PRId64>; "
-"Byte %<PRId64> cá»§a %<PRId64>"
+#, c-format
+msgid "Col %s of %s; Line %ld of %ld; Word %ld of %ld; Byte %ld of %ld"
+msgstr "Cột %s của %s; Dòng %ld của %ld; Từ %ld của %ld; Byte %ld của %ld"
-#: ../ops.c:5146
#, c-format
-msgid "(+%<PRId64> for BOM)"
-msgstr "(+%<PRId64> cho BOM)"
+msgid "(+%ld for BOM)"
+msgstr "(+%ld cho BOM)"
-#: ../option.c:1238
msgid "%<%f%h%m%=Page %N"
msgstr "%<%f%h%m%=Trang %N"
-#: ../option.c:1574
msgid "Thanks for flying Vim"
msgstr "Xin cảm ơn đã sử dụng Vim"
-#. found a mismatch: skip
-#: ../option.c:2698
msgid "E518: Unknown option"
msgstr "E518: Tùy chá»n không biết"
-#: ../option.c:2709
msgid "E519: Option not supported"
msgstr "E519: Tùy chá»n không được há»— trợ"
-#: ../option.c:2740
msgid "E520: Not allowed in a modeline"
msgstr "E520: Không cho phép trên dòng chế độ (modeline)"
-#: ../option.c:2815
-msgid "E846: Key code not set"
+msgid ""
+"\n"
+"\tLast set from "
msgstr ""
+"\n"
+"\tLần cuối cùng tùy chá»n thay đổi vào "
-#: ../option.c:2924
msgid "E521: Number required after ="
msgstr "E521: Sau dấu = cần đưa ra một số"
-#: ../option.c:3226 ../option.c:3864
msgid "E522: Not found in termcap"
msgstr "E522: Không tìm thấy trong termcap"
-#: ../option.c:3335
#, c-format
msgid "E539: Illegal character <%s>"
msgstr "E539: Ký tự không cho phép <%s>"
-#: ../option.c:3862
msgid "E529: Cannot set 'term' to empty string"
msgstr "E529: Giá trị cá»§a tùy chá»n 'term' không thể là má»™t chuá»—i trống rá»—ng"
-#: ../option.c:3885
+msgid "E530: Cannot change term in GUI"
+msgstr "E530: Không thể thay đổi terminal trong giao diện đồ há»a GUI"
+
+msgid "E531: Use \":gui\" to start the GUI"
+msgstr "E531: Hãy sá»­ dụng \":gui\" để chạy giao diện đồ há»a GUI"
+
msgid "E589: 'backupext' and 'patchmode' are equal"
msgstr "E589: giá trị cá»§a tùy chá»n 'backupext' và 'patchmode' bằng nhau"
-#: ../option.c:3964
-msgid "E834: Conflicts with value of 'listchars'"
-msgstr ""
+msgid "E617: Cannot be changed in the GTK+ 2 GUI"
+msgstr "E617: Không thể thay đổi trong giao diện đồ há»a GTK+ 2"
-#: ../option.c:3966
-msgid "E835: Conflicts with value of 'fillchars'"
-msgstr ""
-
-#: ../option.c:4163
msgid "E524: Missing colon"
msgstr "E524: Thiếu dấu hai chấm"
-#: ../option.c:4165
msgid "E525: Zero length string"
msgstr "E525: Chuỗi có độ dài bằng không"
-#: ../option.c:4220
#, c-format
msgid "E526: Missing number after <%s>"
msgstr "E526: Thiếu một số sau <%s>"
-#: ../option.c:4232
msgid "E527: Missing comma"
msgstr "E527: Thiếu dấu phẩy"
-#: ../option.c:4239
msgid "E528: Must specify a ' value"
msgstr "E528: Cần đưa ra một giá trị cho '"
-#: ../option.c:4271
msgid "E595: contains unprintable or wide character"
msgstr "E595: chứa ký tá»± không in ra hoặc ký tá»± vá»›i chiá»u rá»™ng gấp đôi"
-#: ../option.c:4469
+msgid "E596: Invalid font(s)"
+msgstr "E596: Phông chữ không đúng"
+
+# TODO: Capitalise first word of message?
+msgid "E597: Can't select fontset"
+msgstr "E597: không chá»n được bá»™ phông chữ"
+
+msgid "E598: Invalid fontset"
+msgstr "E598: Bộ phông chữ không đúng"
+
+# TODO: Capitalise first word of message?
+msgid "E533: Can't select wide font"
+msgstr "E533: không chá»n được phông chữ vá»›i các ký tá»± có chiá»u rá»™ng gấp đôi"
+
+msgid "E534: Invalid wide font"
+msgstr "E534: Phông chữ, vá»›i ký tá»± có chiá»u rá»™ng gấp đôi, không đúng"
+
#, c-format
msgid "E535: Illegal character after <%c>"
msgstr "E535: Ký tự sau <%c> không chính xác"
-#: ../option.c:4534
-msgid "E536: comma required"
+# TODO: Capitalise first word of message?
+msgid "E536: Comma required"
msgstr "E536: cầu có dấu phẩy"
-#: ../option.c:4543
#, c-format
msgid "E537: 'commentstring' must be empty or contain %s"
msgstr "E537: Giá trị cá»§a tùy chá»n 'commentstring' phải rá»—ng hoặc chứa %s"
-#: ../option.c:4928
+msgid "E538: No mouse support"
+msgstr "E538: Chuột không được hỗ trợ"
+
msgid "E540: Unclosed expression sequence"
msgstr "E540: Dãy các biểu thức không đóng"
-#: ../option.c:4932
-msgid "E541: too many items"
-msgstr "E541: quá nhiá»u phần tá»­"
-#: ../option.c:4934
-msgid "E542: unbalanced groups"
+# TODO: Capitalise first word of message?
+msgid "E542: Unbalanced groups"
msgstr "E542: các nhóm không cân bằng"
-#: ../option.c:5148
msgid "E590: A preview window already exists"
msgstr "E590: Cửa sổ xem trước đã có"
-#: ../option.c:5311
msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'"
msgstr "W17: Tiếng Ả Rập yêu cầu sử dụng UTF-8, hãy nhập ':set encoding=utf-8'"
-#: ../option.c:5623
#, c-format
msgid "E593: Need at least %d lines"
msgstr "E593: Cần ít nhất %d dòng"
-#: ../option.c:5631
#, c-format
msgid "E594: Need at least %d columns"
msgstr "E594: Cần ít nhất %d cột"
-#: ../option.c:6011
#, c-format
msgid "E355: Unknown option: %s"
msgstr "E355: Tùy chá»n không biết: %s"
-#. There's another character after zeros or the string
-#. * is empty. In both cases, we are trying to set a
-#. * num option using a string.
-#: ../option.c:6037
-#, fuzzy, c-format
-msgid "E521: Number required: &%s = '%s'"
-msgstr "E521: Sau dấu = cần đưa ra một số"
-
-#: ../option.c:6149
msgid ""
"\n"
"--- Terminal codes ---"
@@ -4623,7 +3604,6 @@ msgstr ""
"\n"
"--- Mã terminal ---"
-#: ../option.c:6151
msgid ""
"\n"
"--- Global option values ---"
@@ -4631,7 +3611,6 @@ msgstr ""
"\n"
"--- Giá trị tùy chá»n toàn cầu ---"
-#: ../option.c:6153
msgid ""
"\n"
"--- Local option values ---"
@@ -4639,7 +3618,6 @@ msgstr ""
"\n"
"--- Giá trị tùy chá»n ná»™i bá»™ ---"
-#: ../option.c:6155
msgid ""
"\n"
"--- Options ---"
@@ -4647,21 +3625,127 @@ msgstr ""
"\n"
"--- Tùy chá»n ---"
-#: ../option.c:6816
msgid "E356: get_varp ERROR"
msgstr "E356: Lá»–I get_varp"
-#: ../option.c:7696
#, c-format
msgid "E357: 'langmap': Matching character missing for %s"
msgstr "E357: 'langmap': Thiếu ký tự tương ứng cho %s"
-#: ../option.c:7715
#, c-format
msgid "E358: 'langmap': Extra characters after semicolon: %s"
msgstr "E358: 'langmap': Thừa ký tự sau dấu chấm phẩy: %s"
-#: ../os/shell.c:194
+msgid "cannot open "
+msgstr "không mở được "
+
+msgid "VIM: Can't open window!\n"
+msgstr "VIM: Không mở được cửa sổ!\n"
+
+msgid "Need Amigados version 2.04 or later\n"
+msgstr "Cần Amigados phiên bản 2.04 hoặc mới hơn\n"
+
+#, c-format
+msgid "Need %s version %ld\n"
+msgstr "Cần %s phiên bản %ld\n"
+
+msgid "Cannot open NIL:\n"
+msgstr "Không mở được NIL:\n"
+
+msgid "Cannot create "
+msgstr "Không tạo được "
+
+#, c-format
+msgid "Vim exiting with %d\n"
+msgstr "Thoát Vim với mã %d\n"
+
+msgid "cannot change console mode ?!\n"
+msgstr "không thay đổi được chế độ kênh giao tác (console)?!\n"
+
+msgid "mch_get_shellsize: not a console??\n"
+msgstr "mch_get_shellsize: không phải là kênh giao tác (console)??\n"
+
+msgid "E360: Cannot execute shell with -f option"
+msgstr "E360: Không chạy được shell vá»›i tùy chá»n -f"
+
+msgid "Cannot execute "
+msgstr "Không chạy được "
+
+msgid "shell "
+msgstr "shell "
+
+msgid " returned\n"
+msgstr " thoát\n"
+
+msgid "ANCHOR_BUF_SIZE too small."
+msgstr "Giá trị ANCHOR_BUF_SIZE quá nhá»."
+
+msgid "I/O ERROR"
+msgstr "LỖI I/O (NHẬP/XUẤT)"
+
+msgid "...(truncated)"
+msgstr "...(bị cắt bớt)"
+
+msgid "'columns' is not 80, cannot execute external commands"
+msgstr "Tùy chá»n 'columns' khác 80, chương trình ngoại trú không thể thá»±c hiện"
+
+msgid "E237: Printer selection failed"
+msgstr "E237: Chá»n máy in không thành công"
+
+#, c-format
+msgid "to %s on %s"
+msgstr "tới %s trên %s"
+
+#, c-format
+msgid "E613: Unknown printer font: %s"
+msgstr "E613: Không rõ phông chữ của máy in: %s"
+
+#, c-format
+msgid "E238: Print error: %s"
+msgstr "E238: Lá»—i in: %s"
+
+msgid "Unknown"
+msgstr "Không rõ"
+
+#, c-format
+msgid "Printing '%s'"
+msgstr "Äang in '%s'"
+
+#, c-format
+msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
+msgstr "E244: Tên bảng mã không cho phép \"%s\" trong tên phông chữ \"%s\""
+
+#, c-format
+msgid "E245: Illegal char '%c' in font name \"%s\""
+msgstr "E245: Ký tự không cho phép '%c' trong tên phông chữ \"%s\""
+
+msgid "Vim: Double signal, exiting\n"
+msgstr "Vim: Tín hiệu đôi, thoát\n"
+
+#, c-format
+msgid "Vim: Caught deadly signal %s\n"
+msgstr "Vim: Nhận được tín hiệu chết %s\n"
+
+msgid "Vim: Caught deadly signal\n"
+msgstr "Vim: Nhận được tín hiệu chết\n"
+
+#, c-format
+msgid "Opening the X display took %ld msec"
+msgstr "Mở màn hình X mất %ld mili giây"
+
+msgid ""
+"\n"
+"Vim: Got X error\n"
+msgstr ""
+"\n"
+"Vim: Lá»—i X\n"
+
+msgid "Testing the X display failed"
+msgstr "Kiểm tra màn hình X không thành công"
+
+msgid "Opening the X display timed out"
+msgstr "Không mở được màn hình X trong thá»i gian cho phép (time out)"
+
msgid ""
"\n"
"Cannot execute shell "
@@ -4669,7 +3753,13 @@ msgstr ""
"\n"
"Không chạy được shell "
-#: ../os/shell.c:439
+msgid ""
+"\n"
+"Cannot execute shell sh\n"
+msgstr ""
+"\n"
+"Không chạy được shell sh\n"
+
msgid ""
"\n"
"shell returned "
@@ -4677,959 +3767,387 @@ msgstr ""
"\n"
"shell dừng làm việc "
-#: ../os_unix.c:465 ../os_unix.c:471
msgid ""
"\n"
-"Could not get security context for "
+"Cannot create pipes\n"
msgstr ""
+"\n"
+"Không tạo được đưá»ng ống (pipe)\n"
-#: ../os_unix.c:479
msgid ""
"\n"
-"Could not set security context for "
+"Cannot fork\n"
msgstr ""
+"\n"
+"Không thực hiện được fork()\n"
-#: ../os_unix.c:1558 ../os_unix.c:1647
-#, c-format
-msgid "dlerror = \"%s\""
+msgid ""
+"\n"
+"Command terminated\n"
msgstr ""
+"\n"
+"Câu lệnh bị gián đoạn\n"
+
+msgid "XSMP lost ICE connection"
+msgstr "XSMP mất kết nối ICE"
+
+msgid "Opening the X display failed"
+msgstr "Mở màn hình X không thành công"
+
+msgid "XSMP handling save-yourself request"
+msgstr "XSMP xử lý yêu cầu tự động ghi nhớ"
+
+msgid "XSMP opening connection"
+msgstr "XSMP mở kết nối"
+
+msgid "XSMP ICE connection watch failed"
+msgstr "XSMP mất theo dõi kết nối ICE"
-#: ../path.c:1449
#, c-format
-msgid "E447: Can't find file \"%s\" in path"
-msgstr "E447: Không tìm thấy tập tin \"%s\" trong đưá»ng dẫn"
+msgid "XSMP SmcOpenConnection failed: %s"
+msgstr "XSMP thực hiện SmcOpenConnection không thành công: %s"
+
+msgid "At line"
+msgstr "Tại dòng"
+
+msgid "Could not allocate memory for command line."
+msgstr "Không phân chia được bộ nhớ cho dòng lệnh."
+
+msgid "VIM Error"
+msgstr "Lá»—i VIM"
+
+msgid "Could not load vim32.dll!"
+msgstr "Không nạp được vim32.dll!"
+
+msgid "Could not fix up function pointers to the DLL!"
+msgstr "Không sửa được cái chỉ (pointer) hàm số tới DLL!"
+
+#, c-format
+msgid "shell returned %d"
+msgstr "thoát shell với mã %d"
+
+#, c-format
+msgid "Vim: Caught %s event\n"
+msgstr "Vim: Nhận được sự kiện %s\n"
+
+msgid "close"
+msgstr "đóng"
+
+msgid "logoff"
+msgstr "thoát"
+
+msgid "shutdown"
+msgstr "tắt máy"
+
+msgid "E371: Command not found"
+msgstr "E371: Câu lệnh không tìm thấy"
+
+msgid ""
+"VIMRUN.EXE not found in your $PATH.\n"
+"External commands will not pause after completion.\n"
+"See :help win32-vimrun for more information."
+msgstr ""
+"Không tìm thấy VIMRUN.EXE trong $PATH.\n"
+"Lệnh ngoại trú sẽ không dừng lại sau khi hoàn thành.\n"
+"Thông tin chi tiết xem trong :help win32-vimrun"
+
+msgid "Vim Warning"
+msgstr "Cảnh báo Vim"
-#: ../quickfix.c:359
#, c-format
msgid "E372: Too many %%%c in format string"
msgstr "E372: Quá nhiá»u %%%c trong chuá»—i định dạng"
-#: ../quickfix.c:371
#, c-format
msgid "E373: Unexpected %%%c in format string"
msgstr "E373: Không mong đợi %%%c trong chuỗi định dạng"
-#: ../quickfix.c:420
msgid "E374: Missing ] in format string"
msgstr "E374: Thiếu ] trong chuỗi định dạng"
-#: ../quickfix.c:431
#, c-format
msgid "E375: Unsupported %%%c in format string"
msgstr "E375: %%%c không được hỗ trợ trong chuỗi định dạng"
-#: ../quickfix.c:448
#, c-format
msgid "E376: Invalid %%%c in format string prefix"
msgstr "E376: Không cho phép %%%c trong tiá»n tố cá»§a chuá»—i định dạng"
-#: ../quickfix.c:454
#, c-format
msgid "E377: Invalid %%%c in format string"
msgstr "E377: Không cho phép %%%c trong chuỗi định dạng"
-#. nothing found
-#: ../quickfix.c:477
msgid "E378: 'errorformat' contains no pattern"
msgstr "E378: Trong giá trị 'errorformat' thiếu mẫu (pattern)"
-#: ../quickfix.c:695
msgid "E379: Missing or empty directory name"
msgstr "E379: Tên thư mục không được đưa ra hoặc bằng một chuỗi rỗng"
-#: ../quickfix.c:1305
msgid "E553: No more items"
msgstr "E553: Không còn phần tử nào nữa"
-#: ../quickfix.c:1674
#, c-format
msgid "(%d of %d)%s%s: "
msgstr "(%d cá»§a %d)%s%s: "
-#: ../quickfix.c:1676
msgid " (line deleted)"
msgstr " (dòng bị xóa)"
-#: ../quickfix.c:1863
msgid "E380: At bottom of quickfix stack"
msgstr "E380: Ở dưới của đống sửa nhanh"
-#: ../quickfix.c:1869
msgid "E381: At top of quickfix stack"
msgstr "E381: Ở đầu của đống sửa nhanh"
-#: ../quickfix.c:1880
#, c-format
msgid "error list %d of %d; %d errors"
msgstr "danh sách lỗi %d của %d; %d lỗi"
-#: ../quickfix.c:2427
msgid "E382: Cannot write, 'buftype' option is set"
msgstr "E382: Không ghi nhớ được, giá trị 'buftype' không phải là chuỗi rỗng"
-#: ../quickfix.c:2812
-msgid "E683: File name missing or invalid pattern"
-msgstr ""
-
-#: ../quickfix.c:2911
-#, fuzzy, c-format
-msgid "Cannot open file \"%s\""
-msgstr "E624: Không thể mở tập tin \"%s\""
+# TODO: Capitalise first word of message?
+msgid "E369: Invalid item in %s%%[]"
+msgstr "E369: phần tử không cho phép trong %s%%[]"
-#: ../quickfix.c:3429
-#, fuzzy
-msgid "E681: Buffer is not loaded"
-msgstr "1 bộ đệm được bỠnạp từ bộ nhớ"
+msgid "E339: Pattern too long"
+msgstr "E339: Mẫu (pattern) quá dài"
-#: ../quickfix.c:3487
-#, fuzzy
-msgid "E777: String or List expected"
-msgstr "E548: yêu cầu một số"
+msgid "E50: Too many \\z("
+msgstr "E50: Quá nhiá»u \\z("
-#: ../regexp.c:359
#, c-format
-msgid "E369: invalid item in %s%%[]"
-msgstr "E369: phần tử không cho phép trong %s%%[]"
+msgid "E51: Too many %s("
+msgstr "E51: Quá nhiá»u %s("
-#: ../regexp.c:374
-#, fuzzy, c-format
-msgid "E769: Missing ] after %s["
-msgstr "E69: Thiếu ] sau %s%%["
+msgid "E52: Unmatched \\z("
+msgstr "E52: Không có cặp cho \\z("
-#: ../regexp.c:375
#, c-format
msgid "E53: Unmatched %s%%("
msgstr "E53: Không có cặp cho %s%%("
-#: ../regexp.c:376
#, c-format
msgid "E54: Unmatched %s("
msgstr "E54: Không có cặp cho %s("
-#: ../regexp.c:377
#, c-format
msgid "E55: Unmatched %s)"
msgstr "E55: Không có cặp cho %s)"
-#: ../regexp.c:378
-msgid "E66: \\z( not allowed here"
-msgstr "E66: \\z( không thể sử dụng ở đây"
-
-#: ../regexp.c:379
-msgid "E67: \\z1 et al. not allowed here"
-msgstr "E67: \\z1 và tương tự không được sử dụng ở đây"
-
-#: ../regexp.c:380
-#, c-format
-msgid "E69: Missing ] after %s%%["
-msgstr "E69: Thiếu ] sau %s%%["
-
-#: ../regexp.c:381
#, c-format
-msgid "E70: Empty %s%%[]"
-msgstr "E70: %s%%[] rá»—ng"
-
-#: ../regexp.c:1209 ../regexp.c:1224
-msgid "E339: Pattern too long"
-msgstr "E339: Mẫu (pattern) quá dài"
+msgid "E56: %s* operand could be empty"
+msgstr "E56: operand %s* không thể rỗng"
-#: ../regexp.c:1371
-msgid "E50: Too many \\z("
-msgstr "E50: Quá nhiá»u \\z("
-
-#: ../regexp.c:1378
#, c-format
-msgid "E51: Too many %s("
-msgstr "E51: Quá nhiá»u %s("
+msgid "E57: %s+ operand could be empty"
+msgstr "E57: operand %s+ không thể rỗng"
-#: ../regexp.c:1427
-msgid "E52: Unmatched \\z("
-msgstr "E52: Không có cặp cho \\z("
+# TODO: Capitalise first word of message?
+msgid "E59: Invalid character after %s@"
+msgstr "E59: ký tự không cho phép sau %s@"
-#: ../regexp.c:1637
#, c-format
-msgid "E59: invalid character after %s@"
-msgstr "E59: ký tự không cho phép sau %s@"
+msgid "E58: %s{ operand could be empty"
+msgstr "E58: operand %s{ không thể rỗng"
-#: ../regexp.c:1672
#, c-format
msgid "E60: Too many complex %s{...}s"
msgstr "E60: Quá nhiá»u cấu trúc phức tạp %s{...}"
-#: ../regexp.c:1687
#, c-format
msgid "E61: Nested %s*"
msgstr "E61: %s* lồng vào"
-#: ../regexp.c:1690
#, c-format
msgid "E62: Nested %s%c"
msgstr "E62: %s%c lồng vào"
-#: ../regexp.c:1800
-msgid "E63: invalid use of \\_"
+# TODO: Capitalise first word of message?
+msgid "E63: Invalid use of \\_"
msgstr "E63: không cho phép sử dụng \\_"
-#: ../regexp.c:1850
#, c-format
msgid "E64: %s%c follows nothing"
msgstr "E64: %s%c không theo sau gì cả"
-#: ../regexp.c:1902
msgid "E65: Illegal back reference"
msgstr "E65: Không cho phép liên kết ngược lại"
-#: ../regexp.c:1943
+msgid "E66: \\z( not allowed here"
+msgstr "E66: \\z( không thể sử dụng ở đây"
+
+msgid "E67: \\z1 - \\z9 not allowed here"
+msgstr "E67: \\z1 và tương tự không được sử dụng ở đây"
+
msgid "E68: Invalid character after \\z"
msgstr "E68: Ký tự không cho phép sau \\z"
-#: ../regexp.c:2049 ../regexp_nfa.c:1296
-#, fuzzy, c-format
-msgid "E678: Invalid character after %s%%[dxouU]"
-msgstr "E71: Ký tự không cho phép sau %s%%"
+#, c-format
+msgid "E69: Missing ] after %s%%["
+msgstr "E69: Thiếu ] sau %s%%["
+
+#, c-format
+msgid "E70: Empty %s%%[]"
+msgstr "E70: %s%%[] rá»—ng"
-#: ../regexp.c:2107
#, c-format
msgid "E71: Invalid character after %s%%"
msgstr "E71: Ký tự không cho phép sau %s%%"
-#: ../regexp.c:3017
#, c-format
msgid "E554: Syntax error in %s{...}"
msgstr "E554: Lỗi cú pháp trong %s{...}"
-#: ../regexp.c:3805
-msgid "External submatches:\n"
-msgstr "Sự tương ứng con ngoài:\n"
-
-#: ../regexp.c:7022
-msgid ""
-"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be "
-"used "
-msgstr ""
-
-#: ../regexp_nfa.c:239
-msgid "E865: (NFA) Regexp end encountered prematurely"
-msgstr ""
-
-#: ../regexp_nfa.c:240
-#, c-format
-msgid "E866: (NFA regexp) Misplaced %c"
-msgstr ""
-
-#: ../regexp_nfa.c:242
-#, c-format
-msgid "E877: (NFA regexp) Invalid character class: %<PRId64>"
-msgstr ""
-
-#: ../regexp_nfa.c:1261
-#, c-format
-msgid "E867: (NFA) Unknown operator '\\z%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1387
-#, c-format
-msgid "E867: (NFA) Unknown operator '\\%%%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1802
-#, c-format
-msgid "E869: (NFA) Unknown operator '\\@%c'"
-msgstr ""
-
-#: ../regexp_nfa.c:1831
-msgid "E870: (NFA regexp) Error reading repetition limits"
-msgstr ""
-
-#. 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 ""
-
-#. Too many `('
-#: ../regexp_nfa.c:2037
-msgid "E872: (NFA regexp) Too many '('"
-msgstr ""
-
-#: ../regexp_nfa.c:2042
-#, fuzzy
-msgid "E879: (NFA regexp) Too many \\z("
-msgstr "E50: Quá nhiá»u \\z("
-
-#: ../regexp_nfa.c:2066
-msgid "E873: (NFA regexp) proper termination error"
-msgstr ""
+msgid "E361: Crash intercepted; regexp too complex?"
+msgstr "E361: Sự cố được ngăn chặn; biểu thức chính quy quá phức tạp?"
-#: ../regexp_nfa.c:2599
-msgid "E874: (NFA) Could not pop the stack !"
-msgstr ""
-
-#: ../regexp_nfa.c:3298
-msgid ""
-"E875: (NFA regexp) (While converting from postfix to NFA), too many states "
-"left on stack"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E363: Pattern caused out-of-stack error"
+msgstr "E363: sử dụng mẫu (pattern) gây ra lỗi out-of-stack"
-#: ../regexp_nfa.c:3302
-msgid "E876: (NFA regexp) Not enough space to store the whole NFA "
-msgstr ""
-
-#: ../regexp_nfa.c:4571 ../regexp_nfa.c:4869
-msgid ""
-"Could not open temporary log file for writing, displaying on stderr ... "
-msgstr ""
+msgid "External submatches:\n"
+msgstr "Sự tương ứng con ngoài:\n"
-#: ../regexp_nfa.c:4840
#, c-format
-msgid "(NFA) COULD NOT OPEN %s !"
-msgstr ""
-
-#: ../regexp_nfa.c:6049
-#, fuzzy
-msgid "Could not open temporary log file for writing "
-msgstr "E214: Không tìm thấy tập tin tạm thá»i (temp) để ghi nhá»›"
+msgid "+--%3ld lines folded "
+msgstr "+--%3ld dòng được gấp"
-#: ../screen.c:7435
msgid " VREPLACE"
msgstr " THAY THẾ ẢO"
-#: ../screen.c:7437
msgid " REPLACE"
msgstr " THAY THẾ"
-#: ../screen.c:7440
msgid " REVERSE"
msgstr " NGƯỢC LẠI"
-#: ../screen.c:7441
msgid " INSERT"
msgstr " CHÈN"
-#: ../screen.c:7443
msgid " (insert)"
msgstr " (chèn)"
-#: ../screen.c:7445
msgid " (replace)"
msgstr " (thay thế)"
-#: ../screen.c:7447
msgid " (vreplace)"
msgstr " (thay thế ảo)"
-#: ../screen.c:7449
msgid " Hebrew"
msgstr " Do thái"
-#: ../screen.c:7454
msgid " Arabic"
msgstr " Ả rập"
-#: ../screen.c:7456
msgid " (lang)"
msgstr " (ngôn ngữ)"
-#: ../screen.c:7459
msgid " (paste)"
msgstr " (dán)"
-#: ../screen.c:7469
msgid " VISUAL"
msgstr " CHẾ ÄỘ VISUAL"
-#: ../screen.c:7470
msgid " VISUAL LINE"
msgstr " DÃ’NG VISUAL"
-#: ../screen.c:7471
msgid " VISUAL BLOCK"
msgstr " KHá»I VISUAL"
-#: ../screen.c:7472
msgid " SELECT"
msgstr " LỰA CHỌN"
-#: ../screen.c:7473
msgid " SELECT LINE"
msgstr " LỰA CHỌN DÒNG"
-#: ../screen.c:7474
msgid " SELECT BLOCK"
msgstr " Lá»°A CHỌN KHá»I"
-#: ../screen.c:7486 ../screen.c:7541
msgid "recording"
msgstr "đang ghi"
-#: ../search.c:487
+msgid "search hit TOP, continuing at BOTTOM"
+msgstr "tìm kiếm sẽ được tiếp tục từ CUá»I tài liệu"
+
+msgid "search hit BOTTOM, continuing at TOP"
+msgstr "tìm kiếm sẽ được tiếp tục từ ÄẦU tài liệu"
+
#, c-format
msgid "E383: Invalid search string: %s"
msgstr "E383: Chuỗi tìm kiếm không đúng: %s"
-#: ../search.c:832
-#, c-format
-msgid "E384: search hit TOP without match for: %s"
+# TODO: Capitalise first word of message?
+msgid "E384: Search hit TOP without match for: %s"
msgstr "E384: tìm kiếm kết thúc ở ÄẦU tập tin; không tìm thấy %s"
-#: ../search.c:835
-#, c-format
-msgid "E385: search hit BOTTOM without match for: %s"
+# TODO: Capitalise first word of message?
+msgid "E385: Search hit BOTTOM without match for: %s"
msgstr "E385: tìm kiếm kết thúc ở CUá»I tập tin; không tìm thấy %s"
-#: ../search.c:1200
msgid "E386: Expected '?' or '/' after ';'"
msgstr "E386: Mong đợi nhập '?' hoặc '/' sau ';'"
-#: ../search.c:4085
msgid " (includes previously listed match)"
msgstr " (gồm cả những tương ứng đã liệt kê trước đây)"
-#. cursor at status line
-#: ../search.c:4104
msgid "--- Included files "
msgstr "--- Tập tin tính đến "
-#: ../search.c:4106
msgid "not found "
msgstr "không tìm thấy "
-#: ../search.c:4107
msgid "in path ---\n"
msgstr "trong đưá»ng dẫn ---\n"
-#: ../search.c:4168
msgid " (Already listed)"
msgstr " (Äã liệt kê)"
-#: ../search.c:4170
msgid " NOT FOUND"
msgstr " KHÔNG TÌM THẤY"
-#: ../search.c:4211
#, c-format
msgid "Scanning included file: %s"
msgstr "Quét trong tập tin được tính đến: %s"
-#: ../search.c:4216
-#, fuzzy, c-format
-msgid "Searching included file %s"
-msgstr "Quét trong tập tin được tính đến: %s"
-
-#: ../search.c:4405
msgid "E387: Match is on current line"
msgstr "E387: Tương ứng nằm trên dòng hiện tại"
-#: ../search.c:4517
msgid "All included files were found"
msgstr "Tìm thấy tất cả các tập tin được tính đến"
-#: ../search.c:4519
msgid "No included files"
msgstr "Không có tập tin được tính đến"
-#: ../search.c:4527
msgid "E388: Couldn't find definition"
msgstr "E388: Không tìm thấy định nghĩa"
-#: ../search.c:4529
msgid "E389: Couldn't find pattern"
msgstr "E389: Không tìm thấy mẫu (pattern)"
-#: ../search.c:4668
-#, fuzzy
-msgid "Substitute "
-msgstr "1 thay thế"
-
-#: ../search.c:4681
-#, c-format
-msgid ""
-"\n"
-"# Last %sSearch Pattern:\n"
-"~"
-msgstr ""
-
-#: ../spell.c:951
-#, fuzzy
-msgid "E759: Format error in spell file"
-msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
-
-#: ../spell.c:952
-msgid "E758: Truncated spell file"
-msgstr ""
-
-#: ../spell.c:953
-#, c-format
-msgid "Trailing text in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:954
-#, c-format
-msgid "Affix name too long in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:955
-#, fuzzy
-msgid "E761: Format error in affix file FOL, LOW or UPP"
-msgstr "E431: Lỗi định dạng trong tập tin thẻ ghi \"%s\""
-
-#: ../spell.c:957
-msgid "E762: Character in FOL, LOW or UPP is out of range"
-msgstr ""
-
-#: ../spell.c:958
-msgid "Compressing word tree..."
-msgstr ""
-
-#: ../spell.c:1951
-msgid "E756: Spell checking is not enabled"
-msgstr ""
-
-#: ../spell.c:2249
-#, c-format
-msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""
-msgstr ""
-
-#: ../spell.c:2473
-#, fuzzy, c-format
-msgid "Reading spell file \"%s\""
-msgstr "Äang sá»­ dụng tập tin trao đổi (swap) \"%s\""
-
-#: ../spell.c:2496
-#, fuzzy
-msgid "E757: This does not look like a spell file"
-msgstr "E307: %s không phải là tập tin trao đổi (swap) của Vim"
-
-#: ../spell.c:2501
-msgid "E771: Old spell file, needs to be updated"
-msgstr ""
-
-#: ../spell.c:2504
-msgid "E772: Spell file is for newer version of Vim"
-msgstr ""
-
-#: ../spell.c:2602
-#, fuzzy
-msgid "E770: Unsupported section in spell file"
-msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
-
-#: ../spell.c:3762
-#, fuzzy, c-format
-msgid "Warning: region %s not supported"
-msgstr "E519: Tùy chá»n không được há»— trợ"
-
-#: ../spell.c:4550
-#, fuzzy, c-format
-msgid "Reading affix file %s ..."
-msgstr "Tìm kiếm tập tin thẻ ghi %s"
-
-#: ../spell.c:4589 ../spell.c:5635 ../spell.c:6140
-#, c-format
-msgid "Conversion failure for word in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4630 ../spell.c:6170
-#, c-format
-msgid "Conversion in %s not supported: from %s to %s"
-msgstr ""
-
-#: ../spell.c:4642
-#, c-format
-msgid "Invalid value for FLAG in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4655
-#, c-format
-msgid "FLAG after using flags in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4723
-#, c-format
-msgid ""
-"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line "
-"%d"
-msgstr ""
-
-#: ../spell.c:4731
-#, c-format
-msgid ""
-"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line "
-"%d"
-msgstr ""
-
-#: ../spell.c:4747
-#, c-format
-msgid "Wrong COMPOUNDRULES value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4771
-#, c-format
-msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4777
-#, c-format
-msgid "Wrong COMPOUNDMIN value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4783
-#, c-format
-msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4795
-#, c-format
-msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4847
-#, c-format
-msgid "Different combining flag in continued affix block in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4850
-#, fuzzy, c-format
-msgid "Duplicate affix in %s line %d: %s"
-msgstr "E154: Thẻ ghi lặp lại \"%s\" trong tập tin %s"
-
-#: ../spell.c:4871
-#, c-format
-msgid ""
-"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s "
-"line %d: %s"
-msgstr ""
-
-#: ../spell.c:4893
-#, c-format
-msgid "Expected Y or N in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:4968
-#, c-format
-msgid "Broken condition in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5091
-#, c-format
-msgid "Expected REP(SAL) count in %s line %d"
-msgstr ""
-
-#: ../spell.c:5120
-#, c-format
-msgid "Expected MAP count in %s line %d"
-msgstr ""
-
-#: ../spell.c:5132
-#, c-format
-msgid "Duplicate character in MAP in %s line %d"
-msgstr ""
-
-#: ../spell.c:5176
-#, c-format
-msgid "Unrecognized or duplicate item in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5197
-#, c-format
-msgid "Missing FOL/LOW/UPP line in %s"
-msgstr ""
-
-#: ../spell.c:5220
-msgid "COMPOUNDSYLMAX used without SYLLABLE"
-msgstr ""
-
-#: ../spell.c:5236
-#, fuzzy
-msgid "Too many postponed prefixes"
-msgstr "Có quá nhiá»u tham số soạn thảo"
-
-#: ../spell.c:5238
-#, fuzzy
-msgid "Too many compound flags"
-msgstr "Có quá nhiá»u tham số soạn thảo"
-
-#: ../spell.c:5240
-msgid "Too many postponed prefixes and/or compound flags"
-msgstr ""
-
-#: ../spell.c:5250
-#, c-format
-msgid "Missing SOFO%s line in %s"
-msgstr ""
-
-#: ../spell.c:5253
-#, c-format
-msgid "Both SAL and SOFO lines in %s"
-msgstr ""
-
-#: ../spell.c:5331
-#, c-format
-msgid "Flag is not a number in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5334
-#, c-format
-msgid "Illegal flag in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5493 ../spell.c:5501
-#, c-format
-msgid "%s value differs from what is used in another .aff file"
-msgstr ""
-
-#: ../spell.c:5602
-#, fuzzy, c-format
-msgid "Reading dictionary file %s ..."
-msgstr "Quét từ điển: %s"
-
-#: ../spell.c:5611
-#, c-format
-msgid "E760: No word count in %s"
-msgstr ""
-
-#: ../spell.c:5669
-#, c-format
-msgid "line %6d, word %6d - %s"
-msgstr ""
-
-#: ../spell.c:5691
-#, fuzzy, c-format
-msgid "Duplicate word in %s line %d: %s"
-msgstr "Tìm thấy tương ứng trên má»i dòng: %s"
-
-#: ../spell.c:5694
-#, c-format
-msgid "First duplicate word in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:5746
-#, c-format
-msgid "%d duplicate word(s) in %s"
-msgstr ""
-
-#: ../spell.c:5748
-#, c-format
-msgid "Ignored %d word(s) with non-ASCII characters in %s"
-msgstr ""
-
-#: ../spell.c:6115
-#, fuzzy, c-format
-msgid "Reading word file %s ..."
-msgstr "Äá»c từ đầu vào tiêu chuẩn stdin..."
-
-#: ../spell.c:6155
-#, c-format
-msgid "Duplicate /encoding= line ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6159
-#, c-format
-msgid "/encoding= line after word ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6180
-#, c-format
-msgid "Duplicate /regions= line ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6185
-#, c-format
-msgid "Too many regions in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6198
-#, c-format
-msgid "/ line ignored in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6224
-#, fuzzy, c-format
-msgid "Invalid region nr in %s line %d: %s"
-msgstr "E573: Sử dụng id máy chủ không đúng: %s"
-
-#: ../spell.c:6230
-#, c-format
-msgid "Unrecognized flags in %s line %d: %s"
-msgstr ""
-
-#: ../spell.c:6257
-#, c-format
-msgid "Ignored %d words with non-ASCII characters"
-msgstr ""
-
-#: ../spell.c:6656
-#, c-format
-msgid "Compressed %d of %d nodes; %d (%d%%) remaining"
-msgstr ""
-
-#: ../spell.c:7340
-msgid "Reading back spell file..."
-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 ""
-
-#: ../spell.c:7368
-#, c-format
-msgid "Number of words after soundfolding: %<PRId64>"
-msgstr ""
-
-#: ../spell.c:7476
-#, c-format
-msgid "Total number of words: %d"
-msgstr ""
-
-#: ../spell.c:7655
-#, fuzzy, c-format
-msgid "Writing suggestion file %s ..."
-msgstr "Ghi tập tin viminfo \"%s\""
-
-#: ../spell.c:7707 ../spell.c:7927
-#, c-format
-msgid "Estimated runtime memory use: %d bytes"
-msgstr ""
-
-#: ../spell.c:7820
-msgid "E751: Output file name must not have region name"
-msgstr ""
-
-#: ../spell.c:7822
-#, fuzzy
-msgid "E754: Only up to 8 regions supported"
-msgstr "E519: Tùy chá»n không được há»— trợ"
-
-#: ../spell.c:7846
-#, fuzzy, c-format
-msgid "E755: Invalid region in %s"
-msgstr "E15: Biểu thức không cho phép: %s"
-
-#: ../spell.c:7907
-msgid "Warning: both compounding and NOBREAK specified"
-msgstr ""
-
-#: ../spell.c:7920
-#, fuzzy, c-format
-msgid "Writing spell file %s ..."
-msgstr "Ghi tập tin viminfo \"%s\""
-
-#: ../spell.c:7925
-msgid "Done!"
-msgstr ""
-
-#: ../spell.c:8034
-#, c-format
-msgid "E765: 'spellfile' does not have %<PRId64> entries"
-msgstr ""
-
-#: ../spell.c:8074
-#, c-format
-msgid "Word '%.*s' removed from %s"
-msgstr ""
-
-#: ../spell.c:8117
-#, c-format
-msgid "Word '%.*s' added to %s"
-msgstr ""
-
-#: ../spell.c:8381
-msgid "E763: Word characters differ between spell files"
-msgstr ""
-
-#: ../spell.c:8684
-msgid "Sorry, no suggestions"
-msgstr ""
-
-#: ../spell.c:8687
-#, fuzzy, c-format
-msgid "Sorry, only %<PRId64> suggestions"
-msgstr " trên %<PRId64> dòng"
-
-#. for when 'cmdheight' > 1
-#. avoid more prompt
-#: ../spell.c:8704
-#, fuzzy, c-format
-msgid "Change \"%.*s\" to:"
-msgstr "Ghi nhớ thay đổi vào \"%.*s\"?"
-
-#: ../spell.c:8737
-#, c-format
-msgid " < \"%.*s\""
-msgstr ""
-
-#: ../spell.c:8882
-#, fuzzy
-msgid "E752: No previous spell replacement"
-msgstr "E35: Không có biểu thức chính quy trước"
-
-#: ../spell.c:8925
-#, fuzzy, c-format
-msgid "E753: Not found: %s"
-msgstr "E334: Không tìm thấy trình đơn: %s"
-
-#: ../spell.c:9276
-#, fuzzy, c-format
-msgid "E778: This does not look like a .sug file: %s"
-msgstr "E307: %s không phải là tập tin trao đổi (swap) của Vim"
-
-#: ../spell.c:9282
-#, c-format
-msgid "E779: Old .sug file, needs to be updated: %s"
-msgstr ""
-
-#: ../spell.c:9286
-#, c-format
-msgid "E780: .sug file is for newer version of Vim: %s"
-msgstr ""
-
-#: ../spell.c:9295
-#, c-format
-msgid "E781: .sug file doesn't match .spl file: %s"
-msgstr ""
-
-#: ../spell.c:9305
-#, fuzzy, c-format
-msgid "E782: error while reading .sug file: %s"
-msgstr "E47: Lá»—i khi Ä‘á»c tập tin lá»—i"
-
-#. This should have been checked when generating the .spl
-#. file.
-#: ../spell.c:11575
-msgid "E783: duplicate char in MAP entry"
-msgstr ""
-
-#: ../syntax.c:266
-msgid "No Syntax items defined for this buffer"
-msgstr "Không có phần tử cú pháp nào được định nghĩa cho bộ đệm này"
-
-#: ../syntax.c:3083 ../syntax.c:3104 ../syntax.c:3127
#, c-format
msgid "E390: Illegal argument: %s"
msgstr "E390: Tham số không cho phép: %s"
-#: ../syntax.c:3299
#, c-format
msgid "E391: No such syntax cluster: %s"
msgstr "E391: Không có cụm cú pháp như vậy: %s"
-#: ../syntax.c:3433
+msgid "No Syntax items defined for this buffer"
+msgstr "Không có phần tử cú pháp nào được định nghĩa cho bộ đệm này"
+
msgid "syncing on C-style comments"
msgstr "Äồng bá»™ hóa theo chú thích kiểu C"
-#: ../syntax.c:3439
msgid "no syncing"
msgstr "không đồng bộ hóa"
-#: ../syntax.c:3441
msgid "syncing starts "
msgstr "đồng bộ hóa bắt đầu "
-#: ../syntax.c:3443 ../syntax.c:3506
msgid " lines before top line"
msgstr " dòng trước dòng đầu tiên"
-#: ../syntax.c:3448
msgid ""
"\n"
"--- Syntax sync items ---"
@@ -5637,7 +4155,6 @@ msgstr ""
"\n"
"--- Phần tử đồng bộ hóa cú pháp ---"
-#: ../syntax.c:3452
msgid ""
"\n"
"syncing on items"
@@ -5645,7 +4162,6 @@ msgstr ""
"\n"
"đồng bộ hóa theo phần tử"
-#: ../syntax.c:3457
msgid ""
"\n"
"--- Syntax items ---"
@@ -5653,274 +4169,197 @@ msgstr ""
"\n"
"--- Phần tử cú pháp ---"
-#: ../syntax.c:3475
#, c-format
msgid "E392: No such syntax cluster: %s"
msgstr "E392: Không có cụm cú pháp như vậy: %s"
-#: ../syntax.c:3497
msgid "minimal "
msgstr "nhỠnhất "
-#: ../syntax.c:3503
msgid "maximal "
msgstr "lớn nhất "
-#: ../syntax.c:3513
msgid "; match "
msgstr "; tương ứng "
-#: ../syntax.c:3515
msgid " line breaks"
msgstr " chuyển dòng"
-#: ../syntax.c:4076
-msgid "E395: contains argument not accepted here"
-msgstr "E395: không được sử dụng tham số contains ở đây"
-
-#: ../syntax.c:4096
-#, fuzzy
-msgid "E844: invalid cchar value"
-msgstr "E474: Tham số không cho phép"
-
-#: ../syntax.c:4107
msgid "E393: group[t]here not accepted here"
msgstr "E393: không được sử dụng group[t]here ở đây"
-#: ../syntax.c:4126
#, c-format
msgid "E394: Didn't find region item for %s"
msgstr "E394: Phần tử vùng cho %s không tìm thấy"
-#: ../syntax.c:4188
-msgid "E397: Filename required"
-msgstr "E397: Yêu cầu tên tập tin"
+# TODO: Capitalise first word of message?
+msgid "E395: Contains argument not accepted here"
+msgstr "E395: không được sử dụng tham số contains ở đây"
-#: ../syntax.c:4221
-#, fuzzy
-msgid "E847: Too many syntax includes"
-msgstr "E77: Quá nhiá»u tên tập tin"
+msgid "E396: containedin argument not accepted here"
+msgstr "E396: không được sử dụng tham số containedin ở đây"
-#: ../syntax.c:4303
-#, fuzzy, c-format
-msgid "E789: Missing ']': %s"
-msgstr "E398: Thiếu '=': %s"
+msgid "E397: Filename required"
+msgstr "E397: Yêu cầu tên tập tin"
-#: ../syntax.c:4531
#, c-format
msgid "E398: Missing '=': %s"
msgstr "E398: Thiếu '=': %s"
-#: ../syntax.c:4666
#, c-format
msgid "E399: Not enough arguments: syntax region %s"
msgstr "E399: Không đủ tham số: vùng cú pháp %s"
-#: ../syntax.c:4870
-#, fuzzy
-msgid "E848: Too many syntax clusters"
-msgstr "E391: Không có cụm cú pháp như vậy: %s"
-
-#: ../syntax.c:4954
msgid "E400: No cluster specified"
msgstr "E400: Chưa chỉ ra cụm"
-#. end delimiter not found
-#: ../syntax.c:4986
#, c-format
msgid "E401: Pattern delimiter not found: %s"
msgstr "E401: Không tìm thấy ký tự phân chia mẫu (pattern): %s"
-#: ../syntax.c:5049
#, c-format
msgid "E402: Garbage after pattern: %s"
msgstr "E402: Rác ở sau mẫu (pattern): %s"
-#: ../syntax.c:5120
-msgid "E403: syntax sync: line continuations pattern specified twice"
+# TODO: Capitalise first word of message?
+msgid "E403: syntax sync: Line continuations pattern specified twice"
msgstr "E403: đồng bộ hóa cú pháp: mẫu tiếp tục của dòng chỉ ra hai lần"
-#: ../syntax.c:5169
#, c-format
msgid "E404: Illegal arguments: %s"
msgstr "E404: Tham số không cho phép: %s"
-#: ../syntax.c:5217
#, c-format
msgid "E405: Missing equal sign: %s"
msgstr "E405: Thiếu dấu bằng: %s"
-#: ../syntax.c:5222
#, c-format
msgid "E406: Empty argument: %s"
msgstr "E406: Tham số trống rỗng: %s"
-#: ../syntax.c:5240
#, c-format
msgid "E407: %s not allowed here"
msgstr "E407: %s không được cho phép ở đây"
-#: ../syntax.c:5246
#, c-format
msgid "E408: %s must be first in contains list"
msgstr "E408: %s phải là đầu tiên trong danh sách contains"
-#: ../syntax.c:5304
#, c-format
msgid "E409: Unknown group name: %s"
msgstr "E409: Tên nhóm không biết: %s"
-#: ../syntax.c:5512
#, c-format
msgid "E410: Invalid :syntax subcommand: %s"
msgstr "E410: Câu lệnh con :syntax không đúng: %s"
-#: ../syntax.c:5854
-msgid ""
-" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"
-msgstr ""
-
-#: ../syntax.c:6146
-msgid "E679: recursive loop loading syncolor.vim"
-msgstr ""
-
-#: ../syntax.c:6256
-#, c-format
-msgid "E411: highlight group not found: %s"
+# TODO: Capitalise first word of message?
+msgid "E411: Highlight group not found: %s"
msgstr "E411: không tìm thấy nhóm chiếu sáng cú pháp: %s"
-#: ../syntax.c:6278
#, c-format
msgid "E412: Not enough arguments: \":highlight link %s\""
msgstr "E412: Không đủ tham số: \":highlight link %s\""
-#: ../syntax.c:6284
#, c-format
msgid "E413: Too many arguments: \":highlight link %s\""
msgstr "E413: Quá nhiá»u tham số: \":highlight link %s\""
-#: ../syntax.c:6302
-msgid "E414: group has settings, highlight link ignored"
+# TODO: Capitalise first word of message?
+msgid "E414: Group has settings, highlight link ignored"
msgstr "E414: nhóm có thiết lập riêng, chiếu sáng liên kết bị bỠqua"
-#: ../syntax.c:6367
-#, c-format
-msgid "E415: unexpected equal sign: %s"
+# TODO: Capitalise first word of message?
+msgid "E415: Unexpected equal sign: %s"
msgstr "E415: dấu bằng không được mong đợi: %s"
-#: ../syntax.c:6395
-#, c-format
-msgid "E416: missing equal sign: %s"
+# TODO: Capitalise first word of message?
+msgid "E416: Missing equal sign: %s"
msgstr "E416: thiếu dấu bằng: %s"
-#: ../syntax.c:6418
-#, c-format
-msgid "E417: missing argument: %s"
+# TODO: Capitalise first word of message?
+msgid "E417: Missing argument: %s"
msgstr "E417: thiếu tham số: %s"
-#: ../syntax.c:6446
#, c-format
msgid "E418: Illegal value: %s"
msgstr "E418: Giá trị không cho phép: %s"
-#: ../syntax.c:6496
msgid "E419: FG color unknown"
msgstr "E419: Không rõ màu văn bản (FG)"
-#: ../syntax.c:6504
msgid "E420: BG color unknown"
msgstr "E420: Không rõ màu ná»n sau (BG)"
-#: ../syntax.c:6564
#, c-format
msgid "E421: Color name or number not recognized: %s"
msgstr "E421: Tên hoặc số của màu không được nhận ra: %s"
-#: ../syntax.c:6714
-#, c-format
-msgid "E422: terminal code too long: %s"
+# TODO: Capitalise first word of message?
+msgid "E422: Terminal code too long: %s"
msgstr "E422: mã terminal quá dài: %s"
-#: ../syntax.c:6753
#, c-format
msgid "E423: Illegal argument: %s"
msgstr "E423: Tham số không cho phép: %s"
-#: ../syntax.c:6925
msgid "E424: Too many different highlighting attributes in use"
msgstr "E424: Sá»­ dụng quá nhiá»u thuá»™c tính chiếu sáng cú pháp"
-#: ../syntax.c:7427
msgid "E669: Unprintable character in group name"
msgstr "E669: Ký tự không thể tin ra 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"
+msgid "W18: Invalid character in group name"
+msgstr "W18: Ký tự không cho phép trong tên nhóm"
-#: ../syntax.c:7448
-msgid "E849: Too many highlight and syntax groups"
-msgstr ""
-
-#: ../tag.c:104
-msgid "E555: at bottom of tag stack"
+# TODO: Capitalise first word of message?
+msgid "E555: At bottom of tag stack"
msgstr "E555: ở cuối đống thẻ ghi"
-#: ../tag.c:105
-msgid "E556: at top of tag stack"
+# TODO: Capitalise first word of message?
+msgid "E556: At top of tag stack"
msgstr "E556: ở đầu đống thẻ ghi"
-#: ../tag.c:380
msgid "E425: Cannot go before first matching tag"
msgstr "E425: Không chuyển được tới vị trí ở trước thẻ ghi tương ứng đầu tiên"
-#: ../tag.c:504
-#, c-format
-msgid "E426: tag not found: %s"
+# TODO: Capitalise first word of message?
+msgid "E426: Tag not found: %s"
msgstr "E426: không tìm thấy thẻ ghi: %s"
-#: ../tag.c:528
msgid " # pri kind tag"
msgstr " # pri loại thẻ ghi"
-#: ../tag.c:531
msgid "file\n"
msgstr "tập tin\n"
-#: ../tag.c:829
+msgid "Enter nr of choice (<CR> to abort): "
+msgstr "Hãy chá»n số cần thiết (<CR> để dừng):"
+
msgid "E427: There is only one matching tag"
msgstr "E427: Chỉ có một thẻ ghi tương ứng"
-#: ../tag.c:831
msgid "E428: Cannot go beyond last matching tag"
msgstr "E428: Không chuyển được tới vị trí ở sau thẻ ghi tương ứng cuối cùng"
-#: ../tag.c:850
#, c-format
msgid "File \"%s\" does not exist"
msgstr "Tập tin \"%s\" không tồn tại"
-#. Give an indication of the number of matching tags
-#: ../tag.c:859
#, c-format
msgid "tag %d of %d%s"
msgstr "thẻ ghi %d của %d%s"
-#: ../tag.c:862
msgid " or more"
msgstr " và hơn nữa"
-#: ../tag.c:864
msgid " Using tag with different case!"
msgstr " Äang sá»­ dụng thẻ ghi vá»›i kiểu chữ khác!"
-#: ../tag.c:909
#, c-format
msgid "E429: File \"%s\" does not exist"
msgstr "E429: Tập tin \"%s\" không tồn tại"
-#. Highlight title
-#: ../tag.c:960
msgid ""
"\n"
" # TO tag FROM line in file/text"
@@ -5928,79 +4367,58 @@ msgstr ""
"\n"
" # TỚI thẻ ghi TỪ dòng trong tập tin/văn bản"
-#: ../tag.c:1303
#, c-format
msgid "Searching tags file %s"
msgstr "Tìm kiếm tập tin thẻ ghi %s"
-#: ../tag.c:1545
-msgid "Ignoring long line in tags file"
-msgstr ""
+#, c-format
+msgid "E430: Tag file path truncated for %s\n"
+msgstr "E430: ÄÆ°á»ng dẫn tá»›i tập tin thẻ ghi bị cắt bá»›t cho %s\n"
-#: ../tag.c:1915
#, c-format
msgid "E431: Format error in tags file \"%s\""
msgstr "E431: Lỗi định dạng trong tập tin thẻ ghi \"%s\""
-#: ../tag.c:1917
#, c-format
-msgid "Before byte %<PRId64>"
-msgstr "Trước byte %<PRId64>"
+msgid "Before byte %ld"
+msgstr "Trước byte %ld"
-#: ../tag.c:1929
#, c-format
msgid "E432: Tags file not sorted: %s"
msgstr "E432: Tập tin thẻ ghi chưa được sắp xếp: %s"
-#. never opened any tags file
-#: ../tag.c:1960
msgid "E433: No tags file"
msgstr "E433: Không có tập tin thẻ ghi"
-#: ../tag.c:2536
msgid "E434: Can't find tag pattern"
msgstr "E434: Không tìm thấy mẫu thẻ ghi"
-#: ../tag.c:2544
msgid "E435: Couldn't find tag, just guessing!"
msgstr "E435: Không tìm thấy thẻ ghi, đang thử đoán!"
-#: ../tag.c:2797
-#, c-format
-msgid "Duplicate field name: %s"
-msgstr ""
-
-#: ../term.c:1442
msgid "' not known. Available builtin terminals are:"
msgstr "' không rõ. Có các terminal gắn sẵn (builtin) sau:"
-#: ../term.c:1463
msgid "defaulting to '"
msgstr "theo mặc định '"
-#: ../term.c:1731
msgid "E557: Cannot open termcap file"
msgstr "E557: Không thể mở tập tin termcap"
-#: ../term.c:1735
msgid "E558: Terminal entry not found in terminfo"
msgstr "E558: Trong terminfo không có bản ghi nào vỠterminal này"
-#: ../term.c:1737
msgid "E559: Terminal entry not found in termcap"
msgstr "E559: Trong termcap không có bản ghi nào vỠterminal này"
-#: ../term.c:1878
#, c-format
msgid "E436: No \"%s\" entry in termcap"
msgstr "E436: Trong termcap không có bản ghi \"%s\""
-#: ../term.c:2249
-msgid "E437: terminal capability \"cm\" required"
+# TODO: Capitalise first word of message?
+msgid "E437: Terminal capability \"cm\" required"
msgstr "E437: cần khả năng của terminal \"cm\""
-#. Highlight title
-#: ../term.c:4376
msgid ""
"\n"
"--- Terminal keys ---"
@@ -6008,195 +4426,120 @@ msgstr ""
"\n"
"--- Phím terminal ---"
-#: ../ui.c:481
+msgid "new shell started\n"
+msgstr "đã chạy shell mới\n"
+
msgid "Vim: Error reading input, exiting...\n"
msgstr "Vim: Lá»—i Ä‘á»c dữ liệu nhập, thoát...\n"
-#. This happens when the FileChangedRO autocommand changes the
-#. * file in a way it becomes shorter.
-#: ../undo.c:379
-msgid "E881: Line count changed unexpectedly"
-msgstr ""
+msgid "No undo possible; continue anyway"
+msgstr "Không thể hủy thao tác; tiếp tục thực hiện"
-#: ../undo.c:627
-#, fuzzy, c-format
-msgid "E828: Cannot open undo file for writing: %s"
-msgstr "E212: Không thể mở tập tin để ghi nhớ"
+# TODO: Capitalise first word of message?
+msgid "E438: u_undo: Line numbers wrong"
+msgstr "E438: u_undo: số thứ tự dòng không đúng"
+
+msgid "1 change"
+msgstr "duy nhất 1 thay đổi"
-#: ../undo.c:717
#, c-format
-msgid "E825: Corrupted undo file (%s): %s"
-msgstr ""
+msgid "%ld changes"
+msgstr "%ld thay đổi"
-#: ../undo.c:1039
-msgid "Cannot write undo file in any directory in 'undodir'"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E439: Undo list corrupt"
+msgstr "E439: danh sách há»§y thao tác (undo) bị há»ng"
-#: ../undo.c:1074
-#, c-format
-msgid "Will not overwrite with undo file, cannot read: %s"
-msgstr ""
+# TODO: Capitalise first word of message?
+msgid "E440: Undo line missing"
+msgstr "E440: bị mất dòng hủy thao tác"
-#: ../undo.c:1092
-#, c-format
-msgid "Will not overwrite, this is not an undo file: %s"
+msgid ""
+"\n"
+"MS-Windows 16/32-bit GUI version"
msgstr ""
+"\n"
+"Phiên bản vá»›i giao diện đồ há»a GUI cho MS-Windows 16/32 bit"
-#: ../undo.c:1108
-msgid "Skipping undo file write, nothing to undo"
+msgid ""
+"\n"
+"MS-Windows 32-bit GUI version"
msgstr ""
+"\n"
+"Phiên bản vá»›i giao diện đồ há»a GUI cho MS-Windows 32 bit"
-#: ../undo.c:1121
-#, fuzzy, c-format
-msgid "Writing undo file: %s"
-msgstr "Ghi tập tin viminfo \"%s\""
+msgid " in Win32s mode"
+msgstr " trong chế độ Win32"
-#: ../undo.c:1213
-#, fuzzy, c-format
-msgid "E829: write error in undo file: %s"
-msgstr "E297: Lỗi ghi nhớ tập tin trao đổi (swap)"
+msgid " with OLE support"
+msgstr " với hỗ trợ OLE"
-#: ../undo.c:1280
-#, c-format
-msgid "Not reading undo file, owner differs: %s"
+msgid ""
+"\n"
+"MS-Windows 32-bit console version"
msgstr ""
+"\n"
+"Phiên bản console cho MS-Windows 32 bit"
-#: ../undo.c:1292
-#, fuzzy, c-format
-msgid "Reading undo file: %s"
-msgstr "Äá»c tập tin viminfo \"%s\"%s%s%s"
-
-#: ../undo.c:1299
-#, fuzzy, c-format
-msgid "E822: Cannot open undo file for reading: %s"
-msgstr "E195: Không thể mở tập tin viminfo để Ä‘á»c"
-
-#: ../undo.c:1308
-#, fuzzy, c-format
-msgid "E823: Not an undo file: %s"
-msgstr "E484: Không mở được tập tin %s"
-
-#: ../undo.c:1313
-#, fuzzy, c-format
-msgid "E824: Incompatible undo file: %s"
-msgstr "E484: Không mở được tập tin %s"
-
-#: ../undo.c:1328
-msgid "File contents changed, cannot use undo info"
+msgid ""
+"\n"
+"MS-Windows 16-bit version"
msgstr ""
+"\n"
+"Phiên bản cho MS-Windows 16 bit"
-#: ../undo.c:1497
-#, fuzzy, c-format
-msgid "Finished reading undo file %s"
-msgstr "thực hiện xong %s"
-
-#: ../undo.c:1586 ../undo.c:1812
-msgid "Already at oldest change"
+msgid ""
+"\n"
+"32-bit MS-DOS version"
msgstr ""
+"\n"
+"Phiên bản cho MS-DOS 32 bit"
-#: ../undo.c:1597 ../undo.c:1814
-msgid "Already at newest change"
+msgid ""
+"\n"
+"16-bit MS-DOS version"
msgstr ""
+"\n"
+"Phiên bản cho MS-DOS 16 bit"
-#: ../undo.c:1806
-#, fuzzy, c-format
-msgid "E830: Undo number %<PRId64> not found"
-msgstr "E92: Bộ đệm %<PRId64> không được tìm thấy"
-
-#: ../undo.c:1979
-msgid "E438: u_undo: line numbers wrong"
-msgstr "E438: u_undo: số thứ tự dòng không đúng"
-
-#: ../undo.c:2183
-#, fuzzy
-msgid "more line"
-msgstr "Thêm 1 dòng"
-
-#: ../undo.c:2185
-#, fuzzy
-msgid "more lines"
-msgstr "Thêm 1 dòng"
-
-#: ../undo.c:2187
-#, fuzzy
-msgid "line less"
-msgstr "Bớt 1 dòng"
-
-#: ../undo.c:2189
-#, fuzzy
-msgid "fewer lines"
-msgstr "Bớt %<PRId64> dòng"
-
-#: ../undo.c:2193
-#, fuzzy
-msgid "change"
-msgstr "duy nhất 1 thay đổi"
-
-#: ../undo.c:2195
-#, fuzzy
-msgid "changes"
-msgstr "duy nhất 1 thay đổi"
-
-#: ../undo.c:2225
-#, fuzzy, c-format
-msgid "%<PRId64> %s; %s #%<PRId64> %s"
-msgstr "Trên %<PRId64> dòng %s %d lần"
-
-#: ../undo.c:2228
-msgid "before"
+msgid ""
+"\n"
+"MacOS X (unix) version"
msgstr ""
+"\n"
+"Phiên bản cho MacOS X (unix)"
-#: ../undo.c:2228
-msgid "after"
+msgid ""
+"\n"
+"MacOS X version"
msgstr ""
+"\n"
+"Phiên bản cho MacOS X"
-#: ../undo.c:2325
-#, fuzzy
-msgid "Nothing to undo"
-msgstr "Không tìm thấy ánh xạ"
-
-#: ../undo.c:2330
-msgid "number changes when saved"
+msgid ""
+"\n"
+"MacOS version"
msgstr ""
+"\n"
+"Phiên bản cho MacOS"
-#: ../undo.c:2360
-#, fuzzy, c-format
-msgid "%<PRId64> seconds ago"
-msgstr "%<PRId64> Cá»™t; "
-
-#: ../undo.c:2372
-#, fuzzy
-msgid "E790: undojoin is not allowed after undo"
-msgstr "E407: %s không được cho phép ở đây"
-
-#: ../undo.c:2466
-msgid "E439: undo list corrupt"
-msgstr "E439: danh sách há»§y thao tác (undo) bị há»ng"
-
-#: ../undo.c:2495
-msgid "E440: undo line missing"
-msgstr "E440: bị mất dòng hủy thao tác"
-
-#: ../version.c:600
msgid ""
"\n"
-"Included patches: "
+"RISC OS version"
msgstr ""
"\n"
-"Bao gồm các bản vá lỗi: "
+"Phiên bản cho RISC OS"
-#: ../version.c:627
-#, fuzzy
msgid ""
"\n"
-"Extra patches: "
-msgstr "Sự tương ứng con ngoài:\n"
+"Included patches: "
+msgstr ""
+"\n"
+"Bao gồm các bản vá lỗi: "
-#: ../version.c:639 ../version.c:864
msgid "Modified by "
msgstr "Với các thay đổi bởi "
-#: ../version.c:646
msgid ""
"\n"
"Compiled "
@@ -6204,11 +4547,9 @@ msgstr ""
"\n"
"ÄÆ°á»£c biên dịch "
-#: ../version.c:649
msgid "by "
msgstr "bởi "
-#: ../version.c:660
msgid ""
"\n"
"Huge version "
@@ -6216,1521 +4557,574 @@ msgstr ""
"\n"
"Phiên bản khổng lồ "
-#: ../version.c:661
+msgid ""
+"\n"
+"Big version "
+msgstr ""
+"\n"
+"Phiên bản lớn "
+
+msgid ""
+"\n"
+"Normal version "
+msgstr ""
+"\n"
+"Phiên bản thông thưá»ng "
+
+msgid ""
+"\n"
+"Small version "
+msgstr ""
+"\n"
+"Phiên bản nhỠ"
+
+msgid ""
+"\n"
+"Tiny version "
+msgstr ""
+"\n"
+"Phiên bản \"tí hon\" "
+
msgid "without GUI."
msgstr "không có giao diện đồ há»a GUI."
-#: ../version.c:662
+msgid "with GTK2-GNOME GUI."
+msgstr "vá»›i giao diện đồ há»a GUI GTK2-GNOME."
+
+msgid "with GTK-GNOME GUI."
+msgstr "vá»›i giao diện đồ há»a GUI GTK-GNOME."
+
+msgid "with GTK2 GUI."
+msgstr "vá»›i giao diện đồ há»a GUI GTK2."
+
+msgid "with GTK GUI."
+msgstr "vá»›i giao diện đồ há»a GUI GTK."
+
+msgid "with X11-Motif GUI."
+msgstr "vá»›i giao diện đồ há»a GUI X11-Motif."
+
+msgid "with X11-neXtaw GUI."
+msgstr "vá»›i giao diện đồ há»a GUI X11-neXtaw."
+
+msgid "with X11-Athena GUI."
+msgstr "vá»›i giao diện đồ há»a GUI X11-Athena."
+
+msgid "with BeOS GUI."
+msgstr "vá»›i giao diện đồ há»a GUI BeOS."
+
+msgid "with Photon GUI."
+msgstr "vá»›i giao diện đồ há»a GUI Photon."
+
+msgid "with GUI."
+msgstr "vá»›i giao diện đồ há»a GUI."
+
+msgid "with Carbon GUI."
+msgstr "vá»›i giao diện đồ há»a GUI Carbon."
+
+msgid "with Cocoa GUI."
+msgstr "vá»›i giao diện đồ há»a GUI Cocoa."
+
+msgid "with (classic) GUI."
+msgstr "vá»›i giao diện đồ há»a (cổ Ä‘iển) GUI."
+
msgid " Features included (+) or not (-):\n"
msgstr " Tính năng có (+) hoặc không (-):\n"
-#: ../version.c:667
msgid " system vimrc file: \""
msgstr " tập tin vimrc chung cho hệ thống: \""
-#: ../version.c:672
msgid " user vimrc file: \""
msgstr " tập tin vimrc cá»§a ngưá»i dùng: \""
-#: ../version.c:677
msgid " 2nd user vimrc file: \""
msgstr " tập tin vimrc thứ hai cá»§a ngưá»i dùng: \""
-#: ../version.c:682
msgid " 3rd user vimrc file: \""
msgstr " tập tin vimrc thứ ba cá»§a ngưá»i dùng: \""
-#: ../version.c:687
msgid " user exrc file: \""
msgstr " tập tin exrc cá»§a ngưá»i dùng: \""
-#: ../version.c:692
msgid " 2nd user exrc file: \""
msgstr " tập tin exrc thứ hai cá»§a ngưá»i dùng: \""
-#: ../version.c:699
+msgid " system gvimrc file: \""
+msgstr " tập tin gvimrc chung cho hệ thống: \""
+
+msgid " user gvimrc file: \""
+msgstr " tập tin gvimrc cá»§a ngưá»i dùng: \""
+
+msgid "2nd user gvimrc file: \""
+msgstr " tập tin gvimrc thứ hai cá»§a ngưá»i dùng: \""
+
+msgid "3rd user gvimrc file: \""
+msgstr " tập tin gvimrc thứ ba cá»§a ngưá»i dùng: \""
+
+msgid " system menu file: \""
+msgstr " tập tin trình đơn chung cho hệ thống: \""
+
msgid " fall-back for $VIM: \""
msgstr " giá trị $VIM theo mặc định: \""
-#: ../version.c:705
msgid " f-b for $VIMRUNTIME: \""
msgstr " giá trị $VIMRUNTIME theo mặc định: \""
-#: ../version.c:709
msgid "Compilation: "
msgstr "Tham số biên dịch: "
-#: ../version.c:712
+msgid "Compiler: "
+msgstr "Trình biên dịch: "
+
msgid "Linking: "
msgstr "Liên kết: "
-#: ../version.c:717
msgid " DEBUG BUILD"
msgstr " BIÊN DỊCH SỬA LỖI (DEBUG)"
-#: ../version.c:767
msgid "VIM - Vi IMproved"
msgstr "VIM ::: Vi IMproved (Vi cải tiến) ::: Phiên bản tiếng Việt"
-#: ../version.c:769
msgid "version "
msgstr "phiên bản "
-#: ../version.c:770
msgid "by Bram Moolenaar et al."
msgstr "Do Bram Moolenaar và những ngưá»i khác thá»±c hiện"
-#: ../version.c:774
msgid "Vim is open source and freely distributable"
msgstr "Vim là chương trình mã nguồn mở và phân phối tự do"
-#: ../version.c:776
msgid "Help poor children in Uganda!"
msgstr "Hãy giúp đỡ trẻ em nghèo Uganda!"
-#: ../version.c:777
msgid "type :help iccf<Enter> for information "
msgstr "hãy gõ :help iccf<Enter> để biết thêm thông tin"
-#: ../version.c:779
msgid "type :q<Enter> to exit "
msgstr " hãy gõ :q<Enter> để thoát khá»i chương trình "
-#: ../version.c:780
msgid "type :help<Enter> or <F1> for on-line help"
msgstr " hãy gõ :help<Enter> hoặc <F1> để có được trợ giúp "
-#: ../version.c:781
-msgid "type :help version7<Enter> for version info"
-msgstr "hãy gõ :help version7<Enter> để biết vỠphiên bản này "
+msgid "type :help version9<Enter> for version info"
+msgstr "hãy gõ :help version9<Enter> để biết vỠphiên bản này "
-#: ../version.c:784
msgid "Running in Vi compatible mode"
msgstr "Làm việc trong chế độ tương thích với Vi"
-#: ../version.c:785
msgid "type :set nocp<Enter> for Vim defaults"
msgstr "hãy gõ :set nocp<Enter> để chuyển vào chế độ Vim "
-#: ../version.c:786
msgid "type :help cp-default<Enter> for info on this"
msgstr "hãy gõ :help cp-default<Enter> để có thêm thông tin vá» Ä‘iá»u này"
-#: ../version.c:827
+msgid "menu Help->Orphans for information "
+msgstr "trình đơn Trợ giúp->Mồ côi để có thêm thông tin "
+
+msgid "Running modeless, typed text is inserted"
+msgstr "Không chế độ, văn bản nhập vào sẽ được chèn"
+
+msgid "menu Edit->Global Settings->Toggle Insert Mode "
+msgstr "trình đơn Soạn thảo->Thiết lập chung->Chế độ chèn "
+
+msgid " for two modes "
+msgstr " cho hai chế độ "
+
+msgid "menu Edit->Global Settings->Toggle Vi Compatible"
+msgstr ""
+"trình đơn Soạn thảo->Thiết lập chung->Tương thích với Vi "
+
+msgid " for Vim defaults "
+msgstr ""
+" để chuyển vào chế độ Vim mặc định "
+
msgid "Sponsor Vim development!"
msgstr "Hãy giúp đỡ phát triển Vim!"
-#: ../version.c:828
msgid "Become a registered Vim user!"
msgstr "Hãy trở thành ngưá»i dùng đăng ký cá»§a Vim!"
-#: ../version.c:831
msgid "type :help sponsor<Enter> for information "
msgstr "hãy gõ :help sponsor<Enter> để biết thêm thông tin "
-#: ../version.c:832
msgid "type :help register<Enter> for information "
msgstr "hãy gõ :help register<Enter> để biết thêm thông tin "
-#: ../version.c:834
msgid "menu Help->Sponsor/Register for information "
msgstr "trình đơn Trợ giúp->Giúp đỡ/Äăng ký để biết thêm thông tin "
-#: ../window.c:119
-msgid "Already only one window"
-msgstr "Chỉ có một cửa sổ"
+msgid "WARNING: Windows 95/98/ME detected"
+msgstr "CẢNH BÃO: nhận ra Windows 95/98/ME"
+
+msgid "type :help windows95<Enter> for info on this"
+msgstr "hãy gõ :help windows95<Enter> để biết thêm thông tin "
-#: ../window.c:224
msgid "E441: There is no preview window"
msgstr "E441: Không có cửa sổ xem trước"
-#: ../window.c:559
msgid "E442: Can't split topleft and botright at the same time"
msgstr ""
"E442: Cá»­a sổ không thể đồng thá»i ở bên trái phía trên và bên phải phía dưới"
-#: ../window.c:1228
msgid "E443: Cannot rotate when another window is split"
msgstr "E443: Không đổi được chỗ khi cửa sổ khác được chia"
-#: ../window.c:1803
msgid "E444: Cannot close last window"
msgstr "E444: Không được đóng cửa sổ cuối cùng"
-#: ../window.c:1810
-#, fuzzy
-msgid "E813: Cannot close autocmd window"
-msgstr "E444: Không được đóng cửa sổ cuối cùng"
-
-#: ../window.c:1814
-#, fuzzy
-msgid "E814: Cannot close window, only autocmd window would remain"
-msgstr "E444: Không được đóng cửa sổ cuối cùng"
+msgid "Already only one window"
+msgstr "Chỉ có một cửa sổ"
-#: ../window.c:2717
msgid "E445: Other window contains changes"
msgstr "E445: Cửa sổ khác có thay đổi chưa được ghi nhớ"
-#: ../window.c:4805
msgid "E446: No file name under cursor"
msgstr "E446: Không có tên tập tin tại vị trí con trá»"
-#~ msgid "[Error List]"
-#~ msgstr "[Danh sách lỗi]"
-
-#~ msgid "[No File]"
-#~ msgstr "[Không có tập tin]"
-
-#~ msgid "Patch file"
-#~ msgstr "Tập tin vá lỗi (patch)"
-
-#~ msgid "E106: Unknown variable: \"%s\""
-#~ msgstr "E106: Biến không biết: \"%s\""
-
-#~ msgid ""
-#~ "&OK\n"
-#~ "&Cancel"
-#~ msgstr ""
-#~ "&OK\n"
-#~ "&Há»§y bá»"
-
-#~ msgid "E240: No connection to Vim server"
-#~ msgstr "E240: Không có kết nối với máy chủ Vim"
-
-#~ msgid "E277: Unable to read a server reply"
-#~ msgstr "E277: Máy chá»§ không trả lá»i"
-
-#~ msgid "E241: Unable to send to %s"
-#~ msgstr "E241: Không thể gửi tin nhắn tới %s"
-
-#~ msgid "E130: Undefined function: %s"
-#~ msgstr "E130: Hàm số %s chưa xác định"
-
-#~ msgid "Save As"
-#~ msgstr "Ghi nhớ như"
-
-#~ msgid "Source Vim script"
-#~ msgstr "Thực hiện script của Vim"
-
-#~ msgid "Edit File"
-#~ msgstr "Soạn thảo tập tin"
-
-#~ msgid " (NOT FOUND)"
-#~ msgstr " (KHÔNG TÌM THẤY)"
-
-#~ msgid "Edit File in new window"
-#~ msgstr "Soạn thảo tập tin trong cửa sổ mới"
-
-#~ msgid "Append File"
-#~ msgstr "Thêm tập tin"
-
-#~ msgid "Window position: X %d, Y %d"
-#~ msgstr "Vị trí cửa sổ: X %d, Y %d"
-
-#~ msgid "Save Redirection"
-#~ msgstr "Chuyển hướng ghi nhớ"
-
-#~ msgid "Save View"
-#~ msgstr "Ghi nhớ vẻ ngoài"
-
-#~ msgid "Save Session"
-#~ msgstr "Ghi nhớ buổi làm việc"
-
-#~ msgid "Save Setup"
-#~ msgstr "Ghi nhớ cấu hình"
-
-#~ msgid "E196: No digraphs in this version"
-#~ msgstr "E196: Trong phiên bản này chữ ghép không được hỗ trợ"
-
-#~ msgid "[NL found]"
-#~ msgstr "[tìm thấy ký tự NL]"
-
-#~ msgid "[crypted]"
-#~ msgstr "[đã mã hóa]"
-
-#~ msgid "[CONVERSION ERROR]"
-#~ msgstr "[LỖI CHUYỂN BẢNG MÃ]"
-
-#~ msgid "NetBeans disallows writes of unmodified buffers"
-#~ msgstr "NetBeans không cho phép ghi nhớ bộ đệm chưa có thay đổi nào"
-
-#~ msgid "Partial writes disallowed for NetBeans buffers"
-#~ msgstr "Ghi nhớ một phần bộ đệm NetBeans không được cho phép"
-
-#~ msgid "E460: The resource fork would be lost (add ! to override)"
-#~ msgstr ""
-#~ "E460: Nhánh tài nguyên sẽ bị mất (thêm ! để bỠqua việc kiểm tra lại)"
-
-#~ msgid "<cannot open> "
-#~ msgstr "<không thể mở> "
-
-#~ msgid "E616: vim_SelFile: can't get font %s"
-#~ msgstr "E616: vim_SelFile: không tìm thấy phông chữ %s"
-
-#~ msgid "E614: vim_SelFile: can't return to current directory"
-#~ msgstr "E614: vim_SelFile: không trở lại được thư mục hiện thá»i"
-
-#~ msgid "Pathname:"
-#~ msgstr "ÄÆ°á»ng dẫn tá»›i tập tin:"
-
-#~ msgid "E615: vim_SelFile: can't get current directory"
-#~ msgstr "E615: vim_SelFile: không tìm thấy thư mục hiện thá»i"
-
-#~ msgid "OK"
-#~ msgstr "Äồng ý"
-
-#~ msgid "Cancel"
-#~ msgstr "Há»§y bá»"
-
-#~ msgid "Vim dialog"
-#~ msgstr "Hộp thoại Vim"
-
-#~ msgid "Scrollbar Widget: Could not get geometry of thumb pixmap."
-#~ msgstr "Thanh cuá»™n: Không thể xác định hình há»c cá»§a thanh cuá»™n."
-
-#~ msgid "E232: Cannot create BalloonEval with both message and callback"
-#~ msgstr ""
-#~ "E232: Không tạo được BalloonEval vá»›i cả thông báo và lá»i gá»i ngược lại"
-
-#~ msgid "E229: Cannot start the GUI"
-#~ msgstr "E229: Không chạy được giao diện đồ há»a GUI"
-
-#~ msgid "E230: Cannot read from \"%s\""
-#~ msgstr "E230: Không Ä‘á»c được từ \"%s\""
-
-#~ msgid "E665: Cannot start GUI, no valid font found"
-#~ msgstr ""
-#~ "E665: Không chạy được giao diện đồ há»a GUI, đưa ra phông chữ không đúng"
-
-#~ msgid "E231: 'guifontwide' invalid"
-#~ msgstr "E231: 'guifontwide' có giá trị không đúng"
-
-#~ msgid "E599: Value of 'imactivatekey' is invalid"
-#~ msgstr "E599: Giá trị của 'imactivatekey' không đúng"
-
-#~ msgid "E254: Cannot allocate color %s"
-#~ msgstr "E254: Không chỉ định được màu %s"
-
-#~ msgid "Vim dialog..."
-#~ msgstr "Hộp thoại Vim..."
-
-#~ msgid "Input _Methods"
-#~ msgstr "Phương pháp _nhập liệu"
-
-#~ msgid "VIM - Search and Replace..."
-#~ msgstr "VIM - Tìm kiếm và thay thế..."
-
-#~ msgid "VIM - Search..."
-#~ msgstr "VIM - Tìm kiếm..."
-
-#~ msgid "Find what:"
-#~ msgstr "Tìm kiếm gì:"
-
-#~ msgid "Replace with:"
-#~ msgstr "Thay thế bởi:"
-
-#~ msgid "Match whole word only"
-#~ msgstr "Chỉ tìm tương ứng hoàn toàn với từ"
-
-#~ msgid "Match case"
-#~ msgstr "Có tính kiểu chữ"
-
-#~ msgid "Direction"
-#~ msgstr "Hướng"
-
-#~ msgid "Up"
-#~ msgstr "Lên"
-
-#~ msgid "Down"
-#~ msgstr "Xuống"
-
-#~ msgid "Find Next"
-#~ msgstr "Tìm tiếp"
-
-#~ msgid "Replace"
-#~ msgstr "Thay thế"
-
-#~ msgid "Replace All"
-#~ msgstr "Thay thế tất cả"
-
-#~ msgid "Vim: Received \"die\" request from session manager\n"
-#~ msgstr "Vim: Nhận được yêu cầu \"chết\" (dừng) từ trình quản lý màn hình\n"
-
-#~ msgid "Vim: Main window unexpectedly destroyed\n"
-#~ msgstr "Vim: Cửa sổ chính đã bị đóng đột ngột\n"
-
-#~ msgid "Font Selection"
-#~ msgstr "Chá»n phông chữ"
-
-#~ msgid "Used CUT_BUFFER0 instead of empty selection"
-#~ msgstr "Sá»­ dụng CUT_BUFFER0 thay cho lá»±a chá»n trống rá»—ng"
-
-#~ msgid "Filter"
-#~ msgstr "Äầu lá»c"
-
-#~ msgid "Directories"
-#~ msgstr "Thư mục"
-
-#~ msgid "Help"
-#~ msgstr "Trợ giúp"
-
-#~ msgid "Files"
-#~ msgstr "Tập tin"
-
-#~ msgid "Selection"
-#~ msgstr "Lá»±a chá»n"
-
-#~ msgid "Undo"
-#~ msgstr "Hủy thao tác"
-
-#~ msgid "E671: Cannot find window title \"%s\""
-#~ msgstr "E671: Không tìm được tiêu đỠcửa sổ \"%s\""
-
-#~ msgid "E243: Argument not supported: \"-%s\"; Use the OLE version."
-#~ msgstr ""
-#~ "E243: Tham số không được hỗ trợ: \"-%s\"; Hãy sử dụng phiên bản OLE."
-
-#~ msgid "E672: Unable to open window inside MDI application"
-#~ msgstr "E672: Không mở được cửa sổ bên trong ứng dụng MDI"
-
-#~ msgid "Find string (use '\\\\' to find a '\\')"
-#~ msgstr "Tìm kiếm chuỗi (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-
-#~ msgid "Find & Replace (use '\\\\' to find a '\\')"
-#~ msgstr "Tìm kiếm và Thay thế (hãy sử dụng '\\\\' để tìm kiếm dấu '\\')"
-
-#~ msgid ""
-#~ "Vim E458: Cannot allocate colormap entry, some colors may be incorrect"
-#~ msgstr ""
-#~ "Vim E458: Không chỉ định được bản ghi trong bảng màu, một vài màu có thể "
-#~ "hiển thị không chính xác"
-
-#~ msgid "E250: Fonts for the following charsets are missing in fontset %s:"
-#~ msgstr "E250: Trong bộ phông chữ %s thiếu phông cho các bảng mã sau:"
-
-#~ msgid "E252: Fontset name: %s"
-#~ msgstr "E252: Bộ phông chữ: %s"
-
-#~ msgid "Font '%s' is not fixed-width"
-#~ msgstr "Phông chữ '%s' không phải là phông có độ rộng cố định (fixed-width)"
-
-#~ msgid "E253: Fontset name: %s\n"
-#~ msgstr "E253: Bộ phông chữ: %s\n"
-
-#~ msgid "Font0: %s\n"
-#~ msgstr "Font0: %s\n"
-
-#~ msgid "Font1: %s\n"
-#~ msgstr "Font1: %s\n"
-
-#~ msgid "Font%<PRId64> width is not twice that of font0\n"
-#~ msgstr ""
-#~ "Chiá»u rá»™ng phông chữ font%<PRId64> phải lá»›n hÆ¡n hai lần so vá»›i chiá»u rá»™ng "
-#~ "font0\n"
-
-#~ msgid "Font0 width: %<PRId64>\n"
-#~ msgstr "Chiá»u rá»™ng font0: %<PRId64>\n"
-
-#~ msgid ""
-#~ "Font1 width: %<PRId64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "Chiá»u rá»™ng font1: %<PRId64>\n"
-#~ "\n"
-
-#~ msgid "E256: Hangul automata ERROR"
-#~ msgstr "E256: LỖI máy tự động Hangual (tiếng Hàn)"
-
-#~ msgid "E563: stat error"
-#~ msgstr "E563: lá»—i stat"
-
-#~ msgid "E625: cannot open cscope database: %s"
-#~ msgstr "E625: không mở được cơ sở dữ liệu cscope: %s"
-
-#~ msgid "E626: cannot get cscope database information"
-#~ msgstr "E626: không lấy được thông tin vỠcơ sở dữ liệu cscope"
-
-#~ msgid "E569: maximum number of cscope connections reached"
-#~ msgstr "E569: đã đạt tới số kết nối lớn nhất cho phép với cscope"
-
-#~ msgid ""
-#~ "E263: Sorry, this command is disabled, the Python library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E263: Rất tiếc câu lệnh này không làm việc, vì thư viện Python chưa được "
-#~ "nạp."
-
-#~ msgid "E659: Cannot invoke Python recursively"
-#~ msgstr "E659: Không thể gá»i Python má»™t cách đệ quy"
-
-#~ msgid "can't delete OutputObject attributes"
-#~ msgstr "Không xóa được thuộc tính OutputObject"
-
-#~ msgid "softspace must be an integer"
-#~ msgstr "giá trị softspace phải là một số nguyên"
-
-#~ msgid "invalid attribute"
-#~ msgstr "thuộc tính không đúng"
-
-#~ msgid "writelines() requires list of strings"
-#~ msgstr "writelines() yêu cầu một danh sách các chuỗi"
-
-#~ msgid "E264: Python: Error initialising I/O objects"
-#~ msgstr "E264: Python: Lỗi khi bắt đầu sử dụng vật thể I/O"
-
-#~ msgid "invalid expression"
-#~ msgstr "biểu thức không đúng"
-
-#~ msgid "expressions disabled at compile time"
-#~ msgstr "biểu thức bị tắt khi biên dịch"
-
-#~ msgid "attempt to refer to deleted buffer"
-#~ msgstr "cố chỉ đến bộ đệm đã bị xóa"
-
-#~ msgid "line number out of range"
-#~ msgstr "số thứ tự của dòng vượt quá giới hạn"
-
-#~ msgid "<buffer object (deleted) at %8lX>"
-#~ msgstr "<vật thể của bộ đệm (bị xóa) tại %8lX>"
-
-#~ msgid "invalid mark name"
-#~ msgstr "tên dấu hiệu không đúng"
-
-#~ msgid "no such buffer"
-#~ msgstr "không có bộ đệm như vậy"
-
-#~ msgid "attempt to refer to deleted window"
-#~ msgstr "cố chỉ đến cửa sổ đã bị đóng"
-
-#~ msgid "readonly attribute"
-#~ msgstr "thuá»™c tính chỉ Ä‘á»c"
-
-#~ msgid "cursor position outside buffer"
-#~ msgstr "vị trí con trỠnằm ngoài bộ đệm"
-
-#~ msgid "<window object (deleted) at %.8lX>"
-#~ msgstr "<vật thể của cửa sổ (bị xóa) tại %.8lX>"
-
-#~ msgid "<window object (unknown) at %.8lX>"
-#~ msgstr "<vật thể của cửa sổ (không rõ) tại %.8lX>"
-
-#~ msgid "<window %d>"
-#~ msgstr "<cửa sổ %d>"
-
-#~ msgid "no such window"
-#~ msgstr "không có cửa sổ như vậy"
-
-#~ msgid "cannot save undo information"
-#~ msgstr "không ghi được thông tin vỠviệc hủy thao tác"
-
-#~ msgid "cannot delete line"
-#~ msgstr "không xóa được dòng"
-
-#~ msgid "cannot replace line"
-#~ msgstr "không thay thế được dòng"
-
-#~ msgid "cannot insert line"
-#~ msgstr "không chèn được dòng"
-
-#~ msgid "string cannot contain newlines"
-#~ msgstr "chuỗi không thể chứa ký tự dòng mới"
-
-#~ msgid ""
-#~ "E266: Sorry, this command is disabled, the Ruby library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E266: Rất tiếc câu lệnh này không làm việc, vì thư viện Ruby chưa đượcnạp."
-
-#~ msgid "E273: unknown longjmp status %d"
-#~ msgstr "E273: không rõ trạng thái của longjmp %d"
-
-#~ msgid "Toggle implementation/definition"
-#~ msgstr "Bật tắt giữa thi hành/định nghĩa"
-
-#~ msgid "Show base class of"
-#~ msgstr "Hiển thị hạng cơ bản của"
-
-#~ msgid "Show overridden member function"
-#~ msgstr "Hiển thị hàm số bị nạp đè lên"
-
-#~ msgid "Retrieve from file"
-#~ msgstr "Nhận từ tập tin"
-
-#~ msgid "Retrieve from project"
-#~ msgstr "Nhận từ dự án"
-
-#~ msgid "Retrieve from all projects"
-#~ msgstr "Nhận từ tất cả các dự án"
-
-#~ msgid "Retrieve"
-#~ msgstr "Nhận"
-
-#~ msgid "Show source of"
-#~ msgstr "Hiển thị mã nguồn"
-
-#~ msgid "Find symbol"
-#~ msgstr "Tìm ký hiệu"
-
-#~ msgid "Browse class"
-#~ msgstr "Duyệt hạng"
-
-#~ msgid "Show class in hierarchy"
-#~ msgstr "Hiển thị hạng trong hệ thống cấp bậc"
-
-#~ msgid "Show class in restricted hierarchy"
-#~ msgstr "Hiển thị hạng trong hệ thống cấp bậc giới hạn"
-
-#~ msgid "Xref refers to"
-#~ msgstr "Xref chỉ đến"
-
-#~ msgid "Xref referred by"
-#~ msgstr "Liên kết đến xref từ"
-
-#~ msgid "Xref has a"
-#~ msgstr "Xref có một"
-
-#~ msgid "Xref used by"
-#~ msgstr "Xref được sử dụng bởi"
-
-#~ msgid "Show docu of"
-#~ msgstr "Hiển thị docu của"
-
-#~ msgid "Generate docu for"
-#~ msgstr "Tạo docu cho"
-
-#~ msgid ""
-#~ "Cannot connect to SNiFF+. Check environment (sniffemacs must be found in "
-#~ "$PATH).\n"
-#~ msgstr ""
-#~ "Không kết nối được tá»›i SNiFF+. Hãy kiểm tra cấu hình môi trưá»ng."
-#~ "(sniffemacs phải được chỉ ra trong biến $PATH).\n"
-
-#~ msgid "E274: Sniff: Error during read. Disconnected"
-#~ msgstr "E274: Sniff: Lá»—i trong thá»i gian Ä‘á»c. Ngắt kết nối"
-
-#~ msgid "SNiFF+ is currently "
-#~ msgstr "Trong thá»i Ä‘iểm hiện nay SNiFF+ "
-
-#~ msgid "not "
-#~ msgstr "không "
-
-#~ msgid "connected"
-#~ msgstr "được kết nối"
-
-#~ msgid "E275: Unknown SNiFF+ request: %s"
-#~ msgstr "E275: không rõ yêu cầu của SNiFF+: %s"
-
-#~ msgid "E276: Error connecting to SNiFF+"
-#~ msgstr "E276: Lỗi kết nối với SNiFF+"
-
-#~ msgid "E278: SNiFF+ not connected"
-#~ msgstr "E278: SNiFF+ chưa được kết nối"
-
-#~ msgid "Sniff: Error during write. Disconnected"
-#~ msgstr "Sniff: Lá»—i trong thá»i gian ghi nhá»›. Ngắt kết nối"
-
-#~ msgid "not implemented yet"
-#~ msgstr "tạm thá»i chưa được thá»±c thi"
-
-#~ msgid "unknown option"
-#~ msgstr "tùy chá»n không rõ"
-
-#~ msgid "cannot set line(s)"
-#~ msgstr "không thể đặt (các) dòng"
-
-#~ msgid "mark not set"
-#~ msgstr "dấu hiệu chưa được đặt"
-
-#~ msgid "row %d column %d"
-#~ msgstr "hàng %d cột %d"
-
-#~ msgid "cannot insert/append line"
-#~ msgstr "không thể chèn hoặc thêm dòng"
-
-#~ msgid "unknown flag: "
-#~ msgstr "cỠkhông biết: "
-
-#~ msgid "unknown vimOption"
-#~ msgstr "không rõ tùy chá»n vimOption"
-
-#~ msgid "keyboard interrupt"
-#~ msgstr "sự gián đoạn của bàn phím"
-
-#~ msgid "vim error"
-#~ msgstr "lá»—i cá»§a vim"
-
-#~ msgid "cannot create buffer/window command: object is being deleted"
-#~ msgstr ""
-#~ "không tạo được câu lệnh của bộ đệm hay của cửa sổ: vật thể đang bị xóa"
-
-#~ msgid ""
-#~ "cannot register callback command: buffer/window is already being deleted"
-#~ msgstr ""
-#~ "không đăng ký được câu lệnh gá»i ngược: bá»™ đệm hoặc cá»­a sổ Ä‘ang bị xóa"
-
-#~ msgid ""
-#~ "E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-"
-#~ "dev@vim.org"
-#~ msgstr ""
-#~ "E280: Lá»–I NẶNG CỦA TCL: bị há»ng danh sách liên kết!? Hãy thông báo việc "
-#~ "nàyđến danh sách thư (mailing list) vim-dev@vim.org"
-
-#~ msgid "cannot register callback command: buffer/window reference not found"
-#~ msgstr ""
-#~ "không đăng ký được câu lệnh gá»i ngược: không tìm thấy liên kết đến bá»™ đệm "
-#~ "hoặc cửa sổ"
-
-#~ msgid ""
-#~ "E571: Sorry, this command is disabled: the Tcl library could not be "
-#~ "loaded."
-#~ msgstr ""
-#~ "E571: Rất tiếc là câu lệnh này không làm việc, vì thư viện Tcl chưa được "
-#~ "nạp"
-
-#~ msgid ""
-#~ "E281: TCL ERROR: exit code is not int!? Please report this to vim-dev@vim."
-#~ "org"
-#~ msgstr ""
-#~ "E281: LỖI TCL: mã thoát ra không phải là một số nguyên!? Hãy thông báo "
-#~ "Ä‘iá»u này đến danh sách thư (mailing list) vim-dev@vim.org"
-
-#~ msgid "cannot get line"
-#~ msgstr "không nhận được dòng"
-
-#~ msgid "Unable to register a command server name"
-#~ msgstr "Không đăng ký được một tên cho máy chủ câu lệnh"
-
-#~ msgid "E248: Failed to send command to the destination program"
-#~ msgstr "E248: Gửi câu lệnh vào chương trình khác không thành công"
-
-#~ msgid "E251: VIM instance registry property is badly formed. Deleted!"
-#~ msgstr "E251: Thuộc tính đăng ký của Vim được định dạng không đúng. Xóa!"
-
-#~ msgid "This Vim was not compiled with the diff feature."
-#~ msgstr "Vim không được biên dịch với tính năng hỗ trợ xem khác biệt (diff)."
-
-#~ msgid "-register\t\tRegister this gvim for OLE"
-#~ msgstr "-register\t\tÄăng ký gvim này cho OLE"
-
-#~ msgid "-unregister\t\tUnregister gvim for OLE"
-#~ msgstr "-unregister\t\tBỠđăng ký gvim này cho OLE"
-
-#~ msgid "-g\t\t\tRun using GUI (like \"gvim\")"
-#~ msgstr "-g\t\t\tSá»­ dụng giao diện đồ há»a GUI (giống \"gvim\")"
-
-#~ msgid "-f or --nofork\tForeground: Don't fork when starting GUI"
-#~ msgstr ""
-#~ "-f hoặc --nofork\tTrong chương trình hoạt động: Không thực hiện fork "
-#~ "khi chạy GUI"
-
-#~ msgid "-V[N]\t\tVerbose level"
-#~ msgstr "-V[N]\t\tMức độ chi tiết của thông báo"
-
-#~ msgid "-f\t\t\tDon't use newcli to open window"
-#~ msgstr "-f\t\t\tKhông sử dụng newcli để mở cửa sổ"
-
-#~ msgid "-dev <device>\t\tUse <device> for I/O"
-#~ msgstr "-dev <thiết bị>\t\tSử dụng <thiết bị> cho I/O"
-
-#~ msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc"
-#~ msgstr "-U <gvimrc>\t\tSá»­ dụng <gvimrc> thay thế cho má»i .gvimrc"
-
-#~ msgid "-x\t\t\tEdit encrypted files"
-#~ msgstr "-x\t\t\tSoạn thảo tập tin đã mã hóa"
-
-#~ msgid "-display <display>\tConnect vim to this particular X-server"
-#~ msgstr "-display <màn hình>\tKết nối vim tới máy chủ X đã chỉ ra"
-
-#~ msgid "-X\t\t\tDo not connect to X server"
-#~ msgstr "-X\t\t\tKhông thực hiện việc kết nối tới máy chủ X"
-
-#~ msgid "--remote <files>\tEdit <files> in a Vim server if possible"
-#~ msgstr "--remote <tập tin>\tSoạn thảo <tập tin> trên máy chủ Vim nếu có thể"
-
-#~ msgid "--remote-silent <files> Same, don't complain if there is no server"
-#~ msgstr ""
-#~ "--remote-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có máy "
-#~ "chá»§"
-
-#~ msgid ""
-#~ "--remote-wait <files> As --remote but wait for files to have been edited"
-#~ msgstr "--remote-wait <tập tin> Cũng như --remote, nhưng chỠsự kết thúc"
-
-#~ msgid ""
-#~ "--remote-wait-silent <files> Same, don't complain if there is no server"
-#~ msgstr ""
-#~ "--remote-wait-silent <tập tin> Cũng vậy, nhưng không kêu ca dù không có "
-#~ "máy chủ"
-
-#~ msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit"
-#~ msgstr "--remote-send <phím>\tGửi <phím> lên máy chủ Vim và thoát"
-
-#~ msgid ""
-#~ "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result"
-#~ msgstr ""
-#~ "--remote-expr <biểu thức>\tTính <biểu thức> trên máy chủ Vim và in ra kết "
-#~ "quả"
-
-#~ msgid "--serverlist\t\tList available Vim server names and exit"
-#~ msgstr "--serverlist\t\tHiển thị danh sách máy chủ Vim và thoát"
-
-#~ msgid "--servername <name>\tSend to/become the Vim server <name>"
-#~ msgstr "--servername <tên>\tGửi lên (hoặc trở thành) máy chủ Vim với <tên>"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (Motif version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản Motif):\n"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (neXtaw version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản neXtaw):\n"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (Athena version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản Athena):\n"
-
-#~ msgid "-display <display>\tRun vim on <display>"
-#~ msgstr "-display <màn hình>\tChạy vim trong <màn hình> đã chỉ ra"
-
-#~ msgid "-iconic\t\tStart vim iconified"
-#~ msgstr "-iconic\t\tChạy vim ở dạng thu nhá»"
-
-#~ msgid "-name <name>\t\tUse resource as if vim was <name>"
-#~ msgstr "-name <tên>\t\tSử dụng tài nguyên giống như khi vim có <tên>"
-
-#~ msgid "\t\t\t (Unimplemented)\n"
-#~ msgstr "\t\t\t (Chưa được thực thi)\n"
-
-#~ msgid "-background <color>\tUse <color> for the background (also: -bg)"
-#~ msgstr "-background <màu>\tSá»­ dụng <màu> chỉ ra cho ná»n (cÅ©ng như: -bg)"
-
-#~ msgid "-foreground <color>\tUse <color> for normal text (also: -fg)"
-#~ msgstr ""
-#~ "-foreground <màu>\tSá»­ dụng <màu> cho văn bản thông thưá»ng (cÅ©ng như: -fg)"
-
-#~ msgid "-font <font>\t\tUse <font> for normal text (also: -fn)"
-#~ msgstr ""
-#~ "-font <phông>\t\tSá»­ dụng <phông> chữ cho văn bản thông thưá»ng (cÅ©ng như: -"
-#~ "fn)"
-
-#~ msgid "-boldfont <font>\tUse <font> for bold text"
-#~ msgstr "-boldfont <phông>\tSử dụng <phông> chữ cho văn bản in đậm"
-
-#~ msgid "-italicfont <font>\tUse <font> for italic text"
-#~ msgstr "-italicfont <phông>\tSử dụng <phông> chữ cho văn bản in nghiêng"
-
-#~ msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)"
-#~ msgstr ""
-#~ "-geometry <kích thước>\tSử dụng <kích thước> ban đầu (cũng như: -geom)"
-
-#~ msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)"
-#~ msgstr ""
-#~ "-borderwidth <rá»™ng>\tSá»­ dụng đưá»ng viá»n có chiá»u <rá»™ng> (cÅ©ng như: -bw)"
-
-#~ msgid ""
-#~ "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)"
-#~ msgstr ""
-#~ "-scrollbarwidth <rá»™ng> Sá»­ dụng thanh cuá»™n vá»›i chiá»u <rá»™ng> (cÅ©ng như: -sw)"
-
-#~ msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)"
-#~ msgstr ""
-#~ "-menuheight <cao>\tSá»­ dụng thanh trình đơn vá»›i chiá»u <cao> (cÅ©ng như: -mh)"
-
-#~ msgid "-reverse\t\tUse reverse video (also: -rv)"
-#~ msgstr "-reverse\t\tSử dụng chế độ video đảo ngược (cũng như: -rv)"
-
-#~ msgid "+reverse\t\tDon't use reverse video (also: +rv)"
-#~ msgstr "+reverse\t\tKhông sử dụng chế độ video đảo ngược (cũng như: +rv)"
-
-#~ msgid "-xrm <resource>\tSet the specified resource"
-#~ msgstr "-xrm <tài nguyên>\tÄặt <tài nguyên> chỉ ra"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (RISC OS version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản RISC OS):\n"
-
-#~ msgid "--columns <number>\tInitial width of window in columns"
-#~ msgstr "--columns <số>\tChiá»u rá»™ng ban đầu cá»§a cá»­a sổ tính theo số cá»™t"
-
-#~ msgid "--rows <number>\tInitial height of window in rows"
-#~ msgstr "--rows <số>\tChiá»u cao ban đầu cá»§a cá»­a sổ tính theo số dòng"
-
-#~ msgid ""
-#~ "\n"
-#~ "Arguments recognised by gvim (GTK+ version):\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Tham số cho gvim (phiên bản GTK+):\n"
-
-#~ msgid "-display <display>\tRun vim on <display> (also: --display)"
-#~ msgstr ""
-#~ "-display <màn hình>\tChạy vim trên <màn hình> chỉ ra (cũng như: --display)"
-
-#~ msgid "--role <role>\tSet a unique role to identify the main window"
-#~ msgstr "--role <vai trò>\tÄặt <vai trò> duy nhất để nhận diện cá»­a sổ chính"
-
-#~ msgid "--socketid <xid>\tOpen Vim inside another GTK widget"
-#~ msgstr "--socketid <xid>\tMở Vim bên trong thành phần GTK khác"
-
-#~ msgid "-P <parent title>\tOpen Vim inside parent application"
-#~ msgstr "-P <tiêu đỠcủa mẹ>\tMở Vim bên trong ứng dụng mẹ"
-
-#~ msgid "No display"
-#~ msgstr "Không có màn hình"
-
-#~ msgid ": Send failed.\n"
-#~ msgstr ": Gửi không thành công.\n"
-
-#~ msgid ": Send failed. Trying to execute locally\n"
-#~ msgstr ": Gửi không thành công. Thử thực hiện nội bộ\n"
-
-#~ msgid "%d of %d edited"
-#~ msgstr "đã soạn thảo %d từ %d"
-
-#~ msgid "No display: Send expression failed.\n"
-#~ msgstr "Không có màn hình: gửi biểu thức không thành công.\n"
-
-#~ msgid ": Send expression failed.\n"
-#~ msgstr ": Gửi biểu thức không thành công.\n"
-
-#~ msgid "E543: Not a valid codepage"
-#~ msgstr "E543: Bảng mã không cho phép"
-
-#~ msgid "E285: Failed to create input context"
-#~ msgstr "E285: Không tạo được nội dung nhập vào"
-
-#~ msgid "E286: Failed to open input method"
-#~ msgstr "E286: Việc thử mở phương pháp nhập không thành công"
-
-#~ msgid "E287: Warning: Could not set destroy callback to IM"
-#~ msgstr ""
-#~ "E287: Cảnh báo: không đặt được sá»± gá»i ngược há»§y diệt thành phương pháp "
-#~ "nhập"
-
-#~ msgid "E288: input method doesn't support any style"
-#~ msgstr "E288: phương pháp nhập không hỗ trợ bất kỳ phong cách (style) nào"
-
-#~ msgid "E289: input method doesn't support my preedit type"
-#~ msgstr "E289: phương pháp nhập không hỗ trợ loại soạn thảo trước của Vim"
-
-#~ msgid "E290: over-the-spot style requires fontset"
-#~ msgstr "E290: phong cách over-the-spot yêu cầu một bộ phông chữ"
-
-#~ msgid "E291: Your GTK+ is older than 1.2.3. Status area disabled"
-#~ msgstr "E291: GTK+ cũ hơn 1.2.3. Vùng chỉ trạng thái không làm việc"
-
-#~ msgid "E292: Input Method Server is not running"
-#~ msgstr "E292: Máy chủ phương pháp nhập liệu chưa được chạy"
-
-#~ msgid ""
-#~ "\n"
-#~ " [not usable with this version of Vim]"
-#~ msgstr ""
-#~ "\n"
-#~ " [không sử dụng được với phiên bản này của Vim]"
-
-#~ msgid ""
-#~ "&Open Read-Only\n"
-#~ "&Edit anyway\n"
-#~ "&Recover\n"
-#~ "&Quit\n"
-#~ "&Abort\n"
-#~ "&Delete it"
-#~ msgstr ""
-#~ "&O Mở chỉ để Ä‘á»c\n"
-#~ "&E Vẫn soạn thảo\n"
-#~ "&R Phục hồi\n"
-#~ "&Q Thoát\n"
-#~ "&A Gián đoạn&D Xóa nó"
-
-#~ msgid "Tear off this menu"
-#~ msgstr "Chia cắt trình đơn này"
-
-#~ msgid "[string too long]"
-#~ msgstr "[chuỗi quá dài]"
-
-#~ msgid "Hit ENTER to continue"
-#~ msgstr "Nhấn phím ENTER để tiếp tục"
-
-#~ msgid " (RET/BS: line, SPACE/b: page, d/u: half page, q: quit)"
-#~ msgstr " (RET/BS: dòng, SPACE/b: trang, d/u: nửa trang, q: thoát)"
-
-#~ msgid " (RET: line, SPACE: page, d: half page, q: quit)"
-#~ msgstr " (RET: dòng, SPACE: trang, d: nửa trang, q: thoát)"
-
-#~ msgid "Save File dialog"
-#~ msgstr "Ghi nhớ tập tin"
-
-#~ msgid "Open File dialog"
-#~ msgstr "Mở tập tin"
-
-#~ msgid "E338: Sorry, no file browser in console mode"
-#~ msgstr ""
-#~ "E338: Xin lỗi nhưng không có trình duyệt tập tin trong chế độ kênh giao "
-#~ "tác (console)"
-
-#~ msgid "Vim: preserving files...\n"
-#~ msgstr "Vim: ghi nhớ các tập tin...\n"
-
-#~ msgid "Vim: Finished.\n"
-#~ msgstr "Vim: Äã xong.\n"
-
-#~ msgid "ERROR: "
-#~ msgstr "Lá»–I: "
-
-#~ msgid ""
-#~ "\n"
-#~ "[bytes] total alloc-freed %<PRIu64>-%<PRIu64>, in use %<PRIu64>, peak use "
-#~ "%<PRIu64>\n"
-#~ msgstr ""
-#~ "\n"
-#~ "[byte] tổng phân phối-còn trống %<PRIu64>-%<PRIu64>, sử dụng %<PRIu64>, "
-#~ "píc sử dụng %<PRIu64>\n"
-
-#~ msgid ""
-#~ "[calls] total re/malloc()'s %<PRIu64>, total free()'s %<PRIu64>\n"
-#~ "\n"
-#~ msgstr ""
-#~ "[gá»i] tổng re/malloc() %<PRIu64>, tổng free() %<PRIu64>\n"
-#~ "\n"
-
-#~ msgid "E340: Line is becoming too long"
-#~ msgstr "E340: Dòng đang trở thành quá dài"
-
-#~ msgid "E341: Internal error: lalloc(%<PRId64>, )"
-#~ msgstr "E341: Lá»—i ná»™i bá»™: lalloc(%<PRId64>, )"
+#, c-format
+msgid "E447: Can't find file \"%s\" in path"
+msgstr "E447: Không tìm thấy tập tin \"%s\" trong đưá»ng dẫn"
-#~ msgid "E547: Illegal mouseshape"
-#~ msgstr "E547: Dạng trỠchuột không cho phép"
+#, c-format
+msgid "E370: Could not load library %s"
+msgstr "E370: Không nạp được thư viện %s"
-#~ msgid "Enter encryption key: "
-#~ msgstr "Nhập mật khẩu để mã hóa: "
+msgid "Sorry, this command is disabled: the Perl library could not be loaded."
+msgstr "Xin lỗi, câu lệnh này bị tắt: không nạp được thư viện Perl."
-#~ msgid "Enter same key again: "
-#~ msgstr " Nhập lại mật khẩu:"
+msgid "E299: Perl evaluation forbidden in sandbox without the Safe module"
+msgstr ""
+"E299: Không cho phép sự tính toán Perl trong hộp cát mà không có môđun An "
+"toàn"
-#~ msgid "Keys don't match!"
-#~ msgstr "Hai mật khẩu không trùng nhau!"
+msgid "Edit with &multiple Vims"
+msgstr "Soạn thảo trong nhiá»u Vi&m"
-#~ msgid "Cannot connect to Netbeans #2"
-#~ msgstr "Không kết nối được với Netbeans #2"
+msgid "Edit with single &Vim"
+msgstr "Soạn thảo trong một &Vim"
-#~ msgid "Cannot connect to Netbeans"
-#~ msgstr "Không kết nối được với NetBeans"
+msgid "&Diff with Vim"
+msgstr "&So sánh (diff) qua Vim"
-#~ msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\""
-#~ msgstr ""
-#~ "E668: Chế độ truy cập thông tin vỠliên kết với NetBeans không đúng: \"%s"
-#~ "\""
+msgid "Edit with &Vim"
+msgstr "Soạn thảo trong &Vim"
-#~ msgid "read from Netbeans socket"
-#~ msgstr "Ä‘á»c từ socket NetBeans"
+msgid "Edit with existing Vim - &"
+msgstr "Soạn thảo trong Vim đã chạy - &"
-#~ msgid "E658: NetBeans connection lost for buffer %<PRId64>"
-#~ msgstr "E658: Bị mất liên kết với NetBeans cho bộ đệm %<PRId64>"
+msgid "Edits the selected file(s) with Vim"
+msgstr "Soạn thảo (các) tập tin đã chá»n trong Vim"
-#~ msgid "freeing %<PRId64> lines"
-#~ msgstr "đã làm sạch %<PRId64> dòng"
+msgid "Error creating process: Check if gvim is in your path!"
+msgstr "Lá»—i tạo tiến trình: Hãy kiểm tra xem gvim có trong đưá»ng dẫn không!"
-#~ msgid "E530: Cannot change term in GUI"
-#~ msgstr "E530: Không thể thay đổi terminal trong giao diện đồ há»a GUI"
+msgid "gvimext.dll error"
+msgstr "lá»—i gvimext.dll"
-#~ msgid "E531: Use \":gui\" to start the GUI"
-#~ msgstr "E531: Hãy sá»­ dụng \":gui\" để chạy giao diện đồ há»a GUI"
+msgid "Path length too long!"
+msgstr "ÄÆ°á»ng dẫn quá dài!"
-#~ msgid "E617: Cannot be changed in the GTK+ 2 GUI"
-#~ msgstr "E617: Không thể thay đổi trong giao diện đồ há»a GTK+ 2"
+msgid "--No lines in buffer--"
+msgstr "-- Không có dòng nào trong bộ đệm --"
-#~ msgid "E597: can't select fontset"
-#~ msgstr "E597: không chá»n được bá»™ phông chữ"
+msgid "E470: Command aborted"
+msgstr "E470: Câu lệnh bị dừng"
-#~ msgid "E598: Invalid fontset"
-#~ msgstr "E598: Bộ phông chữ không đúng"
+msgid "E471: Argument required"
+msgstr "E471: Cần chỉ ra tham số"
-#~ msgid "E533: can't select wide font"
-#~ msgstr "E533: không chá»n được phông chữ vá»›i các ký tá»± có chiá»u rá»™ng gấp đôi"
+msgid "E10: \\ should be followed by /, ? or &"
+msgstr "E10: Sau \\ phải là các ký tự /, ? hoặc &"
-#~ msgid "E534: Invalid wide font"
-#~ msgstr "E534: Phông chữ, vá»›i ký tá»± có chiá»u rá»™ng gấp đôi, không đúng"
+msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits"
+msgstr "E11: Lỗi trong cửa sổ dòng lệnh; <CR> thực hiện, CTRL-C thoát"
-#~ msgid "E538: No mouse support"
-#~ msgstr "E538: Chuột không được hỗ trợ"
+msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search"
+msgstr ""
+"E12: Câu lệnh không cho phép từ exrc/vimrc trong thư mục hiện thá»i hoặc "
+"trong tìm kiếm thẻ ghi"
-#~ msgid "cannot open "
-#~ msgstr "không mở được "
+msgid "E171: Missing :endif"
+msgstr "E171: Thiếu câu lệnh :endif"
-#~ msgid "VIM: Can't open window!\n"
-#~ msgstr "VIM: Không mở được cửa sổ!\n"
+msgid "E600: Missing :endtry"
+msgstr "E600: Thiếu câu lệnh :endtry"
-#~ msgid "Need Amigados version 2.04 or later\n"
-#~ msgstr "Cần Amigados phiên bản 2.04 hoặc mới hơn\n"
+msgid "E170: Missing :endwhile"
+msgstr "E170: Thiếu câu lệnh :endwhile"
-#~ msgid "Need %s version %<PRId64>\n"
-#~ msgstr "Cần %s phiên bản %<PRId64>\n"
+msgid "E588: :endwhile without :while"
+msgstr "E588: Câu lệnh :endwhile không có lệnh :while (1 cặp)"
-#~ msgid "Cannot open NIL:\n"
-#~ msgstr "Không mở được NIL:\n"
+msgid "E13: File exists (add ! to override)"
+msgstr "E13: Tập tin đã tồn tại (thêm ! để ghi chèn)"
-#~ msgid "Cannot create "
-#~ msgstr "Không tạo được "
+msgid "E472: Command failed"
+msgstr "E472: Không thực hiện thành công câu lệnh"
-#~ msgid "Vim exiting with %d\n"
-#~ msgstr "Thoát Vim với mã %d\n"
+#, c-format
+msgid "E234: Unknown fontset: %s"
+msgstr "E234: Không rõ bộ phông chữ: %s"
-#~ msgid "cannot change console mode ?!\n"
-#~ msgstr "không thay đổi được chế độ kênh giao tác (console)?!\n"
+#, c-format
+msgid "E235: Unknown font: %s"
+msgstr "E235: Không rõ phông chữ: %s"
-#~ msgid "mch_get_shellsize: not a console??\n"
-#~ msgstr "mch_get_shellsize: không phải là kênh giao tác (console)??\n"
+#, c-format
+msgid "E236: Font \"%s\" is not fixed-width"
+msgstr "E236: Phông chữ \"%s\" không có độ rộng cố định (fixed-width)"
-#~ msgid "Cannot execute "
-#~ msgstr "Không chạy được "
+msgid "E473: Internal error"
+msgstr "E473: Lá»—i ná»™i bá»™"
-#~ msgid "shell "
-#~ msgstr "shell "
+msgid "Interrupted"
+msgstr "Bị gián đoạn"
-#~ msgid " returned\n"
-#~ msgstr " thoát\n"
+msgid "E14: Invalid address"
+msgstr "E14: Äịa chỉ không cho phép"
-#~ msgid "ANCHOR_BUF_SIZE too small."
-#~ msgstr "Giá trị ANCHOR_BUF_SIZE quá nhá»."
+msgid "E474: Invalid argument"
+msgstr "E474: Tham số không cho phép"
-#~ msgid "I/O ERROR"
-#~ msgstr "LỖI I/O (NHẬP/XUẤT)"
+#, c-format
+msgid "E475: Invalid argument: %s"
+msgstr "E475: Tham số không cho phép: %s"
-#~ msgid "...(truncated)"
-#~ msgstr "...(bị cắt bớt)"
+#, c-format
+msgid "E15: Invalid expression: %s"
+msgstr "E15: Biểu thức không cho phép: %s"
-#~ msgid "'columns' is not 80, cannot execute external commands"
-#~ msgstr ""
-#~ "Tùy chá»n 'columns' khác 80, chương trình ngoại trú không thể thá»±c hiện"
+msgid "E16: Invalid range"
+msgstr "E16: Vùng không cho phép"
-#~ msgid "to %s on %s"
-#~ msgstr "tới %s trên %s"
+msgid "E476: Invalid command"
+msgstr "E476: Câu lệnh không cho phép"
-#~ msgid "E613: Unknown printer font: %s"
-#~ msgstr "E613: Không rõ phông chữ của máy in: %s"
+#, c-format
+msgid "E17: \"%s\" is a directory"
+msgstr "E17: \"%s\" là mộ thư mục"
-#~ msgid "E238: Print error: %s"
-#~ msgstr "E238: Lá»—i in: %s"
+msgid "E18: Unexpected characters before '='"
+msgstr "E18: Ở trước '=' có các ký tự không mong đợi"
-#~ msgid "Printing '%s'"
-#~ msgstr "Äang in '%s'"
+#, c-format
+msgid "E364: Library call failed for \"%s()\""
+msgstr "E364: Gá»i hàm số \"%s()\" cá»§a thư viện không thành công"
-#~ msgid "E244: Illegal charset name \"%s\" in font name \"%s\""
-#~ msgstr "E244: Tên bảng mã không cho phép \"%s\" trong tên phông chữ \"%s\""
+#, c-format
+msgid "E448: Could not load library function %s"
+msgstr "E448: Nạp hàm số %s của thư viện không thành công"
-#~ msgid "E245: Illegal char '%c' in font name \"%s\""
-#~ msgstr "E245: Ký tự không cho phép '%c' trong tên phông chữ \"%s\""
+msgid "E19: Mark has invalid line number"
+msgstr "E19: Dấu hiệu chỉ đến một số thứ tự dòng không đúng"
-#~ msgid "Vim: Double signal, exiting\n"
-#~ msgstr "Vim: Tín hiệu đôi, thoát\n"
+msgid "E20: Mark not set"
+msgstr "E20: Dấu hiệu không được xác định"
-#~ msgid "Vim: Caught deadly signal %s\n"
-#~ msgstr "Vim: Nhận được tín hiệu chết %s\n"
+msgid "E21: Cannot make changes, 'modifiable' is off"
+msgstr "E21: Không thể thay đổi, vì tùy chá»n 'modifiable' bị tắt"
-#~ msgid "Vim: Caught deadly signal\n"
-#~ msgstr "Vim: Nhận được tín hiệu chết\n"
+msgid "E22: Scripts nested too deep"
+msgstr "E22: Các script lồng vào nhau quá sâu"
-#~ msgid "Opening the X display took %<PRId64> msec"
-#~ msgstr "Mở màn hình X mất %<PRId64> mili giây"
+msgid "E23: No alternate file"
+msgstr "E23: Không có tập tin xen kẽ"
-#~ msgid ""
-#~ "\n"
-#~ "Vim: Got X error\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Vim: Lá»—i X\n"
+msgid "E24: No such abbreviation"
+msgstr "E24: Không có chữ viết tắt như vậy"
-#~ msgid "Testing the X display failed"
-#~ msgstr "Kiểm tra màn hình X không thành công"
+msgid "E477: No ! allowed"
+msgstr "E477: Không cho phép !"
-#~ msgid "Opening the X display timed out"
-#~ msgstr "Không mở được màn hình X trong thá»i gian cho phép (time out)"
+msgid "E25: GUI cannot be used: Not enabled at compile time"
+msgstr "E25: Không sá»­ dụng được giao diện đồ há»a vì không chá»n khi biên dịch"
-#~ msgid ""
-#~ "\n"
-#~ "Cannot execute shell sh\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Không chạy được shell sh\n"
+msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
+msgstr "E26: Tiếng Do thái không được chá»n khi biên dịch\n"
-#~ msgid ""
-#~ "\n"
-#~ "Cannot create pipes\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Không tạo được đưá»ng ống (pipe)\n"
+msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
+msgstr "E27: Tiếng Farsi không được chá»n khi biên dịch\n"
-#~ msgid ""
-#~ "\n"
-#~ "Cannot fork\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Không thực hiện được fork()\n"
+msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
+msgstr "E800: Tiếng Ả Rập không được chá»n khi biên dịch\n"
-#~ msgid ""
-#~ "\n"
-#~ "Command terminated\n"
-#~ msgstr ""
-#~ "\n"
-#~ "Câu lệnh bị gián đoạn\n"
+#, c-format
+msgid "E28: No such highlight group name: %s"
+msgstr "E28: Nhóm chiếu sáng cú pháp %s không tồn tại"
-#~ msgid "XSMP lost ICE connection"
-#~ msgstr "XSMP mất kết nối ICE"
+msgid "E29: No inserted text yet"
+msgstr "E29: Tạm thá»i chưa có văn bản được chèn"
-#~ msgid "Opening the X display failed"
-#~ msgstr "Mở màn hình X không thành công"
+msgid "E30: No previous command line"
+msgstr "E30: Không có dòng lệnh trước"
-#~ msgid "XSMP handling save-yourself request"
-#~ msgstr "XSMP xử lý yêu cầu tự động ghi nhớ"
+msgid "E31: No such mapping"
+msgstr "E31: Không có ánh xạ (mapping) như vậy"
-#~ msgid "XSMP opening connection"
-#~ msgstr "XSMP mở kết nối"
+msgid "E479: No match"
+msgstr "E479: Không có tương ứng"
-#~ msgid "XSMP ICE connection watch failed"
-#~ msgstr "XSMP mất theo dõi kết nối ICE"
+#, c-format
+msgid "E480: No match: %s"
+msgstr "E480: Không có tương ứng: %s"
-#~ msgid "XSMP SmcOpenConnection failed: %s"
-#~ msgstr "XSMP thực hiện SmcOpenConnection không thành công: %s"
+msgid "E32: No file name"
+msgstr "E32: Không có tên tập tin"
-#~ msgid "At line"
-#~ msgstr "Tại dòng"
+msgid "E33: No previous substitute regular expression"
+msgstr "E33: Không có biểu thức chính quy trước để thay thế"
-#~ msgid "Could not allocate memory for command line."
-#~ msgstr "Không phân chia được bộ nhớ cho dòng lệnh."
+msgid "E34: No previous command"
+msgstr "E34: Không có câu lệnh trước"
-#~ msgid "VIM Error"
-#~ msgstr "Lá»—i VIM"
+msgid "E35: No previous regular expression"
+msgstr "E35: Không có biểu thức chính quy trước"
-#~ msgid "Could not load vim32.dll!"
-#~ msgstr "Không nạp được vim32.dll!"
+msgid "E481: No range allowed"
+msgstr "E481: Không cho phép sử dụng phạm vi"
-#~ msgid "Could not fix up function pointers to the DLL!"
-#~ msgstr "Không sửa được cái chỉ (pointer) hàm số tới DLL!"
+msgid "E36: Not enough room"
+msgstr "E36: Không đủ chỗ trống"
-#~ msgid "shell returned %d"
-#~ msgstr "thoát shell với mã %d"
+# TODO: Capitalise first word of message?
+msgid "E247: No registered server named \"%s\""
+msgstr "E247: máy chủ \"%s\" chưa đăng ký"
-#~ msgid "Vim: Caught %s event\n"
-#~ msgstr "Vim: Nhận được sự kiện %s\n"
+#, c-format
+msgid "E482: Can't create file %s"
+msgstr "E482: Không tạo được tập tin %s"
-#~ msgid "close"
-#~ msgstr "đóng"
+msgid "E483: Can't get temp file name"
+msgstr "E483: Không nhận được tên tập tin tạm thá»i (temp)"
-#~ msgid "logoff"
-#~ msgstr "thoát"
-
-#~ msgid "shutdown"
-#~ msgstr "tắt máy"
-
-#~ msgid "E371: Command not found"
-#~ msgstr "E371: Câu lệnh không tìm thấy"
-
-#~ msgid ""
-#~ "VIMRUN.EXE not found in your $PATH.\n"
-#~ "External commands will not pause after completion.\n"
-#~ "See :help win32-vimrun for more information."
-#~ msgstr ""
-#~ "Không tìm thấy VIMRUN.EXE trong $PATH.\n"
-#~ "Lệnh ngoại trú sẽ không dừng lại sau khi hoàn thành.\n"
-#~ "Thông tin chi tiết xem trong :help win32-vimrun"
-
-#~ msgid "Vim Warning"
-#~ msgstr "Cảnh báo Vim"
-
-#~ msgid "E56: %s* operand could be empty"
-#~ msgstr "E56: operand %s* không thể rỗng"
-
-#~ msgid "E57: %s+ operand could be empty"
-#~ msgstr "E57: operand %s+ không thể rỗng"
-
-#~ msgid "E58: %s{ operand could be empty"
-#~ msgstr "E58: operand %s{ không thể rỗng"
-
-#~ msgid "E361: Crash intercepted; regexp too complex?"
-#~ msgstr "E361: Sự cố được ngăn chặn; biểu thức chính quy quá phức tạp?"
-
-#~ msgid "E363: pattern caused out-of-stack error"
-#~ msgstr "E363: sử dụng mẫu (pattern) gây ra lỗi out-of-stack"
-
-#~ msgid "E396: containedin argument not accepted here"
-#~ msgstr "E396: không được sử dụng tham số containedin ở đây"
-
-#~ msgid "Enter nr of choice (<CR> to abort): "
-#~ msgstr "Hãy chá»n số cần thiết (<CR> để dừng):"
-
-#~ msgid "E430: Tag file path truncated for %s\n"
-#~ msgstr "E430: ÄÆ°á»ng dẫn tá»›i tập tin thẻ ghi bị cắt bá»›t cho %s\n"
-
-#~ msgid "new shell started\n"
-#~ msgstr "đã chạy shell mới\n"
-
-#~ msgid "No undo possible; continue anyway"
-#~ msgstr "Không thể hủy thao tác; tiếp tục thực hiện"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 16/32-bit GUI version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản vá»›i giao diện đồ há»a GUI cho MS-Windows 16/32 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 32-bit GUI version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản vá»›i giao diện đồ há»a GUI cho MS-Windows 32 bit"
-
-#~ msgid " in Win32s mode"
-#~ msgstr " trong chế độ Win32"
-
-#~ msgid " with OLE support"
-#~ msgstr " với hỗ trợ OLE"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 32-bit console version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản console cho MS-Windows 32 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "MS-Windows 16-bit version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MS-Windows 16 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "32-bit MS-DOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MS-DOS 32 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "16-bit MS-DOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MS-DOS 16 bit"
-
-#~ msgid ""
-#~ "\n"
-#~ "MacOS X (unix) version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MacOS X (unix)"
-
-#~ msgid ""
-#~ "\n"
-#~ "MacOS X version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MacOS X"
-
-#~ msgid ""
-#~ "\n"
-#~ "MacOS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho MacOS"
-
-#~ msgid ""
-#~ "\n"
-#~ "RISC OS version"
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản cho RISC OS"
-
-#~ msgid ""
-#~ "\n"
-#~ "Big version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản lớn "
-
-#~ msgid ""
-#~ "\n"
-#~ "Normal version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản thông thưá»ng "
-
-#~ msgid ""
-#~ "\n"
-#~ "Small version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản nhỠ"
-
-#~ msgid ""
-#~ "\n"
-#~ "Tiny version "
-#~ msgstr ""
-#~ "\n"
-#~ "Phiên bản \"tí hon\" "
-
-#~ msgid "with GTK2-GNOME GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI GTK2-GNOME."
-
-#~ msgid "with GTK-GNOME GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI GTK-GNOME."
-
-#~ msgid "with GTK2 GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI GTK2."
-
-#~ msgid "with GTK GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI GTK."
-
-#~ msgid "with X11-Motif GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI X11-Motif."
-
-#~ msgid "with X11-neXtaw GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI X11-neXtaw."
-
-#~ msgid "with X11-Athena GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI X11-Athena."
+#, c-format
+msgid "E484: Can't open file %s"
+msgstr "E484: Không mở được tập tin %s"
-#~ msgid "with BeOS GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI BeOS."
+#, c-format
+msgid "E485: Can't read file %s"
+msgstr "E485: Không Ä‘á»c được tập tin %s"
-#~ msgid "with Photon GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI Photon."
+msgid "E37: No write since last change (add ! to override)"
+msgstr "E37: Thay đổi chưa được ghi nhớ (thêm ! để bỠqua ghi nhớ)"
-#~ msgid "with GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI."
+msgid "E38: Null argument"
+msgstr "E38: Tham sô bằng 0"
-#~ msgid "with Carbon GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI Carbon."
+msgid "E39: Number expected"
+msgstr "E39: Yêu cầu một số"
-#~ msgid "with Cocoa GUI."
-#~ msgstr "vá»›i giao diện đồ há»a GUI Cocoa."
+#, c-format
+msgid "E40: Can't open errorfile %s"
+msgstr "E40: Không mở được tập tin lỗi %s"
-#~ msgid "with (classic) GUI."
-#~ msgstr "vá»›i giao diện đồ há»a (cổ Ä‘iển) GUI."
+# TODO: Capitalise first word of message?
+msgid "E233: Cannot open display"
+msgstr "E233: không mở được màn hình"
-#~ msgid " system gvimrc file: \""
-#~ msgstr " tập tin gvimrc chung cho hệ thống: \""
+msgid "E41: Out of memory!"
+msgstr "E41: Không đủ bộ nhớ!"
-#~ msgid " user gvimrc file: \""
-#~ msgstr " tập tin gvimrc cá»§a ngưá»i dùng: \""
+msgid "Pattern not found"
+msgstr "Không tìm thấy mẫu (pattern)"
-#~ msgid "2nd user gvimrc file: \""
-#~ msgstr " tập tin gvimrc thứ hai cá»§a ngưá»i dùng: \""
+#, c-format
+msgid "E486: Pattern not found: %s"
+msgstr "E486: Không tìm thấy mẫu (pattern): %s"
-#~ msgid "3rd user gvimrc file: \""
-#~ msgstr " tập tin gvimrc thứ ba cá»§a ngưá»i dùng: \""
+msgid "E487: Argument must be positive"
+msgstr "E487: Tham số phải là một số dương"
-#~ msgid " system menu file: \""
-#~ msgstr " tập tin trình đơn chung cho hệ thống: \""
+msgid "E459: Cannot go back to previous directory"
+msgstr "E459: Không quay lại được thư mục trước đó"
-#~ msgid "Compiler: "
-#~ msgstr "Trình biên dịch: "
+msgid "E42: No Errors"
+msgstr "E42: Không có lỗi"
-#~ msgid "menu Help->Orphans for information "
-#~ msgstr "trình đơn Trợ giúp->Mồ côi để có thêm thông tin "
+msgid "E43: Damaged match string"
+msgstr "E43: Chuá»—i tương ứng bị há»ng"
-#~ msgid "Running modeless, typed text is inserted"
-#~ msgstr "Không chế độ, văn bản nhập vào sẽ được chèn"
+msgid "E44: Corrupted regexp program"
+msgstr "E44: Chương trình xá»­ lý biểu thức chính quy bị há»ng"
-#~ msgid "menu Edit->Global Settings->Toggle Insert Mode "
-#~ msgstr ""
-#~ "trình đơn Soạn thảo->Thiết lập chung->Chế độ chèn "
+msgid "E45: 'readonly' option is set (add ! to override)"
+msgstr "E45: Tùy chá»n 'readonly' được bật (Hãy thêm ! để lá» Ä‘i)"
-#~ msgid " for two modes "
-#~ msgstr " cho hai chế độ "
+#, c-format
+msgid "E46: Cannot set read-only variable \"%s\""
+msgstr "E46: Không thay đổi được biến chỉ Ä‘á»c \"%s\""
-#~ msgid "menu Edit->Global Settings->Toggle Vi Compatible"
-#~ msgstr ""
-#~ "trình đơn Soạn thảo->Thiết lập chung->Tương thích với Vi "
+msgid "E47: Error while reading errorfile"
+msgstr "E47: Lá»—i khi Ä‘á»c tập tin lá»—i"
-#~ msgid " for Vim defaults "
-#~ msgstr ""
-#~ " để chuyển vào chế độ Vim mặc định "
+msgid "E48: Not allowed in sandbox"
+msgstr "E48: Không cho phép trong hộp cát (sandbox)"
-#~ msgid "WARNING: Windows 95/98/ME detected"
-#~ msgstr "CẢNH BÃO: nhận ra Windows 95/98/ME"
+msgid "E523: Not allowed here"
+msgstr "E523: Không cho phép ở đây"
-#~ msgid "type :help windows95<Enter> for info on this"
-#~ msgstr "hãy gõ :help windows95<Enter> để biết thêm thông tin "
+msgid "E359: Screen mode setting not supported"
+msgstr "E359: Chế độ màn hình không được hỗ trợ"
-#~ msgid "E370: Could not load library %s"
-#~ msgstr "E370: Không nạp được thư viện %s"
+msgid "E49: Invalid scroll size"
+msgstr "E49: Kích thước thanh cuộn không cho phép"
-#~ msgid ""
-#~ "Sorry, this command is disabled: the Perl library could not be loaded."
-#~ msgstr "Xin lỗi, câu lệnh này bị tắt: không nạp được thư viện Perl."
+msgid "E91: 'shell' option is empty"
+msgstr "E91: Tùy chá»n 'shell' là má»™t chuá»—i rá»—ng"
-#~ msgid "E299: Perl evaluation forbidden in sandbox without the Safe module"
-#~ msgstr ""
-#~ "E299: Không cho phép sự tính toán Perl trong hộp cát mà không có môđun An "
-#~ "toàn"
+msgid "E255: Couldn't read in sign data!"
+msgstr "E255: Không Ä‘á»c được dữ liệu vá» ký tá»±!"
-#~ msgid "Edit with &multiple Vims"
-#~ msgstr "Soạn thảo trong nhiá»u Vi&m"
+msgid "E72: Close error on swap file"
+msgstr "E72: Lỗi đóng tập tin trao đổi (swap)"
-#~ msgid "Edit with single &Vim"
-#~ msgstr "Soạn thảo trong một &Vim"
+# TODO: Capitalise first word of message?
+msgid "E73: Tag stack empty"
+msgstr "E73: đống thẻ ghi rỗng"
-#~ msgid "&Diff with Vim"
-#~ msgstr "&So sánh (diff) qua Vim"
+msgid "E74: Command too complex"
+msgstr "E74: Câu lệnh quá phức tạp"
-#~ msgid "Edit with &Vim"
-#~ msgstr "Soạn thảo trong &Vim"
+msgid "E75: Name too long"
+msgstr "E75: Tên quá dài"
-#~ msgid "Edit with existing Vim - &"
-#~ msgstr "Soạn thảo trong Vim đã chạy - &"
+msgid "E76: Too many ["
+msgstr "E76: Quá nhiá»u ký tá»± ["
-#~ msgid "Edits the selected file(s) with Vim"
-#~ msgstr "Soạn thảo (các) tập tin đã chá»n trong Vim"
+msgid "E77: Too many file names"
+msgstr "E77: Quá nhiá»u tên tập tin"
-#~ msgid "Error creating process: Check if gvim is in your path!"
-#~ msgstr "Lá»—i tạo tiến trình: Hãy kiểm tra xem gvim có trong đưá»ng dẫn không!"
+msgid "E488: Trailing characters"
+msgstr "E488: Ký tự thừa ở đuôi"
-#~ msgid "gvimext.dll error"
-#~ msgstr "lá»—i gvimext.dll"
+msgid "E78: Unknown mark"
+msgstr "E78: Dấu hiệu không biết"
-#~ msgid "Path length too long!"
-#~ msgstr "ÄÆ°á»ng dẫn quá dài!"
+msgid "E79: Cannot expand wildcards"
+msgstr "E79: Không thực hiện được phép thế theo wildcard"
-#~ msgid "E234: Unknown fontset: %s"
-#~ msgstr "E234: Không rõ bộ phông chữ: %s"
+msgid "E591: 'winheight' cannot be smaller than 'winminheight'"
+msgstr "E591: giá trị của 'winheight' không thể nhỠhơn 'winminheight'"
-#~ msgid "E235: Unknown font: %s"
-#~ msgstr "E235: Không rõ phông chữ: %s"
+msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'"
+msgstr "E592: giá trị của 'winwidth' không thể nhỠhơn 'winminwidth'"
-#~ msgid "E448: Could not load library function %s"
-#~ msgstr "E448: Nạp hàm số %s của thư viện không thành công"
+msgid "E80: Error while writing"
+msgstr "E80: Lá»—i khi ghi nhá»›"
-#~ msgid "E26: Hebrew cannot be used: Not enabled at compile time\n"
-#~ msgstr "E26: Tiếng Do thái không được chá»n khi biên dịch\n"
+msgid "Zero count"
+msgstr "Giá trị của bộ đếm bằng 0"
-#~ msgid "E27: Farsi cannot be used: Not enabled at compile time\n"
-#~ msgstr "E27: Tiếng Farsi không được chá»n khi biên dịch\n"
+msgid "E81: Using <SID> not in a script context"
+msgstr "E81: Sử dụng <SID> ngoài phạm vi script"
-#~ msgid "E800: Arabic cannot be used: Not enabled at compile time\n"
-#~ msgstr "E800: Tiếng Ả Rập không được chá»n khi biên dịch\n"
+msgid "E449: Invalid expression received"
+msgstr "E449: Nhận được một biểu thức không cho phép"
-#~ msgid "E247: no registered server named \"%s\""
-#~ msgstr "E247: máy chủ \"%s\" chưa đăng ký"
+msgid "E463: Region is guarded, cannot modify"
+msgstr "E463: Không thể thay đổi vùng đã được bảo vệ"
-#~ msgid "E233: cannot open display"
-#~ msgstr "E233: không mở được màn hình"
+msgid "No tutorial with that name found"
+msgstr "Không tìm thấy hướng dẫn (tutorial) có tên đó"
-#~ msgid "E463: Region is guarded, cannot modify"
-#~ msgstr "E463: Không thể thay đổi vùng đã được bảo vệ"
+msgid "Only one argument accepted (check spaces)"
+msgstr "Chỉ chấp nhận một tham số (vui lòng kiểm tra dấu cách)"
diff --git a/src/nvim/po/zh_CN.UTF-8.po b/src/nvim/po/zh_CN.UTF-8.po
index e30fc47806..3593175572 100644
--- a/src/nvim/po/zh_CN.UTF-8.po
+++ b/src/nvim/po/zh_CN.UTF-8.po
@@ -1991,8 +1991,8 @@ msgstr "E146: 正则表达å¼ä¸èƒ½ç”¨å­—æ¯ä½œåˆ†ç•Œ"
#. Same highlight as wait_return().
#: ../ex_cmds.c:3952
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "替æ¢ä¸º %s (y/n/a/q/l/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "替æ¢ä¸º %s?(y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4462
msgid "(Interrupted) "
diff --git a/src/nvim/po/zh_TW.UTF-8.po b/src/nvim/po/zh_TW.UTF-8.po
index cbbc4ad6eb..04e11a9193 100644
--- a/src/nvim/po/zh_TW.UTF-8.po
+++ b/src/nvim/po/zh_TW.UTF-8.po
@@ -1267,8 +1267,8 @@ msgstr "E146: Regular expression 無法用字æ¯åˆ†éš” (?)"
#: ../ex_cmds.c:3964
#, c-format
-msgid "replace with %s (y/n/a/q/l/^E/^Y)?"
-msgstr "å–代為 %s (y/n/a/q/^E/^Y)?"
+msgid "replace with %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
+msgstr "å–代為 %s? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y)"
#: ../ex_cmds.c:4379
msgid "(Interrupted) "
diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c
index 7df6a1a5d7..d1c6f647fd 100644
--- a/src/nvim/popupmenu.c
+++ b/src/nvim/popupmenu.c
@@ -4,22 +4,19 @@
#include <assert.h>
#include <stdbool.h>
+#include <stdint.h>
#include <string.h>
#include "nvim/api/buffer.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/api/vim.h"
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
-#include "nvim/buffer_updates.h"
-#include "nvim/change.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/drawscreen.h"
-#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval/typval.h"
#include "nvim/ex_cmds.h"
@@ -27,6 +24,7 @@
#include "nvim/extmark.h"
#include "nvim/extmark_defs.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
@@ -35,6 +33,7 @@
#include "nvim/highlight_defs.h"
#include "nvim/insexpand.h"
#include "nvim/keycodes.h"
+#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -46,7 +45,6 @@
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/plines.h"
#include "nvim/popupmenu.h"
#include "nvim/pos_defs.h"
@@ -234,12 +232,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
}
// Figure out the size and position of the pum.
- if (size < PUM_DEF_HEIGHT) {
- pum_height = size;
- } else {
- pum_height = PUM_DEF_HEIGHT;
- }
-
+ pum_height = MIN(size, PUM_DEF_HEIGHT);
if (p_ph > 0 && pum_height > p_ph) {
pum_height = (int)p_ph;
}
@@ -256,11 +249,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
context_lines = 0;
} else {
// Leave two lines of context if possible
- if (curwin->w_wrow - curwin->w_cline_row >= 2) {
- context_lines = 2;
- } else {
- context_lines = curwin->w_wrow - curwin->w_cline_row;
- }
+ context_lines = MIN(2, curwin->w_wrow - curwin->w_cline_row);
}
if (pum_win_row - min_row >= size + context_lines) {
@@ -285,20 +274,13 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
} else {
// Leave two lines of context if possible
validate_cheight(curwin);
- if (curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow >= 3) {
- context_lines = 3;
- } else {
- context_lines = curwin->w_cline_row + curwin->w_cline_height - curwin->w_wrow;
- }
+ int cline_visible_offset = curwin->w_cline_row +
+ curwin->w_cline_height - curwin->w_wrow;
+ context_lines = MIN(3, cline_visible_offset);
}
pum_row = pum_win_row + context_lines;
- if (size > below_row - pum_row) {
- pum_height = below_row - pum_row;
- } else {
- pum_height = size;
- }
-
+ pum_height = MIN(below_row - pum_row, size);
if (p_ph > 0 && pum_height > p_ph) {
pum_height = (int)p_ph;
}
@@ -353,15 +335,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_width = max_col - pum_col - pum_scrollbar;
}
- if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
- && pum_width > p_pw) {
- // the width is more than needed for the items, make it
- // narrower
- pum_width = max_width + pum_kind_width + pum_extra_width + 1;
-
- if (pum_width < p_pw) {
- pum_width = (int)p_pw;
- }
+ int content_width = max_width + pum_kind_width + pum_extra_width + 1;
+ if (pum_width > content_width && pum_width > p_pw) {
+ // Reduce width to fit item
+ pum_width = MAX(content_width, (int)p_pw);
} else if (((cursor_col - min_col > p_pw
|| cursor_col - min_col > max_width) && !pum_rl)
|| (pum_rl && (cursor_col < max_col - p_pw
@@ -373,13 +350,10 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_col = max_col - 1;
}
} else if (!pum_rl) {
- if (win_start_col > max_col - max_width - pum_scrollbar
- && max_width <= p_pw) {
+ int right_edge_col = max_col - max_width - pum_scrollbar;
+ if (win_start_col > right_edge_col && max_width <= p_pw) {
// use full width to end of the screen
- pum_col = max_col - max_width - pum_scrollbar;
- if (pum_col < min_col) {
- pum_col = min_col;
- }
+ pum_col = MAX(min_col, right_edge_col);
}
}
@@ -400,12 +374,8 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
pum_width = max_col - pum_col - 1;
}
}
- } else if (pum_width > max_width + pum_kind_width + pum_extra_width + 1
- && pum_width > p_pw) {
- pum_width = max_width + pum_kind_width + pum_extra_width + 1;
- if (pum_width < p_pw) {
- pum_width = (int)p_pw;
- }
+ } else if (pum_width > content_width && pum_width > p_pw) {
+ pum_width = MAX(content_width, (int)p_pw);
}
}
} else if (max_col - min_col < def_width) {
@@ -442,7 +412,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i
/// Returns attributes for every cell, or NULL if all attributes are the same.
static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
{
- if ((hlf != HLF_PSI && hlf != HLF_PNI)
+ if (*text == NUL || (hlf != HLF_PSI && hlf != HLF_PNI)
|| (win_hl_attr(curwin, HLF_PMSI) == win_hl_attr(curwin, HLF_PSI)
&& win_hl_attr(curwin, HLF_PMNI) == win_hl_attr(curwin, HLF_PNI))) {
return NULL;
@@ -456,7 +426,7 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
int *attrs = xmalloc(sizeof(int) * (size_t)vim_strsize(text));
bool in_fuzzy = State == MODE_CMDLINE ? cmdline_compl_is_fuzzy()
- : (get_cot_flags() & COT_FUZZY) != 0;
+ : (get_cot_flags() & kOptCotFlagFuzzy) != 0;
size_t leader_len = strlen(leader);
garray_T *ga = NULL;
@@ -471,6 +441,7 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
const char *ptr = text;
int cell_idx = 0;
uint32_t char_pos = 0;
+ bool is_select = hlf == HLF_PSI;
while (*ptr != NUL) {
int new_attr = win_hl_attr(curwin, (int)hlf);
@@ -479,14 +450,14 @@ static int *pum_compute_text_attrs(char *text, hlf_T hlf, int user_hlattr)
// Handle fuzzy matching
for (int i = 0; i < ga->ga_len; i++) {
if (char_pos == ((uint32_t *)ga->ga_data)[i]) {
- new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI);
+ new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI);
new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
break;
}
}
} else if (matched_start && ptr < text + leader_len) {
- new_attr = win_hl_attr(curwin, hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI);
+ new_attr = win_hl_attr(curwin, is_select ? HLF_PMSI : HLF_PMNI);
new_attr = hl_combine_attr(win_hl_attr(curwin, HLF_PMNI), new_attr);
new_attr = hl_combine_attr(win_hl_attr(curwin, (int)hlf), new_attr);
}
@@ -552,6 +523,15 @@ static inline char *pum_get_item(int index, int type)
return NULL;
}
+static inline int pum_user_attr_combine(int idx, int type, int attr)
+{
+ int user_attr[] = {
+ pum_array[idx].pum_user_abbr_hlattr,
+ pum_array[idx].pum_user_kind_hlattr,
+ };
+ return user_attr[type] > 0 ? hl_combine_attr(attr, user_attr[type]) : attr;
+}
+
/// Redraw the popup menu, using "pum_first" and "pum_selected".
void pum_redraw(void)
{
@@ -615,19 +595,16 @@ void pum_redraw(void)
pum_row - row_off, pum_left_col, false, pum_grid.zindex);
}
+ int scroll_range = pum_size - pum_height;
// Never display more than we have
- if (pum_first > pum_size - pum_height) {
- pum_first = pum_size - pum_height;
- }
+ pum_first = MIN(pum_first, scroll_range);
if (pum_scrollbar) {
thumb_height = pum_height * pum_height / pum_size;
if (thumb_height == 0) {
thumb_height = 1;
}
- thumb_pos = (pum_first * (pum_height - thumb_height)
- + (pum_size - pum_height) / 2)
- / (pum_size - pum_height);
+ thumb_pos = (pum_first * (pum_height - thumb_height) + scroll_range / 2) / scroll_range;
}
for (int i = 0; i < pum_height; i++) {
@@ -665,13 +642,8 @@ void pum_redraw(void)
attr = win_hl_attr(curwin, (int)hlf);
attr = hl_combine_attr(win_hl_attr(curwin, HLF_PNI), attr);
orig_attr = attr;
- int user_abbr_hlattr = pum_array[idx].pum_user_abbr_hlattr;
- int user_kind_hlattr = pum_array[idx].pum_user_kind_hlattr;
- if (item_type == CPT_ABBR && user_abbr_hlattr > 0) {
- attr = hl_combine_attr(attr, user_abbr_hlattr);
- }
- if (item_type == CPT_KIND && user_kind_hlattr > 0) {
- attr = hl_combine_attr(attr, user_kind_hlattr);
+ if (item_type < 2) { // try combine attr with user custom
+ attr = pum_user_attr_combine(idx, item_type, attr);
}
int width = 0;
char *s = NULL;
@@ -682,89 +654,87 @@ void pum_redraw(void)
s = p;
}
int w = ptr2cells(p);
+ if (*p != NUL && *p != TAB && totwidth + w <= pum_width) {
+ width += w;
+ continue;
+ }
- if ((*p == NUL) || (*p == TAB) || (totwidth + w > pum_width)) {
- // Display the text that fits or comes before a Tab.
- // First convert it to printable characters.
- char *st;
- char saved = *p;
-
- if (saved != NUL) {
- *p = NUL;
- }
- st = transstr(s, true);
- if (saved != NUL) {
- *p = saved;
- }
+ // Display the text that fits or comes before a Tab.
+ // First convert it to printable characters.
+ char saved = *p;
- int *attrs = NULL;
- if (item_type == CPT_ABBR) {
- attrs = pum_compute_text_attrs(st, hlf, user_abbr_hlattr);
- }
+ if (saved != NUL) {
+ *p = NUL;
+ }
+ char *st = transstr(s, true);
+ if (saved != NUL) {
+ *p = saved;
+ }
- if (pum_rl) {
- char *rt = reverse_text(st);
- char *rt_start = rt;
- int cells = vim_strsize(rt);
-
- if (cells > pum_width) {
- do {
- cells -= utf_ptr2cells(rt);
- MB_PTR_ADV(rt);
- } while (cells > pum_width);
-
- if (cells < pum_width) {
- // Most left character requires 2-cells but only 1 cell
- // is available on screen. Put a '<' on the left of the
- // pum item
- *(--rt) = '<';
- cells++;
- }
- }
+ int *attrs = NULL;
+ if (item_type == CPT_ABBR) {
+ attrs = pum_compute_text_attrs(st, hlf,
+ pum_array[idx].pum_user_abbr_hlattr);
+ }
- if (attrs == NULL) {
- grid_line_puts(grid_col - cells + 1, rt, -1, attr);
- } else {
- pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs);
+ if (pum_rl) {
+ char *rt = reverse_text(st);
+ char *rt_start = rt;
+ int cells = vim_strsize(rt);
+
+ if (cells > pum_width) {
+ do {
+ cells -= utf_ptr2cells(rt);
+ MB_PTR_ADV(rt);
+ } while (cells > pum_width);
+
+ if (cells < pum_width) {
+ // Most left character requires 2-cells but only 1 cell is available on
+ // screen. Put a '<' on the left of the pum item.
+ *(--rt) = '<';
+ cells++;
}
+ }
- xfree(rt_start);
- xfree(st);
- grid_col -= width;
+ if (attrs == NULL) {
+ grid_line_puts(grid_col - cells + 1, rt, -1, attr);
} else {
- if (attrs == NULL) {
- grid_line_puts(grid_col, st, -1, attr);
- } else {
- pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs);
- }
-
- xfree(st);
- grid_col += width;
+ pum_grid_puts_with_attrs(grid_col - cells + 1, cells, rt, -1, attrs);
}
- if (attrs != NULL) {
- XFREE_CLEAR(attrs);
+ xfree(rt_start);
+ xfree(st);
+ grid_col -= width;
+ } else {
+ if (attrs == NULL) {
+ grid_line_puts(grid_col, st, -1, attr);
+ } else {
+ pum_grid_puts_with_attrs(grid_col, vim_strsize(st), st, -1, attrs);
}
- if (*p != TAB) {
- break;
- }
+ xfree(st);
+ grid_col += width;
+ }
- // Display two spaces for a Tab.
- if (pum_rl) {
- grid_line_puts(grid_col - 1, " ", 2, attr);
- grid_col -= 2;
- } else {
- grid_line_puts(grid_col, " ", 2, attr);
- grid_col += 2;
- }
- totwidth += 2;
- // start text at next char
- s = NULL;
- width = 0;
+ if (attrs != NULL) {
+ XFREE_CLEAR(attrs);
+ }
+
+ if (*p != TAB) {
+ break;
+ }
+
+ // Display two spaces for a Tab.
+ if (pum_rl) {
+ grid_line_puts(grid_col - 1, " ", 2, attr);
+ grid_col -= 2;
} else {
- width += w;
+ grid_line_puts(grid_col, " ", 2, attr);
+ grid_col += 2;
}
+ totwidth += 2;
+ s = NULL; // start text at next char
+ width = 0;
}
}
@@ -817,36 +787,41 @@ void pum_redraw(void)
}
}
-/// set info text to preview buffer.
+/// Set the informational text in the preview buffer when the completion
+/// item does not include a dedicated preview or popup window.
+///
+/// @param[in] buf Buffer where the text will be set.
+/// @param[in] info Informational text to display in the preview buffer.
+/// @param[in] lnum Where to start the text. Incremented for each added line.
+/// @param[out] max_width Maximum width of the displayed text.
static void pum_preview_set_text(buf_T *buf, char *info, linenr_T *lnum, int *max_width)
{
- bcount_t inserted_bytes = 0;
- for (char *p = info; *p != NUL;) {
- int text_width = 0;
- char *e = vim_strchr(p, '\n');
- if (e == NULL) {
- ml_append_buf(buf, (*lnum)++, p, 0, false);
- text_width = (int)mb_string2cells(p);
- if (text_width > *max_width) {
- *max_width = text_width;
- }
- break;
- }
- *e = NUL;
- ml_append_buf(buf, (*lnum)++, p, (int)(e - p + 1), false);
- inserted_bytes += (bcount_t)strlen(p) + 1;
- text_width = (int)mb_string2cells(p);
- if (text_width > *max_width) {
- *max_width = text_width;
- }
- *e = '\n';
- p = e + 1;
+ Error err = ERROR_INIT;
+ Arena arena = ARENA_EMPTY;
+ Array replacement = ARRAY_DICT_INIT;
+ char *token = NULL;
+ char *line = os_strtok(info, "\n", &token);
+ buf->b_p_ma = true;
+ while (line != NULL) {
+ ADD(replacement, STRING_OBJ(cstr_to_string(line)));
+ (*lnum)++;
+ (*max_width) = MAX(*max_width, (int)mb_string2cells(line));
+ line = os_strtok(NULL, "\n", &token);
}
- // delete the empty last line
- ml_delete_buf(buf, buf->b_ml.ml_line_count, false);
- if (get_cot_flags() & COT_POPUP) {
- extmark_splice(buf, 1, 0, 1, 0, 0, buf->b_ml.ml_line_count, 0, inserted_bytes, kExtmarkNoUndo);
+
+ int original_textlock = textlock;
+ if (textlock > 0) {
+ textlock = 0;
}
+ nvim_buf_set_lines(0, buf->handle, 0, -1, false, replacement, &arena, &err);
+ textlock = original_textlock;
+ if (ERROR_SET(&err)) {
+ emsg(err.msg);
+ api_clear_error(&err);
+ }
+ arena_mem_free(arena_finish(&arena));
+ api_free_array(replacement);
+ buf->b_p_ma = false;
}
/// adjust floating info preview window position
@@ -880,7 +855,7 @@ static void pum_adjust_info_position(win_T *wp, int height, int width)
/// Used for nvim__complete_set
///
/// @param selected the selected compl item.
-/// @parma info Info string.
+/// @param info Info string.
/// @return a win_T pointer.
win_T *pum_set_info(int selected, char *info)
{
@@ -896,14 +871,6 @@ win_T *pum_set_info(int selected, char *info)
if (!wp) {
return NULL;
}
- } else {
- // clean exist buffer
- linenr_T count = wp->w_buffer->b_ml.ml_line_count;
- while (!buf_is_empty(wp->w_buffer)) {
- ml_delete_buf(wp->w_buffer, 1, false);
- }
- bcount_t deleted_bytes = get_region_bytecount(wp->w_buffer, 1, count, 0, 0);
- extmark_splice(wp->w_buffer, 1, 0, count, 0, deleted_bytes, 1, 0, 0, kExtmarkNoUndo);
}
linenr_T lnum = 0;
int max_info_width = 0;
@@ -939,13 +906,17 @@ static bool pum_set_selected(int n, int repeat)
int prev_selected = pum_selected;
pum_selected = n;
+ int scroll_offset = pum_selected - pum_height;
unsigned cur_cot_flags = get_cot_flags();
- bool use_float = (cur_cot_flags & COT_POPUP) != 0;
- // when new leader add and info window is shown and no selected we still
- // need use the first index item to update the info float window position.
- bool force_select = use_float && pum_selected < 0 && win_float_find_preview();
- if (force_select) {
- pum_selected = 0;
+ bool use_float = (cur_cot_flags & kOptCotFlagPopup) != 0;
+
+ // Close the floating preview window if 'selected' is -1, indicating a return to the original
+ // state. It is also closed when the selected item has no corresponding info item.
+ if (use_float && (pum_selected < 0 || pum_array[pum_selected].pum_info == NULL)) {
+ win_T *wp = win_float_find_preview();
+ if (wp) {
+ win_close(wp, true, true);
+ }
}
if ((pum_selected >= 0) && (pum_selected < pum_size)) {
@@ -962,41 +933,28 @@ static bool pum_set_selected(int n, int repeat)
} else {
pum_first = pum_selected;
}
- } else if (pum_first < pum_selected - pum_height + 5) {
+ } else if (pum_first < scroll_offset + 5) {
// scroll up; when we did a jump it's probably a PageDown then
// scroll a whole page
- if (pum_first < pum_selected - pum_height + 1 + 2) {
- pum_first += pum_height - 2;
- if (pum_first < pum_selected - pum_height + 1) {
- pum_first = pum_selected - pum_height + 1;
- }
+ if (pum_first < scroll_offset + 3) {
+ pum_first = MAX(pum_first + pum_height - 2, scroll_offset + 1);
} else {
- pum_first = pum_selected - pum_height + 1;
+ pum_first = scroll_offset + 1;
}
}
// Give a few lines of context when possible.
- if (context > 3) {
- context = 3;
- }
+ context = MIN(context, 3);
if (pum_height > 2) {
if (pum_first > pum_selected - context) {
- // scroll down
- pum_first = pum_selected - context;
-
- if (pum_first < 0) {
- pum_first = 0;
- }
+ pum_first = MAX(pum_selected - context, 0); // scroll down
} else if (pum_first < pum_selected + context - pum_height + 1) {
- // scroll up
- pum_first = pum_selected + context - pum_height + 1;
+ pum_first = pum_selected + context - pum_height + 1; // up
}
}
// adjust for the number of lines displayed
- if (pum_first > pum_size - pum_height) {
- pum_first = pum_size - pum_height;
- }
+ pum_first = MIN(pum_first, pum_size - pum_height);
// Show extra info in the preview window if there is something and
// 'completeopt' contains "preview".
@@ -1006,7 +964,7 @@ static bool pum_set_selected(int n, int repeat)
if ((pum_array[pum_selected].pum_info != NULL)
&& (Rows > 10)
&& (repeat <= 1)
- && (cur_cot_flags & COT_ANY_PREVIEW)) {
+ && (cur_cot_flags & (kOptCotFlagPreview | kOptCotFlagPopup))) {
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
@@ -1050,7 +1008,8 @@ static bool pum_set_selected(int n, int repeat)
&& (curbuf->b_nwindows == 1)
&& (curbuf->b_fname == NULL)
&& bt_nofile(curbuf)
- && (curbuf->b_p_bh[0] == 'w')) {
+ && (curbuf->b_p_bh[0] == 'w')
+ && !use_float) {
// Already a "wipeout" buffer, make it empty.
while (!buf_is_empty(curbuf)) {
ml_delete(1, false);
@@ -1079,9 +1038,7 @@ static bool pum_set_selected(int n, int repeat)
// Increase the height of the preview window to show the
// text, but no more than 'previewheight' lines.
if (repeat == 0 && !use_float) {
- if (lnum > p_pvh) {
- lnum = (linenr_T)p_pvh;
- }
+ lnum = MIN(lnum, (linenr_T)p_pvh);
if (curwin->w_height < lnum) {
win_setheight((int)lnum);
@@ -1164,11 +1121,6 @@ static bool pum_set_selected(int n, int repeat)
}
}
- // restore before selected value
- if (force_select) {
- pum_selected = n;
- }
-
return resized;
}
@@ -1349,9 +1301,7 @@ static void pum_position_at_mouse(int min_width)
pum_width = max_col - pum_col;
}
- if (pum_width > pum_base_width + 1) {
- pum_width = pum_base_width + 1;
- }
+ pum_width = MIN(pum_width, pum_base_width + 1);
}
/// Select the pum entry at the mouse position.
diff --git a/src/nvim/profile.c b/src/nvim/profile.c
index 1b4f4a2029..65d341cac3 100644
--- a/src/nvim/profile.c
+++ b/src/nvim/profile.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <uv.h>
#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 6526b0d0bf..44b66c4f73 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -38,7 +38,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
@@ -2697,7 +2696,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum)
// Didn't find it, go to the window before the quickfix
// window, unless 'switchbuf' contains 'uselast': in this case we
// try to jump to the previously used window first.
- if ((swb_flags & SWB_USELAST) && win_valid(prevwin)
+ if ((swb_flags & kOptSwbFlagUselast) && win_valid(prevwin)
&& !prevwin->w_p_wfb) {
win = prevwin;
} else if (altwin != NULL) {
@@ -2754,7 +2753,7 @@ static int qf_jump_to_usable_window(int qf_fnum, bool newwin, bool *opened_windo
// If no usable window is found and 'switchbuf' contains "usetab"
// then search in other tabs.
- if (!usable_win && (swb_flags & SWB_USETAB)) {
+ if (!usable_win && (swb_flags & kOptSwbFlagUsetab)) {
usable_win = qf_goto_tabwin_with_file(qf_fnum);
}
@@ -2937,7 +2936,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf
msg_scroll = false;
}
msg_ext_set_kind("quickfix");
- msg_hl_keep(gap->ga_data, 0, true, false);
+ msg_keep(gap->ga_data, 0, true, false);
msg_scroll = (int)i;
qfga_clear();
@@ -3032,7 +3031,7 @@ static int qf_jump_to_buffer(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, int
qf_jump_goto_line(qf_ptr->qf_lnum, qf_ptr->qf_col, qf_ptr->qf_viscol, qf_ptr->qf_pattern);
- if ((fdo_flags & FDO_QUICKFIX) && openfold) {
+ if ((fdo_flags & kOptFdoFlagQuickfix) && openfold) {
foldOpenCursor();
}
if (print_message) {
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index c91c112c3c..de9a7e580f 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -12,6 +12,7 @@
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include <uv.h>
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index 3f00b74e61..cdedf86977 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -43,6 +43,7 @@
#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
@@ -159,10 +160,7 @@ 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);
+ return entry->es_name != NULL ? xstrdup(entry->es_name) : NULL;
}
// If evaluated in a function or autocommand, return the path of the script
@@ -230,6 +228,72 @@ char *estack_sfile(estack_arg_T which)
return (char *)ga.ga_data;
}
+static void stacktrace_push_item(list_T *const l, ufunc_T *const fp, const char *const event,
+ const linenr_T lnum, char *const filepath,
+ const bool filepath_alloced)
+{
+ dict_T *const d = tv_dict_alloc_lock(VAR_FIXED);
+ typval_T tv = {
+ .v_type = VAR_DICT,
+ .v_lock = VAR_LOCKED,
+ .vval.v_dict = d,
+ };
+
+ if (fp != NULL) {
+ tv_dict_add_func(d, S_LEN("funcref"), fp);
+ }
+ if (event != NULL) {
+ tv_dict_add_str(d, S_LEN("event"), event);
+ }
+ tv_dict_add_nr(d, S_LEN("lnum"), lnum);
+ if (filepath_alloced) {
+ tv_dict_add_allocated_str(d, S_LEN("filepath"), filepath);
+ } else {
+ tv_dict_add_str(d, S_LEN("filepath"), filepath);
+ }
+
+ tv_list_append_tv(l, &tv);
+}
+
+/// Create the stacktrace from exestack.
+list_T *stacktrace_create(void)
+{
+ list_T *const l = tv_list_alloc(exestack.ga_len);
+
+ for (int i = 0; i < exestack.ga_len; i++) {
+ estack_T *const entry = &((estack_T *)exestack.ga_data)[i];
+ linenr_T lnum = entry->es_lnum;
+
+ if (entry->es_type == ETYPE_SCRIPT) {
+ stacktrace_push_item(l, NULL, NULL, lnum, entry->es_name, false);
+ } else if (entry->es_type == ETYPE_UFUNC) {
+ ufunc_T *const fp = entry->es_info.ufunc;
+ const sctx_T sctx = fp->uf_script_ctx;
+ bool filepath_alloced = false;
+ char *filepath = sctx.sc_sid > 0
+ ? get_scriptname((LastSet){ .script_ctx = sctx },
+ &filepath_alloced) : "";
+ lnum += sctx.sc_lnum;
+ stacktrace_push_item(l, fp, NULL, lnum, filepath, filepath_alloced);
+ } else if (entry->es_type == ETYPE_AUCMD) {
+ const sctx_T sctx = entry->es_info.aucmd->script_ctx;
+ bool filepath_alloced = false;
+ char *filepath = sctx.sc_sid > 0
+ ? get_scriptname((LastSet){ .script_ctx = sctx },
+ &filepath_alloced) : "";
+ lnum += sctx.sc_lnum;
+ stacktrace_push_item(l, NULL, entry->es_name, lnum, filepath, filepath_alloced);
+ }
+ }
+ return l;
+}
+
+/// getstacktrace() function
+void f_getstacktrace(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_set_ret(rettv, stacktrace_create());
+}
+
static bool runtime_search_path_valid = false;
static int *runtime_search_path_ref = NULL;
static RuntimeSearchPath runtime_search_path;
@@ -499,7 +563,6 @@ static void runtime_search_path_unref(RuntimeSearchPath path, const int *ref)
/// return FAIL when no file could be sourced, OK otherwise.
static int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char *tail;
bool did_one = false;
char buf[MAXPATHL];
@@ -533,7 +596,7 @@ static int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback,
} else if (buflen + strlen(name) + 2 < MAXPATHL) {
STRCPY(buf, item.path);
add_pathsep(buf);
- tail = buf + strlen(buf);
+ char *tail = buf + strlen(buf);
// Loop over all patterns in "name"
char *np = name;
@@ -935,7 +998,6 @@ static int gen_expand_wildcards_and_cb(int num_pat, char **pats, int flags, bool
/// @param is_pack whether the added dir is a "pack/*/start/*/" style package
static int add_pack_dir_to_rtp(char *fname, bool is_pack)
{
- char *p;
char *afterdir = NULL;
int retval = FAIL;
@@ -943,7 +1005,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
char *p2 = p1;
char *p3 = p1;
char *p4 = p1;
- for (p = p1; *p; MB_PTR_ADV(p)) {
+ for (char *p = p1; *p; MB_PTR_ADV(p)) {
if (vim_ispathsep_nocolon(*p)) {
p4 = p3;
p3 = p2;
@@ -970,21 +1032,21 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
// Find "ffname" in "p_rtp", ignoring '/' vs '\' differences
// Also stop at the first "after" directory
size_t fname_len = strlen(ffname);
- char *buf = try_malloc(MAXPATHL);
- if (buf == NULL) {
- goto theend;
- }
+ char buf[MAXPATHL];
const char *insp = NULL;
const char *after_insp = NULL;
- for (const char *entry = p_rtp; *entry != NUL;) {
+ const char *entry = p_rtp;
+ while (*entry != NUL) {
const char *cur_entry = entry;
-
copy_option_part((char **)&entry, buf, MAXPATHL, ",");
- if ((p = strstr(buf, "after")) != NULL
- && p > buf
- && vim_ispathsep(p[-1])
- && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',')) {
+ char *p = strstr(buf, "after");
+ bool is_after = p != NULL
+ && p > buf
+ && vim_ispathsep(p[-1])
+ && (vim_ispathsep(p[5]) || p[5] == NUL || p[5] == ',');
+
+ if (is_after) {
if (insp == NULL) {
// Did not find "ffname" before the first "after" directory,
// insert it before this entry.
@@ -1000,12 +1062,11 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
if (rtp_ffname == NULL) {
goto theend;
}
- bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
- xfree(rtp_ffname);
- if (match) {
+ if (path_fnamencmp(rtp_ffname, ffname, fname_len) == 0) {
// Insert "ffname" after this entry (and comma).
insp = entry;
}
+ xfree(rtp_ffname);
}
}
@@ -1075,7 +1136,6 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
retval = OK;
theend:
- xfree(buf);
xfree(ffname);
xfree(afterdir);
return retval;
@@ -1123,7 +1183,7 @@ static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all,
bool did_one = false;
if (cookie != &APP_LOAD) {
- char *buf = xmalloc(MAXPATHL);
+ char buf[MAXPATHL];
for (int i = 0; i < num_fnames; i++) {
bool found = false;
@@ -1138,7 +1198,6 @@ static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all,
if (!found) {
// directory is not yet in 'runtimepath', add it
if (add_pack_dir_to_rtp(fnames[i], false) == FAIL) {
- xfree(buf);
return;
}
}
@@ -1147,7 +1206,6 @@ static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all,
break;
}
}
- xfree(buf);
}
if (!all && did_one) {
@@ -1276,25 +1334,23 @@ void ex_packadd(exarg_T *eap)
static const char plugpat[] = "pack/*/%s/%s"; // NOLINT
int res = OK;
- // Round 1: use "start", round 2: use "opt".
- for (int round = 1; round <= 2; round++) {
- // Only look under "start" when loading packages wasn't done yet.
- if (round == 1 && did_source_packages) {
- continue;
- }
+ const size_t len = sizeof(plugpat) + strlen(eap->arg) + 5;
+ char *pat = xmallocz(len);
+ void *cookie = eap->forceit ? &APP_ADD_DIR : &APP_BOTH;
- const size_t len = sizeof(plugpat) + strlen(eap->arg) + 5;
- char *pat = xmallocz(len);
- 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_plugins : add_opt_pack_plugins,
- eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
- xfree(pat);
+ // Only look under "start" when loading packages wasn't done yet.
+ if (!did_source_packages) {
+ vim_snprintf(pat, len, plugpat, "start", eap->arg);
+ res = do_in_path(p_pp, "", pat, DIP_ALL + DIP_DIR,
+ add_start_pack_plugins, cookie);
}
+
+ // Give a "not found" error if nothing was found in 'start' or 'opt'.
+ vim_snprintf(pat, len, plugpat, "opt", eap->arg);
+ do_in_path(p_pp, "", pat, DIP_ALL + DIP_DIR + (res == FAIL ? DIP_ERR : 0),
+ add_opt_pack_plugins, cookie);
+
+ xfree(pat);
}
static void ExpandRTDir_int(char *pat, size_t pat_len, int flags, bool keep_ext, garray_T *gap,
@@ -2726,22 +2782,15 @@ retry:
/// Without the multi-byte feature it's simply ignored.
void ex_scriptencoding(exarg_T *eap)
{
- source_cookie_T *sp;
- char *name;
-
if (!getline_equal(eap->ea_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;
- }
+ char *name = (*eap->arg != NUL) ? enc_canonize(eap->arg) : eap->arg;
// Setup for conversion from the specified encoding to 'encoding'.
- sp = (source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie);
+ source_cookie_T *sp = (source_cookie_T *)getline_cookie(eap->ea_getline, eap->cookie);
convert_setup(&sp->conv, name, p_enc);
if (name != eap->arg) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index debc5697d1..9f8ceae2a0 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -30,10 +30,10 @@
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
+#include "nvim/garray_defs.h"
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/indent_c.h"
#include "nvim/insexpand.h"
@@ -41,7 +41,6 @@
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
-#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
@@ -1203,6 +1202,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen
// Compute msg_row early.
msg_start();
+ msg_ext_set_kind("search_cmd");
// Get the offset, so we know how long it is.
if (!cmd_silent
@@ -1422,7 +1422,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, size_t patlen
cmdline_search_stat(dirc, &pos, &curwin->w_cursor,
show_top_bot_msg, msgbuf, msgbuflen,
(count != 1 || has_offset
- || (!(fdo_flags & FDO_SEARCH)
+ || (!(fdo_flags & kOptFdoFlagSearch)
&& hasFolding(curwin, curwin->w_cursor.lnum, NULL,
NULL))),
SEARCH_STAT_DEF_MAX_COUNT,
@@ -2349,7 +2349,7 @@ void showmatch(int c)
}
if ((lpos = findmatch(NULL, NUL)) == NULL) { // no match, so beep
- vim_beep(BO_MATCH);
+ vim_beep(kOptBoFlagShowmatch);
return;
}
@@ -2534,7 +2534,7 @@ int current_search(int count, bool forward)
}
}
- if (fdo_flags & FDO_SEARCH && KeyTyped) {
+ if (fdo_flags & kOptFdoFlagSearch && KeyTyped) {
foldOpenCursor();
}
@@ -2973,6 +2973,10 @@ typedef struct {
#define CAMEL_BONUS 30
/// bonus if the first letter is matched
#define FIRST_LETTER_BONUS 15
+/// bonus if exact match
+#define EXACT_MATCH_BONUS 100
+/// bonus if case match when no ignorecase
+#define CASE_MATCH_BONUS 25
/// penalty applied for every letter in str before the first match
#define LEADING_LETTER_PENALTY (-5)
/// maximum penalty for leading letters
@@ -2988,11 +2992,21 @@ typedef struct {
/// Compute a score for a fuzzy matched string. The matching character locations
/// are in "matches".
-static int fuzzy_match_compute_score(const char *const str, const int strSz,
- const uint32_t *const matches, const int numMatches)
+static int fuzzy_match_compute_score(const char *const fuzpat, const char *const str,
+ const int strSz, const uint32_t *const matches,
+ const int numMatches)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
assert(numMatches > 0); // suppress clang "result of operation is garbage"
+ const char *p = str;
+ uint32_t sidx = 0;
+ bool is_exact_match = true;
+ const char *const orig_fuzpat = fuzpat - numMatches;
+ const char *curpat = orig_fuzpat;
+ int pat_idx = 0;
+ // Track consecutive camel case matches
+ int consecutive_camel = 0;
+
// Initialize score
int score = 100;
@@ -3010,6 +3024,7 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
// Apply ordering bonuses
for (int i = 0; i < numMatches; i++) {
const uint32_t currIdx = matches[i];
+ bool is_camel = false;
if (i > 0) {
const uint32_t prevIdx = matches[i - 1];
@@ -3019,23 +3034,35 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
score += SEQUENTIAL_BONUS;
} else {
score += GAP_PENALTY * (int)(currIdx - prevIdx);
+ // Reset consecutive camel count on gap
+ consecutive_camel = 0;
}
}
+ int curr;
// Check for bonuses based on neighbor character value
if (currIdx > 0) {
// Camel case
- const char *p = str;
int neighbor = ' ';
- for (uint32_t sidx = 0; sidx < currIdx; sidx++) {
+ while (sidx < currIdx) {
neighbor = utf_ptr2char(p);
MB_PTR_ADV(p);
+ sidx++;
}
- const int curr = utf_ptr2char(p);
+ curr = utf_ptr2char(p);
+ // Enhanced camel case scoring
if (mb_islower(neighbor) && mb_isupper(curr)) {
- score += CAMEL_BONUS;
+ score += CAMEL_BONUS * 2; // Double the camel case bonus
+ is_camel = true;
+ consecutive_camel++;
+ // Additional bonus for consecutive camel
+ if (consecutive_camel > 1) {
+ score += CAMEL_BONUS;
+ }
+ } else {
+ consecutive_camel = 0;
}
// Bonus if the match follows a separator character
@@ -3047,8 +3074,36 @@ static int fuzzy_match_compute_score(const char *const str, const int strSz,
} else {
// First letter
score += FIRST_LETTER_BONUS;
+ curr = utf_ptr2char(p);
+ }
+
+ // Case matching bonus
+ if (mb_isalpha(curr)) {
+ while (pat_idx < i && *curpat) {
+ MB_PTR_ADV(curpat);
+ pat_idx++;
+ }
+
+ if (curr == utf_ptr2char(curpat)) {
+ score += CASE_MATCH_BONUS;
+ // Extra bonus for exact case match in camel
+ if (is_camel) {
+ score += CASE_MATCH_BONUS / 2;
+ }
+ }
+ }
+
+ // Check exact match condition
+ if (currIdx != (uint32_t)i) {
+ is_exact_match = false;
}
}
+
+ // Boost score for exact matches
+ if (is_exact_match && numMatches == strSz) {
+ score += EXACT_MATCH_BONUS;
+ }
+
return score;
}
@@ -3127,7 +3182,7 @@ static int fuzzy_match_recursive(const char *fuzpat, const char *str, uint32_t s
// Calculate score
if (matched) {
- *outScore = fuzzy_match_compute_score(strBegin, strLen, matches, nextMatch);
+ *outScore = fuzzy_match_compute_score(fuzpat, strBegin, strLen, matches, nextMatch);
}
// Return best result
diff --git a/src/nvim/search.h b/src/nvim/search.h
index 1b6c1a6375..d92f3c9002 100644
--- a/src/nvim/search.h
+++ b/src/nvim/search.h
@@ -1,9 +1,10 @@
#pragma once
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
-#include "nvim/eval/typval_defs.h"
+#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/normal_defs.h" // IWYU pragma: keep
#include "nvim/os/time_defs.h"
#include "nvim/pos_defs.h"
diff --git a/src/nvim/shada.c b/src/nvim/shada.c
index 2961b35a29..a5e98af7ac 100644
--- a/src/nvim/shada.c
+++ b/src/nvim/shada.c
@@ -2,21 +2,22 @@
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <uv.h>
#include "auto/config.h"
+#include "klib/kvec.h"
+#include "mpack/mpack_core.h"
#include "nvim/api/keysets_defs.h"
#include "nvim/api/private/defs.h"
+#include "nvim/api/private/dispatch.h"
#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/buffer.h"
#include "nvim/buffer_defs.h"
#include "nvim/cmdhist.h"
-#include "nvim/errors.h"
#include "nvim/eval.h"
#include "nvim/eval/decode.h"
#include "nvim/eval/encode.h"
@@ -26,8 +27,6 @@
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/fileio.h"
-#include "nvim/garray.h"
-#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/hashtab.h"
@@ -38,8 +37,10 @@
#include "nvim/mark_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/msgpack_rpc/packer.h"
+#include "nvim/msgpack_rpc/packer_defs.h"
#include "nvim/msgpack_rpc/unpacker.h"
#include "nvim/normal_defs.h"
#include "nvim/ops.h"
diff --git a/src/nvim/shada.h b/src/nvim/shada.h
index 7d736dadc7..558e020180 100644
--- a/src/nvim/shada.h
+++ b/src/nvim/shada.h
@@ -1,6 +1,6 @@
#pragma once
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
/// Flags for shada_read_file and children
typedef enum {
diff --git a/src/nvim/sign.c b/src/nvim/sign.c
index f8e7eeaca4..a6e72b8855 100644
--- a/src/nvim/sign.c
+++ b/src/nvim/sign.c
@@ -32,8 +32,6 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/macros_defs.h"
@@ -43,7 +41,6 @@
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
-#include "nvim/move.h"
#include "nvim/pos_defs.h"
#include "nvim/sign.h"
#include "nvim/sign_defs.h"
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index a0fdd46b04..7437a7a32f 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -245,7 +245,7 @@ size_t spell_check(win_T *wp, char *ptr, hlf_T *attrp, int *capcol, bool docount
size_t nrlen = 0; // found a number first
size_t wrongcaplen = 0;
bool count_word = docount;
- bool use_camel_case = (wp->w_s->b_p_spo_flags & SPO_CAMEL) != 0;
+ bool use_camel_case = (wp->w_s->b_p_spo_flags & kOptSpoFlagCamel) != 0;
bool is_camel_case = false;
matchinf_T mi; // Most things are put in "mi" so that it can be passed to functions quickly.
@@ -1407,7 +1407,7 @@ size_t spell_move_to(win_T *wp, int dir, smt_T behaviour, bool curline, hlf_T *a
: p - buf) > wp->w_cursor.col)) {
colnr_T col = (colnr_T)(p - buf);
- bool no_plain_buffer = (wp->w_s->b_p_spo_flags & SPO_NPBUFFER) != 0;
+ bool no_plain_buffer = (wp->w_s->b_p_spo_flags & kOptSpoFlagNoplainbuffer) != 0;
bool can_spell = !no_plain_buffer;
switch (decor_spell_nav_col(wp, lnum, &decor_lnum, col)) {
case kTrue:
@@ -3141,7 +3141,7 @@ static void spell_soundfold_wsal(slang_T *slang, const char *inword, char *res)
c = *ws;
}
if (strstr(s, "^^") != NULL) {
- if (c != NUL) {
+ if (c != NUL && reslen < MAXWLEN) {
wres[reslen++] = c;
}
memmove(word, word + i + 1, sizeof(int) * (size_t)(wordlen - (i + 1) + 1));
diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c
index b37f01e769..21bfa367bf 100644
--- a/src/nvim/spellsuggest.c
+++ b/src/nvim/spellsuggest.c
@@ -402,7 +402,7 @@ int spell_check_sps(void)
if (*s != NUL && !ascii_isdigit(*s)) {
f = -1;
}
- // Note: Keep this in sync with p_sps_values.
+ // Note: Keep this in sync with opt_sps_values.
} else if (strcmp(buf, "best") == 0) {
f = SPS_BEST;
} else if (strcmp(buf, "fast") == 0) {
@@ -444,7 +444,7 @@ void spell_suggest(int count)
char wcopy[MAXWLEN + 2];
suginfo_T sug;
suggest_T *stp;
- bool mouse_used;
+ bool mouse_used = false;
int selected = count;
int badlen = 0;
int msg_scroll_save = msg_scroll;
@@ -464,7 +464,7 @@ void spell_suggest(int count)
// 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);
+ vim_beep(kOptBoFlagSpell);
return;
}
badlen = (int)curwin->w_cursor.col - (int)VIsual.col;
@@ -516,6 +516,7 @@ void spell_suggest(int count)
spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit,
true, need_cap, true);
+ msg_ext_set_kind("list_cmd");
if (GA_EMPTY(&sug.su_ga)) {
msg(_("Sorry, no suggestions"), 0);
} else if (count > 0) {
@@ -593,15 +594,11 @@ void spell_suggest(int count)
cmdmsg_rl = false;
msg_col = 0;
// Ask for choice.
- selected = prompt_for_number(&mouse_used);
-
- if (ui_has(kUIMessages)) {
- ui_call_msg_clear();
- }
-
+ selected = prompt_for_input(NULL, 0, false, &mouse_used);
if (mouse_used) {
- selected -= lines_left;
+ selected = sug.su_ga.ga_len + 1 - (cmdline_row - mouse_row);
}
+
lines_left = Rows; // avoid more prompt
// don't delay for 'smd' in normal_cmd()
msg_scroll = msg_scroll_save;
diff --git a/src/nvim/state.c b/src/nvim/state.c
index 908f724792..c4041dda07 100644
--- a/src/nvim/state.c
+++ b/src/nvim/state.c
@@ -138,17 +138,23 @@ void state_handle_k_event(void)
/// Return true if in the current mode we need to use virtual.
bool virtual_active(win_T *wp)
{
- unsigned cur_ve_flags = get_ve_flags(wp);
-
// While an operator is being executed we return "virtual_op", because
// VIsual_active has already been reset, thus we can't check for "block"
// being used.
if (virtual_op != kNone) {
return virtual_op;
}
- return cur_ve_flags == VE_ALL
- || ((cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V)
- || ((cur_ve_flags & VE_INSERT) && (State & MODE_INSERT));
+
+ // In Terminal mode the cursor can be positioned anywhere by the application
+ if (State & MODE_TERMINAL) {
+ return true;
+ }
+
+ unsigned cur_ve_flags = get_ve_flags(wp);
+
+ return cur_ve_flags == kOptVeFlagAll
+ || ((cur_ve_flags & kOptVeFlagBlock) && VIsual_active && VIsual_mode == Ctrl_V)
+ || ((cur_ve_flags & kOptVeFlagInsert) && (State & MODE_INSERT));
}
/// MODE_VISUAL, MODE_SELECT and MODE_OP_PENDING State are never set, they are
diff --git a/src/nvim/state.h b/src/nvim/state.h
index 8220d90a67..2cc493de46 100644
--- a/src/nvim/state.h
+++ b/src/nvim/state.h
@@ -1,7 +1,7 @@
#pragma once
#include "nvim/state_defs.h" // IWYU pragma: keep
-#include "nvim/types_defs.h"
+#include "nvim/types_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "state.h.generated.h"
diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c
index 4e78067d46..ddae023ad5 100644
--- a/src/nvim/statusline.c
+++ b/src/nvim/statusline.c
@@ -35,7 +35,6 @@
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/option_vars.h"
-#include "nvim/optionstr.h"
#include "nvim/os/os.h"
#include "nvim/os/os_defs.h"
#include "nvim/path.h"
@@ -97,53 +96,52 @@ void win_redr_status(win_T *wp)
get_trans_bufname(wp->w_buffer);
char *p = NameBuff;
- int len = (int)strlen(p);
+ int plen = (int)strlen(p);
if ((bt_help(wp->w_buffer)
|| wp->w_p_pvw
|| bufIsChanged(wp->w_buffer)
|| wp->w_buffer->b_p_ro)
- && len < MAXPATHL - 1) {
- *(p + len++) = ' ';
+ && plen < MAXPATHL - 1) {
+ *(p + plen++) = ' '; // replace NUL with space
+ *(p + plen) = NUL; // NUL terminate the string
}
if (bt_help(wp->w_buffer)) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Help]"));
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[Help]"));
}
if (wp->w_p_pvw) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[Preview]"));
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[Preview]"));
}
if (bufIsChanged(wp->w_buffer)) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", "[+]");
- len += (int)strlen(p + len);
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", "[+]");
}
if (wp->w_buffer->b_p_ro) {
- snprintf(p + len, MAXPATHL - (size_t)len, "%s", _("[RO]"));
- // len += (int)strlen(p + len); // dead assignment
+ plen += snprintf(p + plen, MAXPATHL - (size_t)plen, "%s", _("[RO]"));
}
+ (void)plen;
- int this_ru_col = MAX(ru_col - (Columns - stl_width), (stl_width + 1) / 2);
+ int n = (stl_width + 1) / 2;
+ int this_ru_col = ru_col - (Columns - stl_width);
+ this_ru_col = MAX(this_ru_col, n);
if (this_ru_col <= 1) {
p = "<"; // No room for file name!
- len = 1;
+ plen = 1;
} else {
int i;
// Count total number of display cells.
- int clen = (int)mb_string2cells(p);
+ plen = (int)mb_string2cells(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;
+ for (i = 0; p[i] != NUL && plen >= this_ru_col - 1;
i += utfc_ptr2len(p + i)) {
- clen -= utf_ptr2cells(p + i);
+ plen -= utf_ptr2cells(p + i);
}
- len = clen;
if (i > 0) {
p = p + i - 1;
*p = '<';
- len++;
+ plen++;
}
}
@@ -153,16 +151,17 @@ void win_redr_status(win_T *wp)
int width = grid_line_puts(off, p, -1, attr);
grid_line_fill(off + width, off + this_ru_col, fillchar, attr);
- if (get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL)
- && this_ru_col - len > (int)strlen(NameBuff) + 1) {
- grid_line_puts(off + this_ru_col - (int)strlen(NameBuff) - 1, NameBuff, -1, attr);
+ int NameBufflen = get_keymap_str(wp, "<%s>", NameBuff, MAXPATHL);
+ if (NameBufflen > 0 && this_ru_col - plen > NameBufflen + 1) {
+ grid_line_puts(off + this_ru_col - NameBufflen - 1, NameBuff, -1, attr);
}
win_redr_ruler(wp);
// Draw the 'showcmd' information if 'showcmdloc' == "statusline".
if (p_sc && *p_sloc == 's') {
- const int sc_width = MIN(10, this_ru_col - len - 2);
+ n = this_ru_col - plen - 2; // perform the calculation here so we only do it once
+ const int sc_width = MIN(10, n);
if (sc_width > 0) {
grid_line_puts(off + this_ru_col - sc_width - 1, showcmd_buf, sc_width, attr);
@@ -549,36 +548,40 @@ void win_redr_ruler(win_T *wp)
#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) ? 0 : (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);
+ int bufferlen = vim_snprintf(buffer, RULER_BUF_LEN, "%" PRId64 ",",
+ (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
+ ? 0
+ : (int64_t)wp->w_cursor.lnum);
+ bufferlen += col_print(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen,
+ 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);
+ char rel_pos[RULER_BUF_LEN];
+ int rel_poslen = get_rel_pos(wp, rel_pos, RULER_BUF_LEN);
+ int n1 = bufferlen + vim_strsize(rel_pos);
if (wp->w_status_height == 0 && !is_stl_global) { // can't use last char of screen
- o++;
+ n1++;
}
+
+ int this_ru_col = ru_col - (Columns - width);
// Never use more than half the window/screen width, leave the other half
// for the filename.
- int this_ru_col = MAX(ru_col - (Columns - width), (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 += (int)schar_get(buffer + i, fillchar);
- o++;
- }
- get_rel_pos(wp, buffer + i, RULER_BUF_LEN - i);
+ int n2 = (width + 1) / 2;
+ this_ru_col = MAX(this_ru_col, n2);
+ if (this_ru_col + n1 < width) {
+ // need at least space for rel_pos + NUL
+ while (this_ru_col + n1 < width
+ && RULER_BUF_LEN > bufferlen + rel_poslen + 1) { // +1 for NUL
+ bufferlen += (int)schar_get(buffer + bufferlen, fillchar);
+ n1++;
+ }
+ bufferlen += vim_snprintf(buffer + bufferlen, RULER_BUF_LEN - (size_t)bufferlen,
+ "%s", rel_pos);
}
+ (void)bufferlen;
if (ui_has(kUIMessages) && !part_of_status) {
MAXSIZE_TEMP_ARRAY(content, 1);
@@ -596,11 +599,11 @@ void win_redr_ruler(win_T *wp)
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;
+ for (n1 = 0, n2 = 0; buffer[n1] != NUL; n1 += utfc_ptr2len(buffer + n1)) {
+ n2 += utf_ptr2cells(buffer + n1);
+ if (this_ru_col + n2 > width) {
+ bufferlen = n1;
+ buffer[bufferlen] = NUL;
break;
}
}
@@ -769,8 +772,7 @@ void draw_tabline(void)
if (modified || wincount > 1) {
if (wincount > 1) {
- vim_snprintf(NameBuff, MAXPATHL, "%d", wincount);
- int len = (int)strlen(NameBuff);
+ int len = vim_snprintf(NameBuff, MAXPATHL, "%d", wincount);
if (col + len >= Columns - 3) {
break;
}
@@ -795,7 +797,8 @@ void draw_tabline(void)
len -= ptr2cells(p);
MB_PTR_ADV(p);
}
- len = MIN(len, Columns - col - 1);
+ int n = Columns - col - 1;
+ len = MIN(len, n);
grid_line_puts(col, p, -1, attr);
col += len;
@@ -829,7 +832,8 @@ void draw_tabline(void)
// Draw the 'showcmd' information if 'showcmdloc' == "tabline".
if (p_sc && *p_sloc == 't') {
- const int sc_width = MIN(10, (int)Columns - col - (tabcount > 1) * 3);
+ int n = Columns - col - (tabcount > 1) * 3;
+ const int sc_width = MIN(10, n);
if (sc_width > 0) {
grid_line_puts(Columns - sc_width - (tabcount > 1) * 2,
@@ -923,6 +927,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
static stl_hlrec_t *stl_hltab = NULL;
static StlClickRecord *stl_tabtab = NULL;
static int *stl_separator_locations = NULL;
+ static int curitem = 0;
#define TMPLEN 70
char buf_tmp[TMPLEN];
@@ -1009,7 +1014,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
int groupdepth = 0;
int evaldepth = 0;
- int curitem = 0;
+ // nvim_eval_statusline() can be called from inside a {-expression item so
+ // this may be a recursive call. Keep track of the start index into "stl_items".
+ // During post-processing only treat items filled in a certain recursion level.
+ int evalstart = curitem;
+
bool prevchar_isflag = true;
bool prevchar_isitem = false;
@@ -1153,9 +1162,11 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
}
}
- // 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) {
+ // If the group is longer than it is allowed to be truncate by removing
+ // bytes from the start of the group text. Don't truncate when item is a
+ // 'statuscolumn' fold item to ensure correctness of the mouse clicks.
+ if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid
+ && stl_items[stl_groupitems[groupdepth]].type != HighlightFold) {
// { Determine the number of bytes to remove
// Find the first character that should be included.
@@ -1537,7 +1548,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
// 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);
+ (void)get_rel_pos(wp, buf_tmp, TMPLEN);
str = buf_tmp;
break;
@@ -1565,7 +1576,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
case STL_KEYMAP:
fillable = false;
- if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
+ if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN) > 0) {
str = buf_tmp;
}
break;
@@ -1629,12 +1640,12 @@ stcsign:
break;
}
foldsignitem = curitem;
+ lnum = (linenr_T)get_vim_var_nr(VV_LNUM);
if (fdc > 0) {
schar_T fold_buf[9];
- fill_foldcolumn(wp, stcp->foldinfo, (linenr_T)get_vim_var_nr(VV_LNUM),
- 0, fdc, NULL, fold_buf);
- stl_items[curitem].minwid = -(stcp->use_cul ? HLF_CLF : HLF_FC);
+ fill_foldcolumn(wp, stcp->foldinfo, lnum, 0, fdc, NULL, stcp->fold_vcol, fold_buf);
+ stl_items[curitem].minwid = -(use_cursor_line_highlight(wp, lnum) ? HLF_CLF : HLF_FC);
size_t buflen = 0;
// TODO(bfredl): this is very backwards. we must support schar_T
// being used directly in 'statuscolumn'
@@ -1647,18 +1658,18 @@ stcsign:
for (int i = 0; i < width; i++) {
stl_items[curitem].start = out_p + signlen;
if (fdc == 0) {
- if (stcp->sattrs[i].text[0] && get_vim_var_nr(VV_VIRTNUM) == 0) {
- SignTextAttrs sattrs = stcp->sattrs[i];
- signlen += describe_sign_text(buf_tmp + signlen, sattrs.text);
- stl_items[curitem].minwid = -(stcp->sign_cul_id ? stcp->sign_cul_id : sattrs.hl_id);
+ SignTextAttrs sattr = stcp->sattrs[i];
+ if (sattr.text[0] && get_vim_var_nr(VV_VIRTNUM) == 0) {
+ signlen += describe_sign_text(buf_tmp + signlen, sattr.text);
+ stl_items[curitem].minwid = -(stcp->sign_cul_id ? stcp->sign_cul_id : sattr.hl_id);
} else {
buf_tmp[signlen++] = ' ';
buf_tmp[signlen++] = ' ';
buf_tmp[signlen] = NUL;
- stl_items[curitem].minwid = -(stcp->use_cul ? HLF_CLS : HLF_SC);
+ stl_items[curitem].minwid = 0;
}
}
- stl_items[curitem++].type = Highlight;
+ stl_items[curitem++].type = fdc > 0 ? HighlightFold : HighlightSign;
}
str = buf_tmp;
break;
@@ -1943,7 +1954,9 @@ stcsign:
}
*out_p = NUL;
- int itemcnt = curitem;
+ // Subtract offset from `itemcnt` and restore `curitem` to previous recursion level.
+ int itemcnt = curitem - evalstart;
+ curitem = evalstart;
// Free the format buffer if we allocated it internally
if (usefmt != fmt) {
@@ -1969,7 +1982,7 @@ stcsign:
trunc_p = stl_items[0].start;
item_idx = 0;
- for (int i = 0; i < itemcnt; i++) {
+ for (int i = evalstart; i < itemcnt + evalstart; i++) {
if (stl_items[i].type == Trunc) {
// Truncate at %< stl_items.
trunc_p = stl_items[i].start;
@@ -1999,9 +2012,9 @@ stcsign:
// Ignore any items in the statusline that occur after
// the truncation point
- for (int i = 0; i < itemcnt; i++) {
+ for (int i = evalstart; i < itemcnt + evalstart; i++) {
if (stl_items[i].start > trunc_p) {
- for (int j = i; j < itemcnt; j++) {
+ for (int j = i; j < itemcnt + evalstart; j++) {
if (stl_items[j].type == ClickFunc) {
XFREE_CLEAR(stl_items[j].cmd);
}
@@ -2040,7 +2053,7 @@ stcsign:
// the truncation marker `<` is not counted.
int item_offset = trunc_len - 1;
- for (int i = item_idx; i < itemcnt; i++) {
+ for (int i = item_idx; i < itemcnt + evalstart; 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) {
@@ -2073,7 +2086,7 @@ stcsign:
// 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++) {
+ for (int i = evalstart; i < itemcnt + evalstart; i++) {
if (stl_items[i].type == Separate) {
// Create an array of the start location for each separator mark.
stl_separator_locations[num_separators] = i;
@@ -2098,7 +2111,7 @@ stcsign:
}
for (int item_idx = stl_separator_locations[l] + 1;
- item_idx < itemcnt;
+ item_idx < itemcnt + evalstart;
item_idx++) {
stl_items[item_idx].start += dislocation;
}
@@ -2112,10 +2125,13 @@ stcsign:
if (hltab != NULL) {
*hltab = stl_hltab;
stl_hlrec_t *sp = stl_hltab;
- for (int l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == Highlight) {
+ for (int l = evalstart; l < itemcnt + evalstart; l++) {
+ if (stl_items[l].type == Highlight
+ || stl_items[l].type == HighlightFold || stl_items[l].type == HighlightSign) {
sp->start = stl_items[l].start;
sp->userhl = stl_items[l].minwid;
+ unsigned type = stl_items[l].type;
+ sp->item = type == HighlightSign ? STL_SIGNCOL : type == HighlightFold ? STL_FOLDCOL : 0;
sp++;
}
}
@@ -2130,7 +2146,7 @@ stcsign:
if (tabtab != NULL) {
*tabtab = stl_tabtab;
StlClickRecord *cur_tab_rec = stl_tabtab;
- for (int l = 0; l < itemcnt; l++) {
+ for (int l = evalstart; l < itemcnt + evalstart; l++) {
if (stl_items[l].type == TabPage) {
cur_tab_rec->start = stl_items[l].start;
if (stl_items[l].minwid == 0) {
diff --git a/src/nvim/statusline_defs.h b/src/nvim/statusline_defs.h
index 118f4a257b..83dda1e035 100644
--- a/src/nvim/statusline_defs.h
+++ b/src/nvim/statusline_defs.h
@@ -5,6 +5,50 @@
#include "nvim/fold_defs.h"
#include "nvim/sign_defs.h"
+/// 'statusline' item flags
+typedef enum {
+ STL_FILEPATH = 'f', ///< Path of file in buffer.
+ STL_FULLPATH = 'F', ///< Full path of file in buffer.
+ STL_FILENAME = 't', ///< Last part (tail) of file path.
+ STL_COLUMN = 'c', ///< Column og cursor.
+ STL_VIRTCOL = 'v', ///< Virtual column.
+ STL_VIRTCOL_ALT = 'V', ///< - with 'if different' display.
+ STL_LINE = 'l', ///< Line number of cursor.
+ STL_NUMLINES = 'L', ///< Number of lines in buffer.
+ STL_BUFNO = 'n', ///< Current buffer number.
+ STL_KEYMAP = 'k', ///< 'keymap' when active.
+ STL_OFFSET = 'o', ///< Offset of character under cursor.
+ STL_OFFSET_X = 'O', ///< - in hexadecimal.
+ STL_BYTEVAL = 'b', ///< Byte value of character.
+ STL_BYTEVAL_X = 'B', ///< - in hexadecimal.
+ STL_ROFLAG = 'r', ///< Readonly flag.
+ STL_ROFLAG_ALT = 'R', ///< - other display.
+ STL_HELPFLAG = 'h', ///< Window is showing a help file.
+ STL_HELPFLAG_ALT = 'H', ///< - other display.
+ STL_FILETYPE = 'y', ///< 'filetype'.
+ STL_FILETYPE_ALT = 'Y', ///< - other display.
+ STL_PREVIEWFLAG = 'w', ///< Window is showing the preview buf.
+ STL_PREVIEWFLAG_ALT = 'W', ///< - other display.
+ STL_MODIFIED = 'm', ///< Modified flag.
+ STL_MODIFIED_ALT = 'M', ///< - other display.
+ STL_QUICKFIX = 'q', ///< Quickfix window description.
+ STL_PERCENTAGE = 'p', ///< Percentage through file.
+ STL_ALTPERCENT = 'P', ///< Percentage as TOP BOT ALL or NN%.
+ STL_ARGLISTSTAT = 'a', ///< Argument list status as (x of y).
+ STL_PAGENUM = 'N', ///< Page number (when printing).
+ STL_SHOWCMD = 'S', ///< 'showcmd' buffer
+ STL_FOLDCOL = 'C', ///< Fold column for 'statuscolumn'
+ STL_SIGNCOL = 's', ///< Sign column for 'statuscolumn'
+ STL_VIM_EXPR = '{', ///< Start of expression to substitute.
+ STL_SEPARATE = '=', ///< Separation between alignment sections.
+ STL_TRUNCMARK = '<', ///< Truncation mark if line is too long.
+ STL_USER_HL = '*', ///< Highlight from (User)1..9 or 0.
+ STL_HIGHLIGHT = '#', ///< Highlight name.
+ STL_TABPAGENR = 'T', ///< Tab page label nr.
+ STL_TABCLOSENR = 'X', ///< Tab page close nr.
+ STL_CLICK_FUNC = '@', ///< Click region start.
+} StlFlag;
+
/// Status line click definition
typedef struct {
enum {
@@ -26,27 +70,26 @@ typedef struct {
/// Used for highlighting in the status line.
typedef struct stl_hlrec stl_hlrec_t;
struct stl_hlrec {
- char *start;
- int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID
+ char *start; ///< Where the item starts in the status line output buffer
+ int userhl; ///< 0: no HL, 1-9: User HL, < 0 for syn ID
+ StlFlag item; ///< Item flag belonging to highlight (used for 'statuscolumn')
};
/// Used for building the status line.
typedef struct stl_item stl_item_t;
struct stl_item {
- // Where the item starts in the status line output buffer
- char *start;
- // Function to run for ClickFunc items.
- char *cmd;
- // The minimum width of the item
- int minwid;
- // The maximum width of the item
- int maxwid;
+ char *start; ///< Where the item starts in the status line output buffer
+ char *cmd; ///< Function to run for ClickFunc items
+ int minwid; ///< The minimum width of the item
+ int maxwid; ///< The maximum width of the item
enum {
Normal,
Empty,
Group,
Separate,
Highlight,
+ HighlightSign,
+ HighlightFold,
TabPage,
ClickFunc,
Trunc,
@@ -56,11 +99,10 @@ struct stl_item {
/// Struct to hold info for 'statuscolumn'
typedef struct {
int width; ///< width of the status column
- int num_attr; ///< default highlight attr
int sign_cul_id; ///< cursorline sign highlight id
bool draw; ///< whether to draw the statuscolumn
- bool use_cul; ///< whether to use cursorline attrs
stl_hlrec_t *hlrec; ///< highlight groups
foldinfo_T foldinfo; ///< fold information
+ colnr_T fold_vcol[9]; ///< vcol array filled for fold item
SignTextAttrs *sattrs; ///< sign attributes
} statuscol_T;
diff --git a/src/nvim/strings.c b/src/nvim/strings.c
index 2de65391cc..818e67b32d 100644
--- a/src/nvim/strings.c
+++ b/src/nvim/strings.c
@@ -9,6 +9,8 @@
#include <string.h>
#include "auto/config.h"
+#include "nvim/api/private/defs.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii_defs.h"
#include "nvim/assert_defs.h"
#include "nvim/charset.h"
@@ -20,12 +22,12 @@
#include "nvim/garray.h"
#include "nvim/garray_defs.h"
#include "nvim/gettext_defs.h"
-#include "nvim/globals.h"
#include "nvim/macros_defs.h"
#include "nvim/math.h"
#include "nvim/mbyte.h"
#include "nvim/mbyte_defs.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/plines.h"
@@ -496,20 +498,6 @@ char *vim_strchr(const char *const string, const int c)
}
}
-// Sized version of strchr that can handle embedded NULs.
-// Adjusts n to the new size.
-char *strnchr(const char *p, size_t *n, int c)
-{
- while (*n > 0) {
- if (*p == c) {
- return (char *)p;
- }
- p++;
- (*n)--;
- }
- return NULL;
-}
-
// Sort an array of strings.
static int sort_compare(const void *s1, const void *s2)
diff --git a/src/nvim/strings.h b/src/nvim/strings.h
index c2d078615d..e33be2e076 100644
--- a/src/nvim/strings.h
+++ b/src/nvim/strings.h
@@ -5,7 +5,7 @@
#include "auto/config.h"
#include "klib/kvec.h"
-#include "nvim/api/private/defs.h"
+#include "nvim/api/private/defs.h" // IWYU pragma: keep
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
#include "nvim/os/os_defs.h"
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 03f20047a5..7160c757bb 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -28,7 +28,6 @@
#include "nvim/globals.h"
#include "nvim/hashtab.h"
#include "nvim/hashtab_defs.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/highlight_group.h"
#include "nvim/indent_c.h"
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 3e0bb32391..c676b00986 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -34,7 +34,6 @@
#include "nvim/hashtab.h"
#include "nvim/hashtab_defs.h"
#include "nvim/help.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/input.h"
#include "nvim/insexpand.h"
@@ -451,7 +450,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
curwin->w_cursor.col = saved_fmark.mark.col;
curwin->w_set_curswant = true;
check_cursor(curwin);
- if ((fdo_flags & FDO_TAG) && old_KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) {
foldOpenCursor();
}
@@ -669,7 +668,7 @@ void do_tag(char *tag, int type, int count, int forceit, bool verbose)
if (ask_for_selection) {
// Ask to select a tag from the list.
- int i = prompt_for_number(NULL);
+ int i = prompt_for_input(NULL, 0, false, NULL);
if (i <= 0 || i > num_matches || got_int) {
// no valid choice: don't change anything
if (use_tagstack) {
@@ -2294,17 +2293,17 @@ int find_tags(char *pat, int *num_matches, char ***matchesp, int flags, int minc
// Change the value of 'ignorecase' according to 'tagcase' for the
// duration of this function.
switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) {
- case TC_FOLLOWIC: break;
- case TC_IGNORE:
+ case kOptTcFlagFollowic: break;
+ case kOptTcFlagIgnore:
p_ic = true;
break;
- case TC_MATCH:
+ case kOptTcFlagMatch:
p_ic = false;
break;
- case TC_FOLLOWSCS:
+ case kOptTcFlagFollowscs:
p_ic = ignorecase(pat);
break;
- case TC_SMART:
+ case kOptTcFlagSmart:
p_ic = ignorecase_opt(pat, true, true);
break;
default:
@@ -2846,7 +2845,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
// If it was a CTRL-W CTRL-] command split window now. For ":tab tag"
// open a new tab page.
- if (postponed_split && (swb_flags & (SWB_USEOPEN | SWB_USETAB))) {
+ if (postponed_split && (swb_flags & (kOptSwbFlagUseopen | kOptSwbFlagUsetab))) {
buf_T *const existing_buf = buflist_findname_exp(fname);
if (existing_buf != NULL) {
@@ -3015,7 +3014,7 @@ static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
if (curbuf->b_help) {
set_topline(curwin, curwin->w_cursor.lnum);
}
- if ((fdo_flags & FDO_TAG) && old_KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagTag) && old_KeyTyped) {
foldOpenCursor();
}
}
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index 5ff7f721ba..897c393488 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -52,6 +52,7 @@
#include "nvim/channel.h"
#include "nvim/channel_defs.h"
#include "nvim/cursor.h"
+#include "nvim/cursor_shape.h"
#include "nvim/drawline.h"
#include "nvim/drawscreen.h"
#include "nvim/eval.h"
@@ -64,6 +65,7 @@
#include "nvim/ex_docmd.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"
@@ -91,9 +93,15 @@
#include "nvim/types_defs.h"
#include "nvim/ui.h"
#include "nvim/vim_defs.h"
+#include "nvim/vterm/keyboard.h"
+#include "nvim/vterm/mouse.h"
+#include "nvim/vterm/parser.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
#include "nvim/window.h"
-#include "vterm/vterm.h"
-#include "vterm/vterm_keycodes.h"
typedef struct {
VimState state;
@@ -160,14 +168,20 @@ struct terminal {
int invalid_start, invalid_end; // invalid rows in libvterm screen
struct {
int row, col;
+ int shape;
bool visible;
+ bool blink;
} cursor;
- bool pending_resize; // pending width/height
- bool color_set[16];
+ struct {
+ bool resize; ///< pending width/height
+ bool cursor; ///< pending cursor shape or blink change
+ StringBuilder *send; ///< When there is a pending TermRequest autocommand, block and store input.
+ } pending;
+
+ bool theme_updates; ///< Send a theme update notification when 'bg' changes
- // When there is a pending TermRequest autocommand, block and store input.
- StringBuilder *pending_send;
+ bool color_set[16];
char *selection_buffer; /// libvterm selection buffer
StringBuilder selection; /// Growable array containing full selection data
@@ -181,6 +195,7 @@ static VTermScreenCallbacks vterm_screen_callbacks = {
.movecursor = term_movecursor,
.settermprop = term_settermprop,
.bell = term_bell,
+ .theme = term_theme,
.sb_pushline = term_sb_push, // Called before a line goes offscreen.
.sb_popline = term_sb_pop,
};
@@ -207,24 +222,24 @@ static void emit_termrequest(void **argv)
apply_autocmds_group(EVENT_TERMREQUEST, NULL, NULL, false, AUGROUP_ALL, buf, NULL, &data);
xfree(payload);
- StringBuilder *term_pending_send = term->pending_send;
- term->pending_send = NULL;
+ StringBuilder *term_pending_send = term->pending.send;
+ term->pending.send = NULL;
if (kv_size(*pending_send)) {
terminal_send(term, pending_send->items, pending_send->size);
kv_destroy(*pending_send);
}
if (term_pending_send != pending_send) {
- term->pending_send = term_pending_send;
+ term->pending.send = term_pending_send;
}
xfree(pending_send);
}
static void schedule_termrequest(Terminal *term, char *payload, size_t payload_length)
{
- term->pending_send = xmalloc(sizeof(StringBuilder));
- kv_init(*term->pending_send);
+ term->pending.send = xmalloc(sizeof(StringBuilder));
+ kv_init(*term->pending.send);
multiqueue_put(main_loop.events, emit_termrequest, term, payload, (void *)payload_length,
- term->pending_send);
+ term->pending.send);
}
static int parse_osc8(VTermStringFragment frag, int *attr)
@@ -352,7 +367,7 @@ static void term_output_callback(const char *s, size_t len, void *user_data)
/// Initializes terminal properties, and triggers TermOpen.
///
-/// The PTY process (TerminalOptions.data) was already started by termopen(),
+/// The PTY process (TerminalOptions.data) was already started by jobstart(),
/// via ex_terminal() or the term:// BufReadCmd.
///
/// @param buf Buffer used for presentation of the terminal.
@@ -363,7 +378,7 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
// Create a new terminal instance and configure it
Terminal *term = *termpp = xcalloc(1, sizeof(Terminal));
term->opts = opts;
- term->cursor.visible = true;
+
// Associate the terminal instance with the new buffer
term->buf_handle = buf->handle;
buf->terminal = term;
@@ -387,6 +402,28 @@ void terminal_open(Terminal **termpp, buf_T *buf, TerminalOptions opts)
vterm_state_set_selection_callbacks(state, &vterm_selection_callbacks, term,
term->selection_buffer, SELECTIONBUF_SIZE);
+ VTermValue cursor_shape;
+ switch (shape_table[SHAPE_IDX_TERM].shape) {
+ case SHAPE_BLOCK:
+ cursor_shape.number = VTERM_PROP_CURSORSHAPE_BLOCK;
+ break;
+ case SHAPE_HOR:
+ cursor_shape.number = VTERM_PROP_CURSORSHAPE_UNDERLINE;
+ break;
+ case SHAPE_VER:
+ cursor_shape.number = VTERM_PROP_CURSORSHAPE_BAR_LEFT;
+ break;
+ }
+ vterm_state_set_termprop(state, VTERM_PROP_CURSORSHAPE, &cursor_shape);
+
+ VTermValue cursor_blink;
+ if (shape_table[SHAPE_IDX_TERM].blinkon != 0 && shape_table[SHAPE_IDX_TERM].blinkoff != 0) {
+ cursor_blink.boolean = true;
+ } else {
+ cursor_blink.boolean = false;
+ }
+ vterm_state_set_termprop(state, VTERM_PROP_CURSORBLINK, &cursor_blink);
+
// force a initial refresh of the screen to ensure the buffer will always
// have as many lines as screen rows when refresh_scrollback is called
term->invalid_start = 0;
@@ -565,7 +602,7 @@ void terminal_check_size(Terminal *term)
vterm_set_size(term->vt, height, width);
vterm_screen_flush_damage(term->vts);
- term->pending_resize = true;
+ term->pending.resize = true;
invalidate_terminal(term, -1, -1);
}
@@ -598,12 +635,12 @@ bool terminal_enter(void)
int save_w_p_cuc = curwin->w_p_cuc;
OptInt save_w_p_so = curwin->w_p_so;
OptInt save_w_p_siso = curwin->w_p_siso;
- if (curwin->w_p_cul && curwin->w_p_culopt_flags & CULOPT_NBR) {
+ if (curwin->w_p_cul && curwin->w_p_culopt_flags & kOptCuloptFlagNumber) {
if (strcmp(curwin->w_p_culopt, "number") != 0) {
save_w_p_culopt = curwin->w_p_culopt;
curwin->w_p_culopt = xstrdup("number");
}
- curwin->w_p_culopt_flags = CULOPT_NBR;
+ curwin->w_p_culopt_flags = kOptCuloptFlagNumber;
} else {
curwin->w_p_cul = false;
}
@@ -614,15 +651,25 @@ bool terminal_enter(void)
curwin->w_p_so = 0;
curwin->w_p_siso = 0;
+ // Update the cursor shape table and flush changes to the UI
+ s->term->pending.cursor = true;
+ refresh_cursor(s->term);
+
adjust_topline(s->term, buf, 0); // scroll to end
- // erase the unfocused cursor
- invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
showmode();
curwin->w_redr_status = true; // For mode() in statusline. #8323
- ui_busy_start();
+ redraw_custom_title_later();
+ if (!s->term->cursor.visible) {
+ // Hide cursor if it should be hidden
+ ui_busy_start();
+ }
+ ui_cursor_shape();
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
may_trigger_modechanged();
+ // Tell the terminal it has focus
+ terminal_focus(s->term, true);
+
s->state.execute = terminal_execute;
s->state.check = terminal_check;
state_enter(&s->state);
@@ -634,6 +681,9 @@ bool terminal_enter(void)
RedrawingDisabled = s->save_rd;
apply_autocmds(EVENT_TERMLEAVE, NULL, NULL, false, curbuf);
+ // Restore the terminal cursor to what is set in 'guicursor'
+ (void)parse_shape_opt(SHAPE_CURSOR);
+
if (save_curwin == curwin->handle) { // Else: window was closed.
curwin->w_p_cul = save_w_p_cul;
if (save_w_p_culopt) {
@@ -648,8 +698,9 @@ bool terminal_enter(void)
free_string_option(save_w_p_culopt);
}
- // draw the unfocused cursor
- invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
+ // Tell the terminal it lost focus
+ terminal_focus(s->term, false);
+
if (curbuf->terminal == s->term && !s->close) {
terminal_check_cursor();
}
@@ -658,7 +709,11 @@ bool terminal_enter(void)
} else {
unshowmode(true);
}
- ui_busy_stop();
+ if (!s->term->cursor.visible) {
+ // If cursor was hidden, show it again
+ ui_busy_stop();
+ }
+ ui_cursor_shape();
if (s->close) {
bool wipe = s->term->buf_handle != 0;
s->term->destroy = true;
@@ -728,21 +783,32 @@ static int terminal_execute(VimState *state, int key)
{
TerminalState *s = (TerminalState *)state;
- switch (key) {
+ // Check for certain control keys like Ctrl-C and Ctrl-\. We still send the
+ // unmerged key and modifiers to the terminal.
+ int tmp_mod_mask = mod_mask;
+ int mod_key = merge_modifiers(key, &tmp_mod_mask);
+
+ switch (mod_key) {
case K_LEFTMOUSE:
case K_LEFTDRAG:
case K_LEFTRELEASE:
- case K_MOUSEMOVE:
case K_MIDDLEMOUSE:
case K_MIDDLEDRAG:
case K_MIDDLERELEASE:
case K_RIGHTMOUSE:
case K_RIGHTDRAG:
case K_RIGHTRELEASE:
+ case K_X1MOUSE:
+ case K_X1DRAG:
+ case K_X1RELEASE:
+ case K_X2MOUSE:
+ case K_X2DRAG:
+ case K_X2RELEASE:
case K_MOUSEDOWN:
case K_MOUSEUP:
case K_MOUSELEFT:
case K_MOUSERIGHT:
+ case K_MOUSEMOVE:
if (send_mouse_event(s->term, key)) {
return 0;
}
@@ -786,13 +852,13 @@ static int terminal_execute(VimState *state, int key)
FALLTHROUGH;
default:
- if (key == Ctrl_C) {
+ if (mod_key == Ctrl_C) {
// terminal_enter() always sets `mapped_ctrl_c` to avoid `got_int`. 8eeda7169aa4
// But `got_int` may be set elsewhere, e.g. by interrupt() or an autocommand,
// so ensure that it is cleared.
got_int = false;
}
- if (key == Ctrl_BSL && !s->got_bsl) {
+ if (mod_key == Ctrl_BSL && !s->got_bsl) {
s->got_bsl = true;
break;
}
@@ -809,6 +875,19 @@ static int terminal_execute(VimState *state, int key)
return 0;
}
if (s->term != curbuf->terminal) {
+ // Active terminal buffer changed, flush terminal's cursor state to the UI
+ curbuf->terminal->pending.cursor = true;
+
+ if (!s->term->cursor.visible) {
+ // If cursor was hidden, show it again
+ ui_busy_stop();
+ }
+
+ if (!curbuf->terminal->cursor.visible) {
+ // Hide cursor if it should be hidden
+ ui_busy_start();
+ }
+
invalidate_terminal(s->term, s->term->cursor.row, s->term->cursor.row + 1);
invalidate_terminal(curbuf->terminal,
curbuf->terminal->cursor.row,
@@ -856,8 +935,8 @@ static void terminal_send(Terminal *term, const char *data, size_t size)
if (term->closed) {
return;
}
- if (term->pending_send) {
- kv_concat_len(*term->pending_send, data, size);
+ if (term->pending.send) {
+ kv_concat_len(*term->pending.send, data, size);
return;
}
term->opts.write_cb(data, size, term->opts.data);
@@ -868,28 +947,28 @@ static bool is_filter_char(int c)
unsigned flag = 0;
switch (c) {
case 0x08:
- flag = TPF_BS;
+ flag = kOptTpfFlagBS;
break;
case 0x09:
- flag = TPF_HT;
+ flag = kOptTpfFlagHT;
break;
case 0x0A:
case 0x0D:
break;
case 0x0C:
- flag = TPF_FF;
+ flag = kOptTpfFlagFF;
break;
case 0x1b:
- flag = TPF_ESC;
+ flag = kOptTpfFlagESC;
break;
case 0x7F:
- flag = TPF_DEL;
+ flag = kOptTpfFlagDEL;
break;
default:
if (c < ' ') {
- flag = TPF_C0;
+ flag = kOptTpfFlagC0;
} else if (c >= 0x80 && c <= 0x9F) {
- flag = TPF_C1;
+ flag = kOptTpfFlagC1;
}
}
return !!(tpf_flags & flag);
@@ -946,9 +1025,9 @@ static void terminal_send_key(Terminal *term, int c)
c = Ctrl_AT;
}
- VTermKey key = convert_key(c, &mod);
+ VTermKey key = convert_key(&c, &mod);
- if (key) {
+ if (key != VTERM_KEY_NONE) {
vterm_keyboard_key(term->vt, key, mod);
} else if (!IS_SPECIAL(c)) {
vterm_keyboard_unichar(term->vt, (uint32_t)c, mod);
@@ -1062,14 +1141,6 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *te
attr_id = hl_combine_attr(attr_id, cell.uri);
}
- if (term->cursor.visible && term->cursor.row == row
- && term->cursor.col == col) {
- attr_id = hl_combine_attr(attr_id,
- is_focused(term) && wp == curwin
- ? win_hl_attr(wp, HLF_TERM)
- : win_hl_attr(wp, HLF_TERMNC));
- }
-
term_attrs[col] = attr_id;
}
}
@@ -1084,6 +1155,31 @@ bool terminal_running(const Terminal *term)
return !term->closed;
}
+void terminal_notify_theme(Terminal *term, bool dark)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!term->theme_updates) {
+ return;
+ }
+
+ char buf[10];
+ ssize_t ret = snprintf(buf, sizeof(buf), "\x1b[997;%cn", dark ? '1' : '2');
+ assert(ret > 0);
+ assert((size_t)ret <= sizeof(buf));
+ terminal_send(term, buf, (size_t)ret);
+}
+
+static void terminal_focus(const Terminal *term, bool focus)
+ FUNC_ATTR_NONNULL_ALL
+{
+ VTermState *state = vterm_obtain_state(term->vt);
+ if (focus) {
+ vterm_state_focus_in(state);
+ } else {
+ vterm_state_focus_out(state);
+ }
+}
+
// }}}
// libvterm callbacks {{{
@@ -1105,8 +1201,7 @@ static int term_movecursor(VTermPos new_pos, VTermPos old_pos, int visible, void
Terminal *term = data;
term->cursor.row = new_pos.row;
term->cursor.col = new_pos.col;
- invalidate_terminal(term, old_pos.row, old_pos.row + 1);
- invalidate_terminal(term, new_pos.row, new_pos.row + 1);
+ invalidate_terminal(term, -1, -1);
return 1;
}
@@ -1134,8 +1229,17 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
break;
case VTERM_PROP_CURSORVISIBLE:
+ if (is_focused(term)) {
+ if (!val->boolean && term->cursor.visible) {
+ // Hide the cursor
+ ui_busy_start();
+ } else if (val->boolean && !term->cursor.visible) {
+ // Unhide the cursor
+ ui_busy_stop();
+ }
+ invalidate_terminal(term, -1, -1);
+ }
term->cursor.visible = val->boolean;
- invalidate_terminal(term, term->cursor.row, term->cursor.row + 1);
break;
case VTERM_PROP_TITLE: {
@@ -1171,6 +1275,22 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
term->forward_mouse = (bool)val->number;
break;
+ case VTERM_PROP_CURSORBLINK:
+ term->cursor.blink = val->boolean;
+ term->pending.cursor = true;
+ invalidate_terminal(term, -1, -1);
+ break;
+
+ case VTERM_PROP_CURSORSHAPE:
+ term->cursor.shape = val->number;
+ term->pending.cursor = true;
+ invalidate_terminal(term, -1, -1);
+ break;
+
+ case VTERM_PROP_THEMEUPDATES:
+ term->theme_updates = val->boolean;
+ break;
+
default:
return 0;
}
@@ -1181,7 +1301,15 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data)
/// Called when the terminal wants to ring the system bell.
static int term_bell(void *data)
{
- vim_beep(BO_TERM);
+ vim_beep(kOptBoFlagTerm);
+ return 1;
+}
+
+/// Called when the terminal wants to query the system theme.
+static int term_theme(bool *dark, void *data)
+ FUNC_ATTR_NONNULL_ALL
+{
+ *dark = (*p_bg == 'd');
return 1;
}
@@ -1266,7 +1394,7 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data)
// copy to vterm state
memcpy(cells, sbrow->cells, sizeof(cells[0]) * cols_to_copy);
for (size_t col = cols_to_copy; col < (size_t)cols; col++) {
- cells[col].chars[0] = 0;
+ cells[col].schar = 0;
cells[col].width = 1;
}
@@ -1327,19 +1455,23 @@ static int term_selection_set(VTermSelectionMask mask, VTermStringFragment frag,
// }}}
// input handling {{{
-static void convert_modifiers(int key, VTermModifier *statep)
+static void convert_modifiers(int *key, VTermModifier *statep)
{
if (mod_mask & MOD_MASK_SHIFT) {
*statep |= VTERM_MOD_SHIFT;
}
if (mod_mask & MOD_MASK_CTRL) {
*statep |= VTERM_MOD_CTRL;
+ if (!(mod_mask & MOD_MASK_SHIFT) && *key >= 'A' && *key <= 'Z') {
+ // vterm interprets CTRL+A as SHIFT+CTRL, change to CTRL+a
+ *key += ('a' - 'A');
+ }
}
if (mod_mask & MOD_MASK_ALT) {
*statep |= VTERM_MOD_ALT;
}
- switch (key) {
+ switch (*key) {
case K_S_TAB:
case K_S_UP:
case K_S_DOWN:
@@ -1371,11 +1503,11 @@ static void convert_modifiers(int key, VTermModifier *statep)
}
}
-static VTermKey convert_key(int key, VTermModifier *statep)
+static VTermKey convert_key(int *key, VTermModifier *statep)
{
convert_modifiers(key, statep);
- switch (key) {
+ switch (*key) {
case K_BS:
return VTERM_KEY_BACKSPACE;
case K_S_TAB:
@@ -1678,8 +1810,6 @@ static bool send_mouse_event(Terminal *term, int c)
pressed = true; FALLTHROUGH;
case K_LEFTRELEASE:
button = 1; break;
- case K_MOUSEMOVE:
- button = 0; break;
case K_MIDDLEDRAG:
case K_MIDDLEMOUSE:
pressed = true; FALLTHROUGH;
@@ -1690,6 +1820,16 @@ static bool send_mouse_event(Terminal *term, int c)
pressed = true; FALLTHROUGH;
case K_RIGHTRELEASE:
button = 3; break;
+ case K_X1DRAG:
+ case K_X1MOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_X1RELEASE:
+ button = 8; break;
+ case K_X2DRAG:
+ case K_X2MOUSE:
+ pressed = true; FALLTHROUGH;
+ case K_X2RELEASE:
+ button = 9; break;
case K_MOUSEDOWN:
pressed = true; button = 4; break;
case K_MOUSEUP:
@@ -1698,12 +1838,14 @@ static bool send_mouse_event(Terminal *term, int c)
pressed = true; button = 7; break;
case K_MOUSERIGHT:
pressed = true; button = 6; break;
+ case K_MOUSEMOVE:
+ button = 0; break;
default:
return false;
}
VTermModifier mod = VTERM_MOD_NONE;
- convert_modifiers(c, &mod);
+ convert_modifiers(&c, &mod);
mouse_action(term, button, row, col - offset, pressed, mod);
return false;
}
@@ -1776,12 +1918,8 @@ static void fetch_row(Terminal *term, int row, int end_col)
while (col < end_col) {
VTermScreenCell cell;
fetch_cell(term, row, col, &cell);
- if (cell.chars[0]) {
- int cell_len = 0;
- for (int i = 0; i < VTERM_MAX_CHARS_PER_CELL && cell.chars[i]; i++) {
- cell_len += utf_char2bytes((int)cell.chars[i], ptr + cell_len);
- }
- ptr += cell_len;
+ if (cell.schar) {
+ schar_get_adv(&ptr, cell.schar);
line_len = (size_t)(ptr - term->textbuf);
} else {
*ptr++ = ' ';
@@ -1802,7 +1940,7 @@ static bool fetch_cell(Terminal *term, int row, int col, VTermScreenCell *cell)
} else {
// fill the pointer with an empty cell
*cell = (VTermScreenCell) {
- .chars = { 0 },
+ .schar = 0,
.width = 1,
};
return false;
@@ -1848,12 +1986,49 @@ static void refresh_terminal(Terminal *term)
refresh_size(term, buf);
refresh_scrollback(term, buf);
refresh_screen(term, buf);
+ refresh_cursor(term);
aucmd_restbuf(&aco);
int ml_added = buf->b_ml.ml_line_count - ml_before;
adjust_topline(term, buf, ml_added);
}
+static void refresh_cursor(Terminal *term)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (!is_focused(term) || !term->pending.cursor) {
+ return;
+ }
+ term->pending.cursor = false;
+
+ if (term->cursor.blink) {
+ // For the TUI, this value doesn't actually matter, as long as it's non-zero. The terminal
+ // emulator dictates the blink frequency, not the application.
+ // For GUIs we just pick an arbitrary value, for now.
+ shape_table[SHAPE_IDX_TERM].blinkon = 500;
+ shape_table[SHAPE_IDX_TERM].blinkoff = 500;
+ } else {
+ shape_table[SHAPE_IDX_TERM].blinkon = 0;
+ shape_table[SHAPE_IDX_TERM].blinkoff = 0;
+ }
+
+ switch (term->cursor.shape) {
+ case VTERM_PROP_CURSORSHAPE_BLOCK:
+ shape_table[SHAPE_IDX_TERM].shape = SHAPE_BLOCK;
+ break;
+ case VTERM_PROP_CURSORSHAPE_UNDERLINE:
+ shape_table[SHAPE_IDX_TERM].shape = SHAPE_HOR;
+ shape_table[SHAPE_IDX_TERM].percentage = 20;
+ break;
+ case VTERM_PROP_CURSORSHAPE_BAR_LEFT:
+ shape_table[SHAPE_IDX_TERM].shape = SHAPE_VER;
+ shape_table[SHAPE_IDX_TERM].percentage = 25;
+ break;
+ }
+
+ ui_mode_info_set();
+}
+
/// Calls refresh_terminal() on all invalidated_terminals.
static void refresh_timer_cb(TimeWatcher *watcher, void *data)
{
@@ -1874,11 +2049,11 @@ static void refresh_timer_cb(TimeWatcher *watcher, void *data)
static void refresh_size(Terminal *term, buf_T *buf)
{
- if (!term->pending_resize || term->closed) {
+ if (!term->pending.resize || term->closed) {
return;
}
- term->pending_resize = false;
+ term->pending.resize = false;
int width, height;
vterm_get_size(term->vt, &height, &width);
term->invalid_start = 0;
diff --git a/src/nvim/textformat.c b/src/nvim/textformat.c
index 06b3aa0411..ca2829fecb 100644
--- a/src/nvim/textformat.c
+++ b/src/nvim/textformat.c
@@ -18,6 +18,7 @@
#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -35,6 +36,7 @@
#include "nvim/strings.h"
#include "nvim/textformat.h"
#include "nvim/textobject.h"
+#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim_defs.h"
#include "nvim/window.h"
@@ -1049,12 +1051,18 @@ void format_lines(linenr_T line_count, bool avoid_fex)
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;
+ // Cursor shape may have been updated (e.g. by :normal) in insertchar(),
+ // so it needs to be updated here.
+ ui_cursor_shape();
+
second_indent = -1;
// at end of par.: need to set indent of next par.
need_set_indent = is_end_par;
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
index 45978765bf..e3b3bba7c1 100644
--- a/src/nvim/textobject.c
+++ b/src/nvim/textobject.c
@@ -3,7 +3,6 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
-#include <string.h>
#include "nvim/ascii_defs.h"
#include "nvim/buffer_defs.h"
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
index 98dd7b4b45..1f73f2d135 100644
--- a/src/nvim/tui/input.c
+++ b/src/nvim/tui/input.c
@@ -8,7 +8,6 @@
#include "nvim/api/private/helpers.h"
#include "nvim/event/loop.h"
#include "nvim/event/rstream.h"
-#include "nvim/event/stream.h"
#include "nvim/macros_defs.h"
#include "nvim/main.h"
#include "nvim/map_defs.h"
@@ -160,12 +159,16 @@ void tinput_init(TermInput *input, Loop *loop)
// initialize a timer handle for handling ESC with libtermkey
uv_timer_init(&loop->uv, &input->timer_handle);
input->timer_handle.data = input;
+
+ uv_timer_init(&loop->uv, &input->bg_query_timer);
+ input->bg_query_timer.data = input;
}
void tinput_destroy(TermInput *input)
{
map_destroy(int, &kitty_key_map);
uv_close((uv_handle_t *)&input->timer_handle, NULL);
+ uv_close((uv_handle_t *)&input->bg_query_timer, NULL);
rstream_may_close(&input->read_stream);
termkey_destroy(input->tk);
}
@@ -179,6 +182,7 @@ void tinput_stop(TermInput *input)
{
rstream_stop(&input->read_stream);
uv_timer_stop(&input->timer_handle);
+ uv_timer_stop(&input->bg_query_timer);
}
static void tinput_done_event(void **argv)
@@ -383,6 +387,10 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key)
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Middle");
} else if (button == 3) {
len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Right");
+ } else if (button == 8) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "X1");
+ } else if (button == 9) {
+ len += (size_t)snprintf(buf + len, sizeof(buf) - len, "X2");
}
switch (ev) {
@@ -427,6 +435,15 @@ static void tk_getkeys(TermInput *input, bool force)
TermKeyResult result;
while ((result = tk_getkey(input->tk, &key, force)) == TERMKEY_RES_KEY) {
+ // Only press and repeat events are handled for now
+ switch (key.event) {
+ case TERMKEY_EVENT_PRESS:
+ case TERMKEY_EVENT_REPEAT:
+ break;
+ default:
+ continue;
+ }
+
if (key.type == TERMKEY_TYPE_UNICODE && !key.modifiers) {
forward_simple_utf8(input, &key);
} else if (key.type == TERMKEY_TYPE_UNICODE
@@ -474,6 +491,13 @@ static void tinput_timer_cb(uv_timer_t *handle)
tinput_flush(input);
}
+static void bg_query_timer_cb(uv_timer_t *handle)
+ FUNC_ATTR_NONNULL_ALL
+{
+ TermInput *input = handle->data;
+ tui_query_bg_color(input->tui_data);
+}
+
/// Handle focus events.
///
/// If the upcoming sequence of bytes in the input stream matches the termcode
@@ -660,6 +684,33 @@ static void handle_unknown_csi(TermInput *input, const TermKeyKey *key)
}
}
break;
+ case 'n':
+ // Device Status Report (DSR)
+ if (nparams == 2) {
+ int args[2];
+ for (size_t i = 0; i < ARRAY_SIZE(args); i++) {
+ if (termkey_interpret_csi_param(params[i], &args[i], NULL, NULL) != TERMKEY_RES_KEY) {
+ return;
+ }
+ }
+
+ if (args[0] == 997) {
+ // Theme update notification
+ // https://github.com/contour-terminal/contour/blob/master/docs/vt-extensions/color-palette-update-notifications.md
+ // The second argument tells us whether the OS theme is set to light
+ // mode or dark mode, but all we care about is the background color of
+ // the terminal emulator. We query for that with OSC 11 and the response
+ // is handled by the autocommand created in _defaults.lua. The terminal
+ // may send us multiple notifications all at once so we use a timer to
+ // coalesce the queries.
+ if (uv_timer_get_due_in(&input->bg_query_timer) > 0) {
+ return;
+ }
+
+ uv_timer_start(&input->bg_query_timer, bg_query_timer_cb, 100, 0);
+ }
+ }
+ break;
default:
break;
}
diff --git a/src/nvim/tui/input.h b/src/nvim/tui/input.h
index 4c2baf908e..e48982f9a4 100644
--- a/src/nvim/tui/input.h
+++ b/src/nvim/tui/input.h
@@ -1,6 +1,7 @@
#pragma once
#include <stdbool.h>
+#include <stddef.h>
#include <stdint.h>
#include <uv.h>
@@ -32,6 +33,7 @@ typedef struct {
TermKey *tk;
TermKey_Terminfo_Getstr_Hook *tk_ti_hook_fn; ///< libtermkey terminfo hook
uv_timer_t timer_handle;
+ uv_timer_t bg_query_timer; ///< timer used to batch background color queries
Loop *loop;
RStream read_stream;
TUIData *tui_data;
diff --git a/src/nvim/tui/termkey/driver-csi.c b/src/nvim/tui/termkey/driver-csi.c
index 28c7eaccfd..d427be50ff 100644
--- a/src/nvim/tui/termkey/driver-csi.c
+++ b/src/nvim/tui/termkey/driver-csi.c
@@ -1,11 +1,10 @@
#include <assert.h>
-#include <stdio.h>
+#include <stdint.h>
#include <string.h>
#include "nvim/memory.h"
#include "nvim/tui/termkey/driver-csi.h"
#include "nvim/tui/termkey/termkey-internal.h"
-#include "nvim/tui/termkey/termkey.h"
#include "nvim/tui/termkey/termkey_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -32,11 +31,20 @@ static TermKeyResult handle_csi_ss3_full(TermKey *tk, TermKeyKey *key, int cmd,
if (nparams > 1 && params[1].param != NULL) {
int arg = 0;
- result = termkey_interpret_csi_param(params[1], &arg, NULL, NULL);
+ int subparam = 0;
+ size_t nsubparams = 1;
+ result = termkey_interpret_csi_param(params[1], &arg, &subparam, &nsubparams);
if (result != TERMKEY_RES_KEY) {
return result;
}
+ if (nsubparams > 0) {
+ key->event = parse_key_event(subparam);
+ if (key->event == TERMKEY_EVENT_UNKNOWN) {
+ return TERMKEY_RES_NONE;
+ }
+ }
+
key->modifiers = arg - 1;
} else {
key->modifiers = 0;
@@ -104,11 +112,20 @@ static TermKeyResult handle_csifunc(TermKey *tk, TermKeyKey *key, int cmd, TermK
int args[3];
if (nparams > 1 && params[1].param != NULL) {
- result = termkey_interpret_csi_param(params[1], &args[1], NULL, NULL);
+ int subparam = 0;
+ size_t nsubparams = 1;
+ result = termkey_interpret_csi_param(params[1], &args[1], &subparam, &nsubparams);
if (result != TERMKEY_RES_KEY) {
return result;
}
+ if (nsubparams > 0) {
+ key->event = parse_key_event(subparam);
+ if (key->event == TERMKEY_EVENT_UNKNOWN) {
+ return TERMKEY_RES_NONE;
+ }
+ }
+
key->modifiers = args[1] - 1;
} else {
key->modifiers = 0;
@@ -178,9 +195,11 @@ static TermKeyResult handle_csi_u(TermKey *tk, TermKeyKey *key, int cmd, TermKey
return TERMKEY_RES_ERROR;
}
- if (nsubparams > 0 && subparam != 1) {
- // Not a press event. Ignore for now
- return TERMKEY_RES_NONE;
+ if (nsubparams > 0) {
+ key->event = parse_key_event(subparam);
+ if (key->event == TERMKEY_EVENT_UNKNOWN) {
+ return TERMKEY_RES_NONE;
+ }
}
key->modifiers = args[1] - 1;
@@ -308,6 +327,12 @@ TermKeyResult termkey_interpret_mouse(TermKey *tk, const TermKeyKey *key, TermKe
btn = code + 4 - 64;
break;
+ case 128:
+ case 129:
+ *event = drag ? TERMKEY_MOUSE_DRAG : TERMKEY_MOUSE_PRESS;
+ btn = code + 8 - 128;
+ break;
+
default:
*event = TERMKEY_MOUSE_UNKNOWN;
}
@@ -419,6 +444,20 @@ TermKeyResult termkey_interpret_modereport(TermKey *tk, const TermKeyKey *key, i
#define CHARAT(i) (tk->buffer[tk->buffstart + (i)])
+static TermKeyEvent parse_key_event(int n)
+{
+ switch (n) {
+ case 1:
+ return TERMKEY_EVENT_PRESS;
+ case 2:
+ return TERMKEY_EVENT_REPEAT;
+ case 3:
+ return TERMKEY_EVENT_RELEASE;
+ default:
+ return TERMKEY_EVENT_UNKNOWN;
+ }
+}
+
static TermKeyResult parse_csi(TermKey *tk, size_t introlen, size_t *csi_len,
TermKeyCsiParam params[], size_t *nargs, unsigned *commandp)
{
@@ -529,7 +568,7 @@ TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, in
if (c == ':') {
if (length == 0) {
*paramp = arg;
- } else {
+ } else if (subparams != NULL) {
subparams[length - 1] = arg;
}
@@ -544,7 +583,7 @@ TermKeyResult termkey_interpret_csi_param(TermKeyCsiParam param, int *paramp, in
if (length == 0) {
*paramp = arg;
- } else {
+ } else if (subparams != NULL) {
subparams[length - 1] = arg;
}
diff --git a/src/nvim/tui/termkey/driver-csi.h b/src/nvim/tui/termkey/driver-csi.h
index 0abd8b5c2e..644cc9d58b 100644
--- a/src/nvim/tui/termkey/driver-csi.h
+++ b/src/nvim/tui/termkey/driver-csi.h
@@ -1,6 +1,6 @@
#pragma once
-#include "nvim/tui/termkey/termkey_defs.h"
+#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/termkey/driver-csi.h.generated.h"
diff --git a/src/nvim/tui/termkey/driver-ti.c b/src/nvim/tui/termkey/driver-ti.c
index 745ee9902f..e402f93e93 100644
--- a/src/nvim/tui/termkey/driver-ti.c
+++ b/src/nvim/tui/termkey/driver-ti.c
@@ -1,16 +1,16 @@
-#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <sys/types.h>
#include <unibilium.h>
+#include <uv.h>
#include "nvim/memory.h"
#include "nvim/tui/termkey/driver-ti.h"
#include "nvim/tui/termkey/termkey-internal.h"
-#include "nvim/tui/termkey/termkey.h"
+#include "nvim/tui/termkey/termkey_defs.h"
#ifndef _WIN32
# include <unistd.h>
diff --git a/src/nvim/tui/termkey/driver-ti.h b/src/nvim/tui/termkey/driver-ti.h
index df9bd72d5b..6dbcb11344 100644
--- a/src/nvim/tui/termkey/driver-ti.h
+++ b/src/nvim/tui/termkey/driver-ti.h
@@ -1,6 +1,6 @@
#pragma once
-#include "nvim/tui/termkey/termkey_defs.h"
+#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/termkey/driver-ti.h.generated.h"
diff --git a/src/nvim/tui/termkey/termkey-internal.h b/src/nvim/tui/termkey/termkey-internal.h
index 107591f950..97fae939c5 100644
--- a/src/nvim/tui/termkey/termkey-internal.h
+++ b/src/nvim/tui/termkey/termkey-internal.h
@@ -47,7 +47,7 @@ struct TermKey {
int canonflags;
unsigned char *buffer;
size_t buffstart; // First offset in buffer
- size_t buffcount; // NUMBER of entires valid in buffer
+ size_t buffcount; // NUMBER of entries valid in buffer
size_t buffsize; // Total malloc'ed size
size_t hightide; // Position beyond buffstart at which peekkey() should next start
// normally 0, but see also termkey_interpret_csi
diff --git a/src/nvim/tui/termkey/termkey.c b/src/nvim/tui/termkey/termkey.c
index e6440118f3..eabde2f9f7 100644
--- a/src/nvim/tui/termkey/termkey.c
+++ b/src/nvim/tui/termkey/termkey.c
@@ -1,9 +1,9 @@
#include <ctype.h>
#include <errno.h>
-#include <stdbool.h>
#include <stdio.h>
#include <string.h>
+#include "nvim/macros_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/tui/termkey/driver-csi.h"
@@ -13,9 +13,10 @@
#include "nvim/tui/termkey/termkey_defs.h"
#ifndef _WIN32
-# include <poll.h>
-# include <strings.h>
-# include <unistd.h>
+// Include these directly instead of <termios.h> which is system-dependent. #31704
+# include <poll.h> // IWYU pragma: keep
+# include <strings.h> // IWYU pragma: keep
+# include <unistd.h> // IWYU pragma: keep
#else
# include <io.h>
#endif
@@ -633,40 +634,13 @@ static void eat_bytes(TermKey *tk, size_t count)
tk->buffcount -= count;
}
-// TODO(dundargoc): we should be able to replace this with utf_char2bytes from mbyte.c
int fill_utf8(int codepoint, char *str)
{
- int nbytes = utf_char2len(codepoint);
-
+ int nbytes = utf_char2bytes(codepoint, str);
str[nbytes] = 0;
-
- // This is easier done backwards
- int b = nbytes;
- while (b > 1) {
- b--;
- str[b] = (char)0x80 | (codepoint & 0x3f);
- codepoint >>= 6;
- }
-
- switch (nbytes) {
- case 1:
- str[0] = (codepoint & 0x7f); break;
- case 2:
- str[0] = (char)0xc0 | (codepoint & 0x1f); break;
- case 3:
- str[0] = (char)0xe0 | (codepoint & 0x0f); break;
- case 4:
- str[0] = (char)0xf0 | (codepoint & 0x07); break;
- case 5:
- str[0] = (char)0xf8 | (codepoint & 0x03); break;
- case 6:
- str[0] = (char)0xfc | (codepoint & 0x01); break;
- }
-
return nbytes;
}
-#define UTF8_INVALID 0xFFFD
static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp, size_t *nbytep)
{
unsigned nbytes;
@@ -680,7 +654,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
return TERMKEY_RES_KEY;
} else if (b0 < 0xc0) {
// Starts with a continuation byte - that's not right
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
*nbytep = 1;
return TERMKEY_RES_KEY;
} else if (b0 < 0xe0) {
@@ -699,7 +673,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
nbytes = 6;
*cp = b0 & 0x01;
} else {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
*nbytep = 1;
return TERMKEY_RES_KEY;
}
@@ -713,7 +687,7 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
cb = bytes[b];
if (cb < 0x80 || cb >= 0xc0) {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
*nbytep = b;
return TERMKEY_RES_KEY;
}
@@ -724,14 +698,14 @@ static TermKeyResult parse_utf8(const unsigned char *bytes, size_t len, int *cp,
// Check for overlong sequences
if ((int)nbytes > utf_char2len(*cp)) {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
}
// Check for UTF-16 surrogates or invalid *cps
if ((*cp >= 0xD800 && *cp <= 0xDFFF)
|| *cp == 0xFFFE
|| *cp == 0xFFFF) {
- *cp = UTF8_INVALID;
+ *cp = UNICODE_INVALID;
}
*nbytep = nbytes;
@@ -833,6 +807,9 @@ static TermKeyResult peekkey(TermKey *tk, TermKeyKey *key, int force, size_t *nb
return TERMKEY_RES_ERROR;
}
+ // Press is the default event type.
+ key->event = TERMKEY_EVENT_PRESS;
+
#ifdef DEBUG
fprintf(stderr, "getkey(force=%d): buffer ", force);
print_buffer(tk);
@@ -958,9 +935,9 @@ static TermKeyResult peekkey_simple(TermKey *tk, TermKeyKey *key, int force, siz
if (res == TERMKEY_RES_AGAIN && force) {
// There weren't enough bytes for a complete UTF-8 sequence but caller
// demands an answer. About the best thing we can do here is eat as many
- // bytes as we have, and emit a UTF8_INVALID. If the remaining bytes
+ // bytes as we have, and emit a UNICODE_INVALID. If the remaining bytes
// arrive later, they'll be invalid too.
- codepoint = UTF8_INVALID;
+ codepoint = UNICODE_INVALID;
*nbytep = tk->buffcount;
res = TERMKEY_RES_KEY;
}
diff --git a/src/nvim/tui/termkey/termkey.h b/src/nvim/tui/termkey/termkey.h
index 21ed141346..5cf9894cac 100644
--- a/src/nvim/tui/termkey/termkey.h
+++ b/src/nvim/tui/termkey/termkey.h
@@ -1,9 +1,9 @@
#pragma once
-#include <stdint.h>
-#include <stdlib.h>
+#include <stdint.h> // IWYU pragma: keep
+#include <stdlib.h> // IWYU pragma: keep
-#include "nvim/tui/termkey/termkey_defs.h"
+#include "nvim/tui/termkey/termkey_defs.h" // IWYU pragma: keep
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/termkey/termkey.h.generated.h"
diff --git a/src/nvim/tui/termkey/termkey_defs.h b/src/nvim/tui/termkey/termkey_defs.h
index 7c218ba7c2..87d3f63447 100644
--- a/src/nvim/tui/termkey/termkey_defs.h
+++ b/src/nvim/tui/termkey/termkey_defs.h
@@ -123,6 +123,13 @@ typedef enum {
TERMKEY_MOUSE_RELEASE,
} TermKeyMouseEvent;
+typedef enum {
+ TERMKEY_EVENT_UNKNOWN,
+ TERMKEY_EVENT_PRESS,
+ TERMKEY_EVENT_REPEAT,
+ TERMKEY_EVENT_RELEASE,
+} TermKeyEvent;
+
enum {
TERMKEY_KEYMOD_SHIFT = 1 << 0,
TERMKEY_KEYMOD_ALT = 1 << 1,
@@ -163,6 +170,8 @@ typedef struct {
int modifiers;
+ TermKeyEvent event;
+
// Any Unicode character can be UTF-8 encoded in no more than 6 bytes, plus
// terminating NUL
char utf8[7];
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 2839a665da..31f95a1006 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -1,9 +1,9 @@
// Terminal UI functions. Invoked (by ui_client.c) on the UI process.
#include <assert.h>
+#include <inttypes.h>
#include <signal.h>
#include <stdbool.h>
-#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -22,8 +22,6 @@
#include "nvim/event/stream.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
-#include "nvim/grid_defs.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/log.h"
#include "nvim/macros_defs.h"
@@ -107,7 +105,7 @@ struct TUIData {
bool busy, is_invisible, want_invisible;
bool cork, overflow;
bool set_cursor_color_as_str;
- bool cursor_color_changed;
+ bool cursor_has_color;
bool is_starting;
bool did_set_grapheme_cluster_mode;
FILE *screenshot;
@@ -241,16 +239,19 @@ void tui_handle_term_mode(TUIData *tui, TermMode mode, TermModeState state)
tui->unibi_ext.sync = (int)unibi_add_ext_str(tui->ut, "Sync",
"\x1b[?2026%?%p1%{1}%-%tl%eh%;");
break;
- case kTermModeResizeEvents:
- signal_watcher_stop(&tui->winch_handle);
- tui_set_term_mode(tui, mode, true);
- break;
case kTermModeGraphemeClusters:
if (!is_set) {
tui_set_term_mode(tui, mode, true);
tui->did_set_grapheme_cluster_mode = true;
}
break;
+ case kTermModeThemeUpdates:
+ tui_set_term_mode(tui, mode, true);
+ break;
+ case kTermModeResizeEvents:
+ signal_watcher_stop(&tui->winch_handle);
+ tui_set_term_mode(tui, mode, true);
+ break;
}
}
}
@@ -295,7 +296,10 @@ void tui_set_key_encoding(TUIData *tui)
{
switch (tui->input.key_encoding) {
case kKeyEncodingKitty:
- out(tui, S_LEN("\x1b[>1u"));
+ // Progressive enhancement flags:
+ // 0b01 (1) Disambiguate escape codes
+ // 0b10 (2) Report event types
+ out(tui, S_LEN("\x1b[>3u"));
break;
case kKeyEncodingXterm:
out(tui, S_LEN("\x1b[>4;2m"));
@@ -310,7 +314,7 @@ static void tui_reset_key_encoding(TUIData *tui)
{
switch (tui->input.key_encoding) {
case kKeyEncodingKitty:
- out(tui, S_LEN("\x1b[<1u"));
+ out(tui, S_LEN("\x1b[<u"));
break;
case kKeyEncodingXterm:
out(tui, S_LEN("\x1b[>4;0m"));
@@ -320,6 +324,18 @@ static void tui_reset_key_encoding(TUIData *tui)
}
}
+/// Write the OSC 11 sequence to the terminal emulator to query the current
+/// background color.
+///
+/// The response will be handled by the TermResponse autocommand created in
+/// _defaults.lua.
+void tui_query_bg_color(TUIData *tui)
+ FUNC_ATTR_NONNULL_ALL
+{
+ out(tui, S_LEN("\x1b]11;?\x07"));
+ flush_buf(tui);
+}
+
/// Enable the alternate screen and emit other control sequences to start the TUI.
///
/// This is also called when the TUI is resumed after being suspended. We reinitialize all state
@@ -336,7 +352,7 @@ static void terminfo_start(TUIData *tui)
tui->cork = false;
tui->overflow = false;
tui->set_cursor_color_as_str = false;
- tui->cursor_color_changed = false;
+ tui->cursor_has_color = false;
tui->showing_mode = SHAPE_IDX_N;
tui->unibi_ext.enable_mouse = -1;
tui->unibi_ext.disable_mouse = -1;
@@ -438,14 +454,13 @@ static void terminfo_start(TUIData *tui)
// Enable bracketed paste
unibi_out_ext(tui, tui->unibi_ext.enable_bracketed_paste);
- // Query support for mode 2026 (Synchronized Output). Some terminals also
- // support an older DCS sequence for synchronized output, but we will only use
- // mode 2026.
+ // Query support for private DEC modes that Nvim can take advantage of.
// Some terminals (such as Terminal.app) do not support DECRQM, so skip the query.
if (!nsterm) {
tui_request_term_mode(tui, kTermModeSynchronizedOutput);
- tui_request_term_mode(tui, kTermModeResizeEvents);
tui_request_term_mode(tui, kTermModeGraphemeClusters);
+ tui_request_term_mode(tui, kTermModeThemeUpdates);
+ tui_request_term_mode(tui, kTermModeResizeEvents);
}
// Don't use DECRQSS in screen or tmux, as they behave strangely when receiving it.
@@ -493,6 +508,10 @@ static void terminfo_start(TUIData *tui)
/// Disable the alternate screen and prepare for the TUI to close.
static void terminfo_stop(TUIData *tui)
{
+ // Disable theme update notifications. We do this first to avoid getting any
+ // more notifications after we reset the cursor and any color palette changes.
+ tui_set_term_mode(tui, kTermModeThemeUpdates, false);
+
// Destroy output stuff
tui_mode_change(tui, NULL_STRING, SHAPE_IDX_N);
tui_mouse_off(tui);
@@ -509,6 +528,7 @@ static void terminfo_stop(TUIData *tui)
if (tui->did_set_grapheme_cluster_mode) {
tui_set_term_mode(tui, kTermModeGraphemeClusters, false);
}
+
// May restore old title before exiting alternate screen.
tui_set_title(tui, NULL_STRING);
if (ui_client_exit_status == 0) {
@@ -521,7 +541,7 @@ static void terminfo_stop(TUIData *tui)
// Exit alternate screen.
unibi_out(tui, unibi_exit_ca_mode);
}
- if (tui->cursor_color_changed) {
+ if (tui->cursor_has_color) {
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
// Disable bracketed paste
@@ -1302,11 +1322,12 @@ static void tui_set_mode(TUIData *tui, ModeShape mode)
UNIBI_SET_NUM_VAR(tui->params[0], aep.rgb_bg_color);
}
unibi_out_ext(tui, tui->unibi_ext.set_cursor_color);
- tui->cursor_color_changed = true;
+ tui->cursor_has_color = true;
}
- } else if (c.id == 0) {
+ } else if (c.id == 0 && (tui->want_invisible || tui->cursor_has_color)) {
// No cursor color for this mode; reset to default.
tui->want_invisible = false;
+ tui->cursor_has_color = false;
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
@@ -1319,7 +1340,7 @@ static void tui_set_mode(TUIData *tui, ModeShape mode)
case SHAPE_VER:
shape = 5; break;
}
- UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0));
+ UNIBI_SET_NUM_VAR(tui->params[0], shape + (int)(c.blinkon == 0 || c.blinkoff == 0));
unibi_out_ext(tui, tui->unibi_ext.set_cursor_style);
}
@@ -2273,6 +2294,7 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
bool putty = terminfo_is_term_family(term, "putty");
bool screen = terminfo_is_term_family(term, "screen");
bool tmux = terminfo_is_term_family(term, "tmux") || !!os_getenv("TMUX");
+ bool st = terminfo_is_term_family(term, "st");
bool iterm = terminfo_is_term_family(term, "iterm")
|| terminfo_is_term_family(term, "iterm2")
|| terminfo_is_term_family(term, "iTerm.app")
@@ -2357,9 +2379,10 @@ static void augment_terminfo(TUIData *tui, const char *term, int vte_version, in
// would use a tmux control sequence and an extra if(screen) test.
tui->unibi_ext.set_cursor_color =
(int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\"));
- } else if ((xterm || hterm || rxvt || tmux || alacritty)
+ } else if ((xterm || hterm || rxvt || tmux || alacritty || st)
&& (vte_version == 0 || vte_version >= 3900)) {
// Supported in urxvt, newer VTE.
+ // Supported in st, but currently missing in ncurses definitions. #32217
tui->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color",
"\033]12;%p1%s\007");
}
diff --git a/src/nvim/tui/tui_defs.h b/src/nvim/tui/tui_defs.h
index bd99d6b0ad..5d6f027bf7 100644
--- a/src/nvim/tui/tui_defs.h
+++ b/src/nvim/tui/tui_defs.h
@@ -5,6 +5,7 @@ typedef struct TUIData TUIData;
typedef enum {
kTermModeSynchronizedOutput = 2026,
kTermModeGraphemeClusters = 2027,
+ kTermModeThemeUpdates = 2031,
kTermModeResizeEvents = 2048,
} TermMode;
diff --git a/src/nvim/ui.c b/src/nvim/ui.c
index 7c81110ae9..51815c36e1 100644
--- a/src/nvim/ui.c
+++ b/src/nvim/ui.c
@@ -6,7 +6,6 @@
#include <string.h>
#include <uv.h>
-#include "klib/kvec.h"
#include "nvim/api/private/helpers.h"
#include "nvim/api/private/validate.h"
#include "nvim/api/ui.h"
@@ -224,10 +223,10 @@ void ui_refresh(void)
// Reset 'cmdheight' for all tabpages when ext_messages toggles.
if (had_message != ui_ext[kUIMessages]) {
set_option_value(kOptCmdheight, NUMBER_OPTVAL(had_message), 0);
- command_height();
FOR_ALL_TABS(tp) {
tp->tp_ch_used = had_message;
}
+ msg_scroll_flush();
}
if (!ui_active()) {
@@ -325,7 +324,7 @@ void ui_busy_stop(void)
/// Emit a bell or visualbell as a warning
///
-/// val is one of the BO_ values, e.g., BO_OPER
+/// val is one of the OptBoFlags values, e.g., kOptBoFlagOperator
void vim_beep(unsigned val)
{
called_vim_beep = true;
@@ -334,7 +333,7 @@ void vim_beep(unsigned val)
return;
}
- if (!((bo_flags & val) || (bo_flags & BO_ALL))) {
+ if (!((bo_flags & val) || (bo_flags & kOptBoFlagAll))) {
static int beeps = 0;
static uint64_t start_time = 0;
@@ -477,7 +476,7 @@ void ui_line(ScreenGrid *grid, int row, bool invalid_row, int startcol, int endc
(const sattr_T *)grid->attrs + off);
// 'writedelay': flush & delay each time.
- if (p_wd && (rdb_flags & RDB_LINE)) {
+ if (p_wd && (rdb_flags & kOptRdbFlagLine)) {
// If 'writedelay' is active, set the cursor to indicate what was drawn.
ui_call_grid_cursor_goto(grid->handle, row,
MIN(clearcol, (int)grid->cols - 1));
@@ -564,7 +563,7 @@ void ui_flush(void)
}
ui_call_flush();
- if (p_wd && (rdb_flags & RDB_FLUSH)) {
+ if (p_wd && (rdb_flags & kOptRdbFlagFlush)) {
os_sleep((uint64_t)llabs(p_wd));
}
}
@@ -718,10 +717,10 @@ void ui_call_event(char *name, bool fast, Array args)
bool handled = false;
UIEventCallback *event_cb;
- // Prompt messages should be shown immediately so must be safe
+ // Return prompt is still a non-fast event, other prompt messages are
+ // followed by a "cmdline_show" event.
if (strcmp(name, "msg_show") == 0) {
- char *kind = args.items[0].data.string.data;
- fast = !kind || (strncmp(kind, "confirm", 7) != 0 && strcmp(kind, "return_prompt") != 0);
+ fast = !strequal(args.items[0].data.string.data, "return_prompt");
}
map_foreach(&ui_event_cbs, ui_event_ns_id, event_cb, {
diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c
index adaf3eadb8..af946d799a 100644
--- a/src/nvim/ui_client.c
+++ b/src/nvim/ui_client.c
@@ -1,5 +1,6 @@
/// Nvim's own UI client, which attaches to a child or remote Nvim server.
+#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c
index e28e8d4da7..38d882462d 100644
--- a/src/nvim/ui_compositor.c
+++ b/src/nvim/ui_compositor.c
@@ -101,6 +101,35 @@ bool ui_comp_should_draw(void)
return composed_uis != 0 && valid_screen;
}
+/// Raises or lowers the layer, syncing comp_index with zindex.
+///
+/// This function adjusts the position of a layer in the layers array
+/// based on its zindex, either raising or lowering it.
+///
+/// @param[in] layer_idx Index of the layer to be raised or lowered.
+/// @param[in] raise Raise the layer if true, else lower it.
+void ui_comp_layers_adjust(size_t layer_idx, bool raise)
+{
+ size_t size = layers.size;
+ ScreenGrid *layer = layers.items[layer_idx];
+
+ if (raise) {
+ while (layer_idx < size - 1 && layer->zindex > layers.items[layer_idx + 1]->zindex) {
+ layers.items[layer_idx] = layers.items[layer_idx + 1];
+ layers.items[layer_idx]->comp_index = layer_idx;
+ layer_idx++;
+ }
+ } else {
+ while (layer_idx > 0 && layer->zindex < layers.items[layer_idx - 1]->zindex) {
+ layers.items[layer_idx] = layers.items[layer_idx - 1];
+ layers.items[layer_idx]->comp_index = layer_idx;
+ layer_idx--;
+ }
+ }
+ layers.items[layer_idx] = layer;
+ layer->comp_index = layer_idx;
+}
+
/// Places `grid` at (col,row) position with (width * height) size.
/// Adds `grid` as the top layer if it is a new layer.
///
@@ -425,7 +454,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
for (int i = skipstart; i < (endcol - skipend) - startcol; i++) {
if (attrbuf[i] < 0) {
- if (rdb_flags & RDB_INVALID) {
+ if (rdb_flags & kOptRdbFlagInvalid) {
abort();
} else {
attrbuf[i] = 0;
@@ -441,7 +470,7 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlag
static void compose_debug(Integer startrow, Integer endrow, Integer startcol, Integer endcol,
int syn_id, bool delay)
{
- if (!(rdb_flags & RDB_COMPOSITOR) || startcol >= endcol) {
+ if (!(rdb_flags & kOptRdbFlagCompositor) || startcol >= endcol) {
return;
}
@@ -637,7 +666,7 @@ void ui_comp_grid_scroll(Integer grid, Integer top, Integer bot, Integer left, I
}
} else {
ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
- if (rdb_flags & RDB_COMPOSITOR) {
+ if (rdb_flags & kOptRdbFlagCompositor) {
debug_delay(2);
}
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 0f8857a6bd..8d791a4c38 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -89,7 +89,6 @@
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/cursor.h"
-#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/edit.h"
#include "nvim/errors.h"
@@ -107,12 +106,10 @@
#include "nvim/getchar.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/macros_defs.h"
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
-#include "nvim/marktree_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memline_defs.h"
@@ -1862,6 +1859,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event)
u_oldcount = -1;
}
+ msg_ext_set_kind("undo");
int count = startcount;
while (count--) {
// Do the change warning now, so that it triggers FileChangedRO when
@@ -2528,7 +2526,7 @@ static void u_undoredo(bool undo, bool do_buf_event)
/// @param absolute used ":undo N"
static void u_undo_end(bool did_undo, bool absolute, bool quiet)
{
- if ((fdo_flags & FDO_UNDO) && KeyTyped) {
+ if ((fdo_flags & kOptFdoFlagUndo) && KeyTyped) {
foldOpenCursor();
}
@@ -2595,12 +2593,12 @@ static void u_undo_end(bool did_undo, bool absolute, bool quiet)
check_pos(curbuf, &VIsual);
}
- smsg_hl_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"),
- u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
- _(msgstr),
- did_undo ? _("before") : _("after"),
- uhp == NULL ? 0 : (int64_t)uhp->uh_seq,
- msgbuf);
+ smsg_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"),
+ u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount,
+ _(msgstr),
+ did_undo ? _("before") : _("after"),
+ uhp == NULL ? 0 : (int64_t)uhp->uh_seq,
+ msgbuf);
}
/// Put the timestamp of an undo header in "buf[buflen]" in a nice format.
diff --git a/src/nvim/usercmd.c b/src/nvim/usercmd.c
index d27899b10f..2aa789a2ee 100644
--- a/src/nvim/usercmd.c
+++ b/src/nvim/usercmd.c
@@ -18,7 +18,6 @@
#include "nvim/garray.h"
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
-#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
@@ -26,6 +25,7 @@
#include "nvim/mapping.h"
#include "nvim/mbyte.h"
#include "nvim/memory.h"
+#include "nvim/memory_defs.h"
#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/option_vars.h"
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 368bf79cf4..fc8819bfcc 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -9,7 +9,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
#include "auto/versiondef.h" // version info generated by the build system
#include "auto/versiondef_git.h"
@@ -23,6 +22,7 @@
#include "nvim/gettext_defs.h"
#include "nvim/globals.h"
#include "nvim/grid.h"
+#include "nvim/grid_defs.h"
#include "nvim/highlight.h"
#include "nvim/highlight_defs.h"
#include "nvim/lua/executor.h"
@@ -33,6 +33,7 @@
#include "nvim/os/os.h"
#include "nvim/strings.h"
#include "nvim/ui.h"
+#include "nvim/ui_defs.h"
#include "nvim/version.h"
#include "nvim/window.h"
diff --git a/src/nvim/vterm/LICENSE b/src/nvim/vterm/LICENSE
new file mode 100644
index 0000000000..0d051634b2
--- /dev/null
+++ b/src/nvim/vterm/LICENSE
@@ -0,0 +1,23 @@
+
+
+The MIT License
+
+Copyright (c) 2008 Paul Evans <leonerd@leonerd.org.uk>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/nvim/vterm/README.md b/src/nvim/vterm/README.md
new file mode 100644
index 0000000000..4cc43bfb94
--- /dev/null
+++ b/src/nvim/vterm/README.md
@@ -0,0 +1 @@
+Adopted from [libvterm](https://www.leonerd.org.uk/code/libvterm/)
diff --git a/src/nvim/vterm/encoding.c b/src/nvim/vterm/encoding.c
new file mode 100644
index 0000000000..f9061e8e50
--- /dev/null
+++ b/src/nvim/vterm/encoding.c
@@ -0,0 +1,271 @@
+#include "nvim/vterm/encoding.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/encoding.c.generated.h"
+#endif
+
+#define UNICODE_INVALID 0xFFFD
+
+#if defined(DEBUG) && DEBUG > 1
+# define DEBUG_PRINT_UTF8
+#endif
+
+struct UTF8DecoderData {
+ // number of bytes remaining in this codepoint
+ int bytes_remaining;
+
+ // number of bytes total in this codepoint once it's finished
+ // (for detecting overlongs)
+ int bytes_total;
+
+ int this_cp;
+};
+
+static void init_utf8(VTermEncoding *enc, void *data_)
+{
+ struct UTF8DecoderData *data = data_;
+
+ data->bytes_remaining = 0;
+ data->bytes_total = 0;
+}
+
+static void decode_utf8(VTermEncoding *enc, void *data_, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct UTF8DecoderData *data = data_;
+
+#ifdef DEBUG_PRINT_UTF8
+ printf("BEGIN UTF-8\n");
+#endif
+
+ for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ uint8_t c = (uint8_t)bytes[*pos];
+
+#ifdef DEBUG_PRINT_UTF8
+ printf(" pos=%zd c=%02x rem=%d\n", *pos, c, data->bytes_remaining);
+#endif
+
+ if (c < 0x20) { // C0
+ return;
+ } else if (c >= 0x20 && c < 0x7f) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ cp[(*cpi)++] = c;
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 char: U+%04x\n", c);
+#endif
+ data->bytes_remaining = 0;
+ } else if (c == 0x7f) { // DEL
+ return;
+ } else if (c >= 0x80 && c < 0xc0) {
+ if (!data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ continue;
+ }
+
+ data->this_cp <<= 6;
+ data->this_cp |= c & 0x3f;
+ data->bytes_remaining--;
+
+ if (!data->bytes_remaining) {
+#ifdef DEBUG_PRINT_UTF8
+ printf(" UTF-8 raw char U+%04x bytelen=%d ", data->this_cp, data->bytes_total);
+#endif
+ // Check for overlong sequences
+ switch (data->bytes_total) {
+ case 2:
+ if (data->this_cp < 0x0080) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 3:
+ if (data->this_cp < 0x0800) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 4:
+ if (data->this_cp < 0x10000) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 5:
+ if (data->this_cp < 0x200000) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ case 6:
+ if (data->this_cp < 0x4000000) {
+ data->this_cp = UNICODE_INVALID;
+ }
+ break;
+ }
+ // Now look for plain invalid ones
+ if ((data->this_cp >= 0xD800 && data->this_cp <= 0xDFFF)
+ || data->this_cp == 0xFFFE
+ || data->this_cp == 0xFFFF) {
+ data->this_cp = UNICODE_INVALID;
+ }
+#ifdef DEBUG_PRINT_UTF8
+ printf(" char: U+%04x\n", data->this_cp);
+#endif
+ cp[(*cpi)++] = (uint32_t)data->this_cp;
+ }
+ } else if (c >= 0xc0 && c < 0xe0) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x1f;
+ data->bytes_total = 2;
+ data->bytes_remaining = 1;
+ } else if (c >= 0xe0 && c < 0xf0) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x0f;
+ data->bytes_total = 3;
+ data->bytes_remaining = 2;
+ } else if (c >= 0xf0 && c < 0xf8) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x07;
+ data->bytes_total = 4;
+ data->bytes_remaining = 3;
+ } else if (c >= 0xf8 && c < 0xfc) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x03;
+ data->bytes_total = 5;
+ data->bytes_remaining = 4;
+ } else if (c >= 0xfc && c < 0xfe) {
+ if (data->bytes_remaining) {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+
+ data->this_cp = c & 0x01;
+ data->bytes_total = 6;
+ data->bytes_remaining = 5;
+ } else {
+ cp[(*cpi)++] = UNICODE_INVALID;
+ }
+ }
+}
+
+static VTermEncoding encoding_utf8 = {
+ .init = &init_utf8,
+ .decode = &decode_utf8,
+};
+
+static void decode_usascii(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ int is_gr = bytes[*pos] & 0x80;
+
+ for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ uint8_t c = (uint8_t)(bytes[*pos] ^ is_gr);
+
+ if (c < 0x20 || c == 0x7f || c >= 0x80) {
+ return;
+ }
+
+ cp[(*cpi)++] = c;
+ }
+}
+
+static VTermEncoding encoding_usascii = {
+ .decode = &decode_usascii,
+};
+
+struct StaticTableEncoding {
+ const VTermEncoding enc;
+ const uint32_t chars[128];
+};
+
+static void decode_table(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t bytelen)
+{
+ struct StaticTableEncoding *table = (struct StaticTableEncoding *)enc;
+ int is_gr = bytes[*pos] & 0x80;
+
+ for (; *pos < bytelen && *cpi < cplen; (*pos)++) {
+ uint8_t c = (uint8_t)(bytes[*pos] ^ is_gr);
+
+ if (c < 0x20 || c == 0x7f || c >= 0x80) {
+ return;
+ }
+
+ if (table->chars[c]) {
+ cp[(*cpi)++] = table->chars[c];
+ } else {
+ cp[(*cpi)++] = c;
+ }
+ }
+}
+
+// https://en.wikipedia.org/wiki/DEC_Special_Graphics
+static const struct StaticTableEncoding encoding_DECdrawing = {
+ { .decode = &decode_table },
+ {
+ [0x60] = 0x25C6, // BLACK DIAMOND
+ [0x61] = 0x2592, // MEDIUM SHADE (checkerboard)
+ [0x62] = 0x2409, // SYMBOL FOR HORIZONTAL TAB
+ [0x63] = 0x240C, // SYMBOL FOR FORM FEED
+ [0x64] = 0x240D, // SYMBOL FOR CARRIAGE RETURN
+ [0x65] = 0x240A, // SYMBOL FOR LINE FEED
+ [0x66] = 0x00B0, // DEGREE SIGN
+ [0x67] = 0x00B1, // PLUS-MINUS SIGN (plus or minus)
+ [0x68] = 0x2424, // SYMBOL FOR NEW LINE
+ [0x69] = 0x240B, // SYMBOL FOR VERTICAL TAB
+ [0x6a] = 0x2518, // BOX DRAWINGS LIGHT UP AND LEFT (bottom-right corner)
+ [0x6b] = 0x2510, // BOX DRAWINGS LIGHT DOWN AND LEFT (top-right corner)
+ [0x6c] = 0x250C, // BOX DRAWINGS LIGHT DOWN AND RIGHT (top-left corner)
+ [0x6d] = 0x2514, // BOX DRAWINGS LIGHT UP AND RIGHT (bottom-left corner)
+ [0x6e] = 0x253C, // BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL (crossing lines)
+ [0x6f] = 0x23BA, // HORIZONTAL SCAN LINE-1
+ [0x70] = 0x23BB, // HORIZONTAL SCAN LINE-3
+ [0x71] = 0x2500, // BOX DRAWINGS LIGHT HORIZONTAL
+ [0x72] = 0x23BC, // HORIZONTAL SCAN LINE-7
+ [0x73] = 0x23BD, // HORIZONTAL SCAN LINE-9
+ [0x74] = 0x251C, // BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ [0x75] = 0x2524, // BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ [0x76] = 0x2534, // BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ [0x77] = 0x252C, // BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ [0x78] = 0x2502, // BOX DRAWINGS LIGHT VERTICAL
+ [0x79] = 0x2A7D, // LESS-THAN OR SLANTED EQUAL-TO
+ [0x7a] = 0x2A7E, // GREATER-THAN OR SLANTED EQUAL-TO
+ [0x7b] = 0x03C0, // GREEK SMALL LETTER PI
+ [0x7c] = 0x2260, // NOT EQUAL TO
+ [0x7d] = 0x00A3, // POUND SIGN
+ [0x7e] = 0x00B7, // MIDDLE DOT
+ }
+};
+
+static struct {
+ VTermEncodingType type;
+ char designation;
+ VTermEncoding *enc;
+}
+encodings[] = {
+ { ENC_UTF8, 'u', &encoding_utf8 },
+ { ENC_SINGLE_94, '0', (VTermEncoding *)&encoding_DECdrawing },
+ { ENC_SINGLE_94, 'B', &encoding_usascii },
+ { 0 },
+};
+
+VTermEncoding *vterm_lookup_encoding(VTermEncodingType type, char designation)
+{
+ for (int i = 0; encodings[i].designation; i++) {
+ if (encodings[i].type == type && encodings[i].designation == designation) {
+ return encodings[i].enc;
+ }
+ }
+ return NULL;
+}
diff --git a/src/nvim/vterm/encoding.h b/src/nvim/vterm/encoding.h
new file mode 100644
index 0000000000..204b6d90c9
--- /dev/null
+++ b/src/nvim/vterm/encoding.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/encoding.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/keyboard.c b/src/nvim/vterm/keyboard.c
new file mode 100644
index 0000000000..dd088ac40e
--- /dev/null
+++ b/src/nvim/vterm/keyboard.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+
+#include "nvim/ascii_defs.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/vterm/keyboard.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/keyboard.c.generated.h"
+#endif
+
+static VTermKeyEncodingFlags vterm_state_get_key_encoding_flags(const VTermState *state)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ const struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size > 0);
+ return stack->items[stack->size - 1];
+}
+
+void vterm_keyboard_unichar(VTerm *vt, uint32_t c, VTermModifier mod)
+{
+ bool passthru = false;
+ if (c == ' ') {
+ // Space is passed through only when there are no modifiers (including shift)
+ passthru = mod == VTERM_MOD_NONE;
+ } else {
+ // Otherwise pass through when there are no modifiers (ignoring shift)
+ passthru = (mod & (unsigned)~VTERM_MOD_SHIFT) == 0;
+ }
+
+ if (passthru) {
+ char str[6];
+ int seqlen = fill_utf8((int)c, str);
+ vterm_push_output_bytes(vt, str, (size_t)seqlen);
+ return;
+ }
+
+ VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state);
+ if (flags.disambiguate) {
+ // Always use unshifted codepoint
+ if (c >= 'A' && c <= 'Z') {
+ c += 'a' - 'A';
+ mod |= VTERM_MOD_SHIFT;
+ }
+
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", c, mod + 1);
+ return;
+ }
+
+ if (mod & VTERM_MOD_CTRL) {
+ // Handle special cases. These are taken from kitty, but seem mostly
+ // consistent across terminals.
+ switch (c) {
+ case '2':
+ case ' ':
+ // Ctrl+2 is NUL to match Ctrl+@ (which is Shift+2 on US keyboards)
+ // Ctrl+Space is also NUL for some reason
+ c = 0x00;
+ break;
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ // Ctrl+3 through Ctrl+7 are sequential starting from 0x1b. Importantly,
+ // this means that Ctrl+6 emits 0x1e (the same as Ctrl+^ on US keyboards)
+ c = 0x1b + c - '3';
+ break;
+ case '8':
+ // Ctrl+8 is DEL
+ c = 0x7f;
+ break;
+ case '/':
+ // Ctrl+/ is equivalent to Ctrl+_ for historic reasons
+ c = 0x1f;
+ break;
+ default:
+ if (c >= '@' && c <= 0x7f) {
+ c &= 0x1f;
+ }
+ break;
+ }
+ }
+
+ vterm_push_output_sprintf(vt, "%s%c", mod & VTERM_MOD_ALT ? ESC_S : "", c);
+}
+
+typedef struct {
+ enum {
+ KEYCODE_NONE,
+ KEYCODE_LITERAL,
+ KEYCODE_TAB,
+ KEYCODE_ENTER,
+ KEYCODE_SS3,
+ KEYCODE_CSI,
+ KEYCODE_CSI_CURSOR,
+ KEYCODE_CSINUM,
+ KEYCODE_KEYPAD,
+ } type;
+ int literal;
+ int csinum;
+} keycodes_s;
+
+static keycodes_s keycodes[] = {
+ { KEYCODE_NONE, NUL, 0 }, // NONE
+
+ { KEYCODE_ENTER, '\r', 0 }, // ENTER
+ { KEYCODE_TAB, '\t', 0 }, // TAB
+ { KEYCODE_LITERAL, '\x7f', 0 }, // BACKSPACE == ASCII DEL
+ { KEYCODE_LITERAL, '\x1b', 0 }, // ESCAPE
+
+ { KEYCODE_CSI_CURSOR, 'A', 0 }, // UP
+ { KEYCODE_CSI_CURSOR, 'B', 0 }, // DOWN
+ { KEYCODE_CSI_CURSOR, 'D', 0 }, // LEFT
+ { KEYCODE_CSI_CURSOR, 'C', 0 }, // RIGHT
+
+ { KEYCODE_CSINUM, '~', 2 }, // INS
+ { KEYCODE_CSINUM, '~', 3 }, // DEL
+ { KEYCODE_CSI_CURSOR, 'H', 0 }, // HOME
+ { KEYCODE_CSI_CURSOR, 'F', 0 }, // END
+ { KEYCODE_CSINUM, '~', 5 }, // PAGEUP
+ { KEYCODE_CSINUM, '~', 6 }, // PAGEDOWN
+};
+
+static keycodes_s keycodes_fn[] = {
+ { KEYCODE_NONE, NUL, 0 }, // F0 - shouldn't happen
+ { KEYCODE_SS3, 'P', 0 }, // F1
+ { KEYCODE_SS3, 'Q', 0 }, // F2
+ { KEYCODE_SS3, 'R', 0 }, // F3
+ { KEYCODE_SS3, 'S', 0 }, // F4
+ { KEYCODE_CSINUM, '~', 15 }, // F5
+ { KEYCODE_CSINUM, '~', 17 }, // F6
+ { KEYCODE_CSINUM, '~', 18 }, // F7
+ { KEYCODE_CSINUM, '~', 19 }, // F8
+ { KEYCODE_CSINUM, '~', 20 }, // F9
+ { KEYCODE_CSINUM, '~', 21 }, // F10
+ { KEYCODE_CSINUM, '~', 23 }, // F11
+ { KEYCODE_CSINUM, '~', 24 }, // F12
+};
+
+static keycodes_s keycodes_kp[] = {
+ { KEYCODE_KEYPAD, '0', 'p' }, // KP_0
+ { KEYCODE_KEYPAD, '1', 'q' }, // KP_1
+ { KEYCODE_KEYPAD, '2', 'r' }, // KP_2
+ { KEYCODE_KEYPAD, '3', 's' }, // KP_3
+ { KEYCODE_KEYPAD, '4', 't' }, // KP_4
+ { KEYCODE_KEYPAD, '5', 'u' }, // KP_5
+ { KEYCODE_KEYPAD, '6', 'v' }, // KP_6
+ { KEYCODE_KEYPAD, '7', 'w' }, // KP_7
+ { KEYCODE_KEYPAD, '8', 'x' }, // KP_8
+ { KEYCODE_KEYPAD, '9', 'y' }, // KP_9
+ { KEYCODE_KEYPAD, '*', 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, '+', 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, ',', 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, '-', 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, '.', 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, '/', 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, '\n', 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, '=', 'X' }, // KP_EQUAL
+};
+
+static keycodes_s keycodes_kp_csiu[] = {
+ { KEYCODE_KEYPAD, 57399, 'p' }, // KP_0
+ { KEYCODE_KEYPAD, 57400, 'q' }, // KP_1
+ { KEYCODE_KEYPAD, 57401, 'r' }, // KP_2
+ { KEYCODE_KEYPAD, 57402, 's' }, // KP_3
+ { KEYCODE_KEYPAD, 57403, 't' }, // KP_4
+ { KEYCODE_KEYPAD, 57404, 'u' }, // KP_5
+ { KEYCODE_KEYPAD, 57405, 'v' }, // KP_6
+ { KEYCODE_KEYPAD, 57406, 'w' }, // KP_7
+ { KEYCODE_KEYPAD, 57407, 'x' }, // KP_8
+ { KEYCODE_KEYPAD, 57408, 'y' }, // KP_9
+ { KEYCODE_KEYPAD, 57411, 'j' }, // KP_MULT
+ { KEYCODE_KEYPAD, 57413, 'k' }, // KP_PLUS
+ { KEYCODE_KEYPAD, 57416, 'l' }, // KP_COMMA
+ { KEYCODE_KEYPAD, 57412, 'm' }, // KP_MINUS
+ { KEYCODE_KEYPAD, 57409, 'n' }, // KP_PERIOD
+ { KEYCODE_KEYPAD, 57410, 'o' }, // KP_DIVIDE
+ { KEYCODE_KEYPAD, 57414, 'M' }, // KP_ENTER
+ { KEYCODE_KEYPAD, 57415, 'X' }, // KP_EQUAL
+};
+
+void vterm_keyboard_key(VTerm *vt, VTermKey key, VTermModifier mod)
+{
+ if (key == VTERM_KEY_NONE) {
+ return;
+ }
+
+ VTermKeyEncodingFlags flags = vterm_state_get_key_encoding_flags(vt->state);
+
+ keycodes_s k;
+ if (key < VTERM_KEY_FUNCTION_0) {
+ if (key >= sizeof(keycodes)/sizeof(keycodes[0])) {
+ return;
+ }
+ k = keycodes[key];
+ } else if (key >= VTERM_KEY_FUNCTION_0 && key <= VTERM_KEY_FUNCTION_MAX) {
+ if ((key - VTERM_KEY_FUNCTION_0) >= sizeof(keycodes_fn)/sizeof(keycodes_fn[0])) {
+ return;
+ }
+ k = keycodes_fn[key - VTERM_KEY_FUNCTION_0];
+ } else if (key >= VTERM_KEY_KP_0) {
+ if ((key - VTERM_KEY_KP_0) >= sizeof(keycodes_kp)/sizeof(keycodes_kp[0])) {
+ return;
+ }
+
+ if (flags.disambiguate) {
+ k = keycodes_kp_csiu[key - VTERM_KEY_KP_0];
+ } else {
+ k = keycodes_kp[key - VTERM_KEY_KP_0];
+ }
+ }
+
+ switch (k.type) {
+ case KEYCODE_NONE:
+ break;
+
+ case KEYCODE_TAB:
+ // Shift-Tab is CSI Z but plain Tab is 0x09
+ if (flags.disambiguate) {
+ goto case_LITERAL;
+ } else if (mod == VTERM_MOD_SHIFT) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "Z");
+ } else if (mod & VTERM_MOD_SHIFT) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%dZ", mod + 1);
+ } else {
+ goto case_LITERAL;
+ }
+ break;
+
+ case KEYCODE_ENTER:
+ // Enter is CRLF in newline mode, but just LF in linefeed
+ if (vt->state->mode.newline) {
+ vterm_push_output_sprintf(vt, "\r\n");
+ } else {
+ goto case_LITERAL;
+ }
+ break;
+
+ case KEYCODE_LITERAL:
+ case_LITERAL:
+ if (flags.disambiguate) {
+ switch (key) {
+ case VTERM_KEY_TAB:
+ case VTERM_KEY_ENTER:
+ case VTERM_KEY_BACKSPACE:
+ // If there are no mods then leave these as-is
+ flags.disambiguate = mod != VTERM_MOD_NONE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (flags.disambiguate) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%du", k.literal, mod + 1);
+ } else {
+ vterm_push_output_sprintf(vt, mod & VTERM_MOD_ALT ? ESC_S "%c" : "%c", k.literal);
+ }
+ break;
+
+ case KEYCODE_SS3:
+ case_SS3:
+ if (mod == 0) {
+ vterm_push_output_sprintf_ctrl(vt, C1_SS3, "%c", k.literal);
+ } else {
+ goto case_CSI;
+ }
+ break;
+
+ case KEYCODE_CSI:
+ case_CSI:
+ if (mod == 0) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%c", k.literal);
+ } else {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "1;%d%c", mod + 1, k.literal);
+ }
+ break;
+
+ case KEYCODE_CSINUM:
+ if (mod == 0) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d%c", k.csinum, k.literal);
+ } else {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "%d;%d%c", k.csinum, mod + 1, k.literal);
+ }
+ break;
+
+ case KEYCODE_CSI_CURSOR:
+ if (vt->state->mode.cursor) {
+ goto case_SS3;
+ } else {
+ goto case_CSI;
+ }
+
+ case KEYCODE_KEYPAD:
+ if (vt->state->mode.keypad) {
+ k.literal = k.csinum;
+ goto case_SS3;
+ } else {
+ goto case_LITERAL;
+ }
+ }
+}
+
+void vterm_keyboard_start_paste(VTerm *vt)
+{
+ if (vt->state->mode.bracketpaste) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "200~");
+ }
+}
+
+void vterm_keyboard_end_paste(VTerm *vt)
+{
+ if (vt->state->mode.bracketpaste) {
+ vterm_push_output_sprintf_ctrl(vt, C1_CSI, "201~");
+ }
+}
diff --git a/src/nvim/vterm/keyboard.h b/src/nvim/vterm/keyboard.h
new file mode 100644
index 0000000000..af5f4a3ed1
--- /dev/null
+++ b/src/nvim/vterm/keyboard.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/keyboard.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/mouse.c b/src/nvim/vterm/mouse.c
new file mode 100644
index 0000000000..2f3b1d9e2f
--- /dev/null
+++ b/src/nvim/vterm/mouse.c
@@ -0,0 +1,124 @@
+#include "nvim/math.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/vterm/mouse.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/mouse.c.generated.h"
+#endif
+
+static void output_mouse(VTermState *state, int code, int pressed, int modifiers, int col, int row)
+{
+ modifiers <<= 2;
+
+ switch (state->mouse_protocol) {
+ case MOUSE_X10:
+ if (col + 0x21 > 0xff) {
+ col = 0xff - 0x21;
+ }
+ if (row + 0x21 > 0xff) {
+ row = 0xff - 0x21;
+ }
+
+ if (!pressed) {
+ code = 3;
+ }
+
+ if (code & 0x80) {
+ break;
+ }
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%c%c%c",
+ (code | modifiers) + 0x20, col + 0x21, row + 0x21);
+ break;
+
+ case MOUSE_UTF8: {
+ char utf8[18];
+ size_t len = 0;
+
+ if (!pressed) {
+ code = 3;
+ }
+
+ len += (size_t)fill_utf8((code | modifiers) + 0x20, utf8 + len);
+ len += (size_t)fill_utf8(col + 0x21, utf8 + len);
+ len += (size_t)fill_utf8(row + 0x21, utf8 + len);
+ utf8[len] = 0;
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "M%s", utf8);
+ }
+ break;
+
+ case MOUSE_SGR:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "<%d;%d;%d%c",
+ code | modifiers, col + 1, row + 1, pressed ? 'M' : 'm');
+ break;
+
+ case MOUSE_RXVT:
+ if (!pressed) {
+ code = 3;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%d;%d;%dM",
+ code | modifiers, col + 1, row + 1);
+ break;
+ }
+}
+
+void vterm_mouse_move(VTerm *vt, int row, int col, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ if (col == state->mouse_col && row == state->mouse_row) {
+ return;
+ }
+
+ state->mouse_col = col;
+ state->mouse_row = row;
+
+ if ((state->mouse_flags & MOUSE_WANT_DRAG && state->mouse_buttons)
+ || (state->mouse_flags & MOUSE_WANT_MOVE)) {
+ if (state->mouse_buttons) {
+ int button = xctz((uint64_t)state->mouse_buttons) + 1;
+ if (button < 4) {
+ output_mouse(state, button - 1 + 0x20, 1, (int)mod, col, row);
+ } else if (button >= 8 && button < 12) {
+ output_mouse(state, button - 8 + 0x80 + 0x20, 1, (int)mod, col, row);
+ }
+ } else {
+ output_mouse(state, 3 + 0x20, 1, (int)mod, col, row);
+ }
+ }
+}
+
+void vterm_mouse_button(VTerm *vt, int button, bool pressed, VTermModifier mod)
+{
+ VTermState *state = vt->state;
+
+ int old_buttons = state->mouse_buttons;
+
+ if ((button > 0 && button <= 3) || (button >= 8 && button <= 11)) {
+ if (pressed) {
+ state->mouse_buttons |= (1 << (button - 1));
+ } else {
+ state->mouse_buttons &= ~(1 << (button - 1));
+ }
+ }
+
+ // Most of the time we don't get button releases from 4/5/6/7
+ if (state->mouse_buttons == old_buttons && (button < 4 || button > 7)) {
+ return;
+ }
+
+ if (!state->mouse_flags) {
+ return;
+ }
+
+ if (button < 4) {
+ output_mouse(state, button - 1, pressed, (int)mod, state->mouse_col, state->mouse_row);
+ } else if (button < 8) {
+ output_mouse(state, button - 4 + 0x40, pressed, (int)mod, state->mouse_col, state->mouse_row);
+ } else if (button < 12) {
+ output_mouse(state, button - 8 + 0x80, pressed, (int)mod, state->mouse_col, state->mouse_row);
+ }
+}
diff --git a/src/nvim/vterm/mouse.h b/src/nvim/vterm/mouse.h
new file mode 100644
index 0000000000..477f4028a2
--- /dev/null
+++ b/src/nvim/vterm/mouse.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/mouse.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/parser.c b/src/nvim/vterm/parser.c
new file mode 100644
index 0000000000..79d348f2c1
--- /dev/null
+++ b/src/nvim/vterm/parser.c
@@ -0,0 +1,411 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/vterm/parser.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/parser.c.generated.h"
+#endif
+
+#undef DEBUG_PARSER
+
+static bool is_intermed(uint8_t c)
+{
+ return c >= 0x20 && c <= 0x2f;
+}
+
+static void do_control(VTerm *vt, uint8_t control)
+{
+ if (vt->parser.callbacks && vt->parser.callbacks->control) {
+ if ((*vt->parser.callbacks->control)(control, vt->parser.cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled control 0x%02x\n", control);
+}
+
+static void do_csi(VTerm *vt, char command)
+{
+#ifdef DEBUG_PARSER
+ printf("Parsed CSI args as:\n", arglen, args);
+ printf(" leader: %s\n", vt->parser.v.csi.leader);
+ for (int argi = 0; argi < vt->parser.v.csi.argi; argi++) {
+ printf(" %lu", CSI_ARG(vt->parser.v.csi.args[argi]));
+ if (!CSI_ARG_HAS_MORE(vt->parser.v.csi.args[argi])) {
+ printf("\n");
+ }
+ printf(" intermed: %s\n", vt->parser.intermed);
+ }
+#endif
+
+ if (vt->parser.callbacks && vt->parser.callbacks->csi) {
+ if ((*vt->parser.callbacks->csi)(vt->parser.v.csi.leaderlen ? vt->parser.v.csi.leader : NULL,
+ vt->parser.v.csi.args,
+ vt->parser.v.csi.argi,
+ vt->parser.intermedlen ? vt->parser.intermed : NULL,
+ command,
+ vt->parser.cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled CSI %c\n", command);
+}
+
+static void do_escape(VTerm *vt, char command)
+{
+ char seq[INTERMED_MAX + 1];
+
+ size_t len = (size_t)vt->parser.intermedlen;
+ strncpy(seq, vt->parser.intermed, len); // NOLINT(runtime/printf)
+ seq[len++] = command;
+ seq[len] = 0;
+
+ if (vt->parser.callbacks && vt->parser.callbacks->escape) {
+ if ((*vt->parser.callbacks->escape)(seq, len, vt->parser.cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled escape ESC 0x%02x\n", command);
+}
+
+static void string_fragment(VTerm *vt, const char *str, size_t len, bool final)
+{
+ VTermStringFragment frag = {
+ .str = str,
+ .len = len,
+ .initial = vt->parser.string_initial,
+ .final = final,
+ };
+
+ switch (vt->parser.state) {
+ case OSC:
+ if (vt->parser.callbacks && vt->parser.callbacks->osc) {
+ (*vt->parser.callbacks->osc)(vt->parser.v.osc.command, frag, vt->parser.cbdata);
+ }
+ break;
+
+ case DCS_VTERM:
+ if (vt->parser.callbacks && vt->parser.callbacks->dcs) {
+ (*vt->parser.callbacks->dcs)(vt->parser.v.dcs.command, (size_t)vt->parser.v.dcs.commandlen,
+ frag,
+ vt->parser.cbdata);
+ }
+ break;
+
+ case APC:
+ if (vt->parser.callbacks && vt->parser.callbacks->apc) {
+ (*vt->parser.callbacks->apc)(frag, vt->parser.cbdata);
+ }
+ break;
+
+ case PM:
+ if (vt->parser.callbacks && vt->parser.callbacks->pm) {
+ (*vt->parser.callbacks->pm)(frag, vt->parser.cbdata);
+ }
+ break;
+
+ case SOS:
+ if (vt->parser.callbacks && vt->parser.callbacks->sos) {
+ (*vt->parser.callbacks->sos)(frag, vt->parser.cbdata);
+ }
+ break;
+
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ break;
+ }
+
+ vt->parser.string_initial = false;
+}
+
+size_t vterm_input_write(VTerm *vt, const char *bytes, size_t len)
+{
+ size_t pos = 0;
+ const char *string_start;
+
+ switch (vt->parser.state) {
+ case NORMAL:
+ case CSI_LEADER:
+ case CSI_ARGS:
+ case CSI_INTERMED:
+ case OSC_COMMAND:
+ case DCS_COMMAND:
+ string_start = NULL;
+ break;
+ case OSC:
+ case DCS_VTERM:
+ case APC:
+ case PM:
+ case SOS:
+ string_start = bytes;
+ break;
+ }
+
+#define ENTER_STATE(st) do { vt->parser.state = st; string_start = NULL; } while (0)
+#define ENTER_NORMAL_STATE() ENTER_STATE(NORMAL)
+
+#define IS_STRING_STATE() (vt->parser.state >= OSC_COMMAND)
+
+ for (; pos < len; pos++) {
+ uint8_t c = (uint8_t)bytes[pos];
+ bool c1_allowed = !vt->mode.utf8;
+
+ if (c == 0x00 || c == 0x7f) { // NUL, DEL
+ if (IS_STRING_STATE()) {
+ string_fragment(vt, string_start, (size_t)(bytes + pos - string_start), false);
+ string_start = bytes + pos + 1;
+ }
+ if (vt->parser.emit_nul) {
+ do_control(vt, c);
+ }
+ continue;
+ }
+ if (c == 0x18 || c == 0x1a) { // CAN, SUB
+ vt->parser.in_esc = false;
+ ENTER_NORMAL_STATE();
+ if (vt->parser.emit_nul) {
+ do_control(vt, c);
+ }
+ continue;
+ } else if (c == 0x1b) { // ESC
+ vt->parser.intermedlen = 0;
+ if (!IS_STRING_STATE()) {
+ vt->parser.state = NORMAL;
+ }
+ vt->parser.in_esc = true;
+ continue;
+ } else if (c == 0x07 // BEL, can stand for ST in OSC or DCS state
+ && IS_STRING_STATE()) {} else if (c < 0x20) { // other C0
+ if (vt->parser.state == SOS) {
+ continue; // All other C0s permitted in SOS
+ }
+ if (IS_STRING_STATE()) {
+ string_fragment(vt, string_start, (size_t)(bytes + pos - string_start), false);
+ }
+ do_control(vt, c);
+ if (IS_STRING_STATE()) {
+ string_start = bytes + pos + 1;
+ }
+ continue;
+ }
+
+ size_t string_len = (size_t)(bytes + pos - string_start);
+
+ if (vt->parser.in_esc) {
+ // Hoist an ESC letter into a C1 if we're not in a string mode
+ // Always accept ESC \ == ST even in string mode
+ if (!vt->parser.intermedlen
+ && c >= 0x40 && c < 0x60
+ && ((!IS_STRING_STATE() || c == 0x5c))) {
+ c += 0x40;
+ c1_allowed = true;
+ if (string_len) {
+ assert(string_len > 0);
+ string_len -= 1;
+ }
+ vt->parser.in_esc = false;
+ } else {
+ string_start = NULL;
+ vt->parser.state = NORMAL;
+ }
+ }
+
+ switch (vt->parser.state) {
+ case CSI_LEADER:
+ // Extract leader bytes 0x3c to 0x3f
+ if (c >= 0x3c && c <= 0x3f) {
+ if (vt->parser.v.csi.leaderlen < CSI_LEADER_MAX - 1) {
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen++] = (char)c;
+ }
+ break;
+ }
+
+ vt->parser.v.csi.leader[vt->parser.v.csi.leaderlen] = 0;
+
+ vt->parser.v.csi.argi = 0;
+ vt->parser.v.csi.args[0] = CSI_ARG_MISSING;
+ vt->parser.state = CSI_ARGS;
+
+ FALLTHROUGH;
+ case CSI_ARGS:
+ // Numerical value of argument
+ if (c >= '0' && c <= '9') {
+ if (vt->parser.v.csi.args[vt->parser.v.csi.argi] == CSI_ARG_MISSING) {
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = 0;
+ }
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] *= 10;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] += c - '0';
+ break;
+ }
+ if (c == ':') {
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] |= CSI_ARG_FLAG_MORE;
+ c = ';';
+ }
+ if (c == ';') {
+ vt->parser.v.csi.argi++;
+ vt->parser.v.csi.args[vt->parser.v.csi.argi] = CSI_ARG_MISSING;
+ break;
+ }
+
+ vt->parser.v.csi.argi++;
+ vt->parser.intermedlen = 0;
+ vt->parser.state = CSI_INTERMED;
+ FALLTHROUGH;
+ case CSI_INTERMED:
+ if (is_intermed(c)) {
+ if (vt->parser.intermedlen < INTERMED_MAX - 1) {
+ vt->parser.intermed[vt->parser.intermedlen++] = (char)c;
+ }
+ break;
+ } else if (c == 0x1b) {
+ // ESC in CSI cancels
+ } else if (c >= 0x40 && c <= 0x7e) {
+ vt->parser.intermed[vt->parser.intermedlen] = 0;
+ do_csi(vt, (char)c);
+ }
+ // else was invalid CSI
+
+ ENTER_NORMAL_STATE();
+ break;
+
+ case OSC_COMMAND:
+ // Numerical value of command
+ if (c >= '0' && c <= '9') {
+ if (vt->parser.v.osc.command == -1) {
+ vt->parser.v.osc.command = 0;
+ } else {
+ vt->parser.v.osc.command *= 10;
+ }
+ vt->parser.v.osc.command += c - '0';
+ break;
+ }
+ if (c == ';') {
+ vt->parser.state = OSC;
+ string_start = bytes + pos + 1;
+ break;
+ }
+
+ string_start = bytes + pos;
+ string_len = 0;
+ vt->parser.state = OSC;
+ goto string_state;
+
+ case DCS_COMMAND:
+ if (vt->parser.v.dcs.commandlen < CSI_LEADER_MAX) {
+ vt->parser.v.dcs.command[vt->parser.v.dcs.commandlen++] = (char)c;
+ }
+
+ if (c >= 0x40 && c <= 0x7e) {
+ string_start = bytes + pos + 1;
+ vt->parser.state = DCS_VTERM;
+ }
+ break;
+
+string_state:
+ case OSC:
+ case DCS_VTERM:
+ case APC:
+ case PM:
+ case SOS:
+ if (c == 0x07 || (c1_allowed && c == 0x9c)) {
+ string_fragment(vt, string_start, string_len, true);
+ ENTER_NORMAL_STATE();
+ }
+ break;
+
+ case NORMAL:
+ if (vt->parser.in_esc) {
+ if (is_intermed(c)) {
+ if (vt->parser.intermedlen < INTERMED_MAX - 1) {
+ vt->parser.intermed[vt->parser.intermedlen++] = (char)c;
+ }
+ } else if (c >= 0x30 && c < 0x7f) {
+ do_escape(vt, (char)c);
+ vt->parser.in_esc = 0;
+ ENTER_NORMAL_STATE();
+ } else {
+ DEBUG_LOG("TODO: Unhandled byte %02x in Escape\n", c);
+ }
+ break;
+ }
+ if (c1_allowed && c >= 0x80 && c < 0xa0) {
+ switch (c) {
+ case 0x90: // DCS
+ vt->parser.string_initial = true;
+ vt->parser.v.dcs.commandlen = 0;
+ ENTER_STATE(DCS_COMMAND);
+ break;
+ case 0x98: // SOS
+ vt->parser.string_initial = true;
+ ENTER_STATE(SOS);
+ string_start = bytes + pos + 1;
+ break;
+ case 0x9b: // CSI
+ vt->parser.v.csi.leaderlen = 0;
+ ENTER_STATE(CSI_LEADER);
+ break;
+ case 0x9d: // OSC
+ vt->parser.v.osc.command = -1;
+ vt->parser.string_initial = true;
+ ENTER_STATE(OSC_COMMAND);
+ break;
+ case 0x9e: // PM
+ vt->parser.string_initial = true;
+ ENTER_STATE(PM);
+ string_start = bytes + pos + 1;
+ break;
+ case 0x9f: // APC
+ vt->parser.string_initial = true;
+ ENTER_STATE(APC);
+ string_start = bytes + pos + 1;
+ break;
+ default:
+ do_control(vt, c);
+ break;
+ }
+ } else {
+ size_t eaten = 0;
+ if (vt->parser.callbacks && vt->parser.callbacks->text) {
+ eaten = (size_t)(*vt->parser.callbacks->text)(bytes + pos, len - pos, vt->parser.cbdata);
+ }
+
+ if (!eaten) {
+ DEBUG_LOG("libvterm: Text callback did not consume any input\n");
+ // force it to make progress
+ eaten = 1;
+ }
+
+ pos += (eaten - 1); // we'll ++ it again in a moment
+ }
+ break;
+ }
+ }
+
+ if (string_start) {
+ size_t string_len = (size_t)(bytes + pos - string_start);
+ if (string_len > 0) {
+ if (vt->parser.in_esc) {
+ string_len -= 1;
+ }
+ string_fragment(vt, string_start, string_len, false);
+ }
+ }
+
+ return len;
+}
+
+void vterm_parser_set_callbacks(VTerm *vt, const VTermParserCallbacks *callbacks, void *user)
+{
+ vt->parser.callbacks = callbacks;
+ vt->parser.cbdata = user;
+}
diff --git a/src/nvim/vterm/parser.h b/src/nvim/vterm/parser.h
new file mode 100644
index 0000000000..168be830c0
--- /dev/null
+++ b/src/nvim/vterm/parser.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/parser.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/pen.c b/src/nvim/vterm/pen.c
new file mode 100644
index 0000000000..e7f50078ae
--- /dev/null
+++ b/src/nvim/vterm/pen.c
@@ -0,0 +1,644 @@
+#include <stdio.h>
+
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/pen.c.generated.h"
+#endif
+
+// Structure used to store RGB triples without the additional metadata stored in VTermColor.
+typedef struct {
+ uint8_t red, green, blue;
+} VTermRGB;
+
+static const VTermRGB ansi_colors[] = {
+ // R G B
+ { 0, 0, 0 }, // black
+ { 224, 0, 0 }, // red
+ { 0, 224, 0 }, // green
+ { 224, 224, 0 }, // yellow
+ { 0, 0, 224 }, // blue
+ { 224, 0, 224 }, // magenta
+ { 0, 224, 224 }, // cyan
+ { 224, 224, 224 }, // white == light grey
+
+ // high intensity
+ { 128, 128, 128 }, // black
+ { 255, 64, 64 }, // red
+ { 64, 255, 64 }, // green
+ { 255, 255, 64 }, // yellow
+ { 64, 64, 255 }, // blue
+ { 255, 64, 255 }, // magenta
+ { 64, 255, 255 }, // cyan
+ { 255, 255, 255 }, // white for real
+};
+
+static uint8_t ramp6[] = {
+ 0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF,
+};
+
+static uint8_t ramp24[] = {
+ 0x00, 0x0B, 0x16, 0x21, 0x2C, 0x37, 0x42, 0x4D, 0x58, 0x63, 0x6E, 0x79,
+ 0x85, 0x90, 0x9B, 0xA6, 0xB1, 0xBC, 0xC7, 0xD2, 0xDD, 0xE8, 0xF3, 0xFF,
+};
+
+static void lookup_default_colour_ansi(long idx, VTermColor *col)
+{
+ if (idx >= 0 && idx < 16) {
+ vterm_color_rgb(col,
+ ansi_colors[idx].red, ansi_colors[idx].green, ansi_colors[idx].blue);
+ }
+}
+
+static bool lookup_colour_ansi(const VTermState *state, long index, VTermColor *col)
+{
+ if (index >= 0 && index < 16) {
+ *col = state->colors[index];
+ return true;
+ }
+
+ return false;
+}
+
+static bool lookup_colour_palette(const VTermState *state, long index, VTermColor *col)
+{
+ if (index >= 0 && index < 16) {
+ // Normal 8 colours or high intensity - parse as palette 0
+ return lookup_colour_ansi(state, index, col);
+ } else if (index >= 16 && index < 232) {
+ // 216-colour cube
+ index -= 16;
+
+ vterm_color_rgb(col, ramp6[index/6/6 % 6],
+ ramp6[index/6 % 6],
+ ramp6[index % 6]);
+
+ return true;
+ } else if (index >= 232 && index < 256) {
+ // 24 greyscales
+ index -= 232;
+
+ vterm_color_rgb(col, ramp24[index], ramp24[index], ramp24[index]);
+
+ return true;
+ }
+
+ return false;
+}
+
+static int lookup_colour(const VTermState *state, int palette, const long args[], int argcount,
+ VTermColor *col)
+{
+ switch (palette) {
+ case 2: // RGB mode - 3 args contain colour values directly
+ if (argcount < 3) {
+ return argcount;
+ }
+
+ vterm_color_rgb(col, (uint8_t)CSI_ARG(args[0]), (uint8_t)CSI_ARG(args[1]),
+ (uint8_t)CSI_ARG(args[2]));
+
+ return 3;
+
+ case 5: // XTerm 256-colour mode
+ if (!argcount || CSI_ARG_IS_MISSING(args[0])) {
+ return argcount ? 1 : 0;
+ }
+
+ vterm_color_indexed(col, (uint8_t)args[0]);
+
+ return argcount ? 1 : 0;
+
+ default:
+ DEBUG_LOG("Unrecognised colour palette %d\n", palette);
+ return 0;
+ }
+}
+
+// Some conveniences
+
+static void setpenattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
+{
+#ifdef DEBUG
+ if (type != vterm_get_attr_type(attr)) {
+ DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
+ attr, vterm_get_attr_type(attr), type);
+ return;
+ }
+#endif
+ if (state->callbacks && state->callbacks->setpenattr) {
+ (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+ }
+}
+
+static void setpenattr_bool(VTermState *state, VTermAttr attr, int boolean)
+{
+ VTermValue val = { .boolean = boolean };
+ setpenattr(state, attr, VTERM_VALUETYPE_BOOL, &val);
+}
+
+static void setpenattr_int(VTermState *state, VTermAttr attr, int number)
+{
+ VTermValue val = { .number = number };
+ setpenattr(state, attr, VTERM_VALUETYPE_INT, &val);
+}
+
+static void setpenattr_col(VTermState *state, VTermAttr attr, VTermColor color)
+{
+ VTermValue val = { .color = color };
+ setpenattr(state, attr, VTERM_VALUETYPE_COLOR, &val);
+}
+
+static void set_pen_col_ansi(VTermState *state, VTermAttr attr, long col)
+{
+ VTermColor *colp = (attr == VTERM_ATTR_BACKGROUND) ? &state->pen.bg : &state->pen.fg;
+
+ vterm_color_indexed(colp, (uint8_t)col);
+
+ setpenattr_col(state, attr, *colp);
+}
+
+void vterm_state_newpen(VTermState *state)
+{
+ // 90% grey so that pure white is brighter
+ vterm_color_rgb(&state->default_fg, 240, 240, 240);
+ vterm_color_rgb(&state->default_bg, 0, 0, 0);
+ vterm_state_set_default_colors(state, &state->default_fg, &state->default_bg);
+
+ for (int col = 0; col < 16; col++) {
+ lookup_default_colour_ansi(col, &state->colors[col]);
+ }
+}
+
+void vterm_state_resetpen(VTermState *state)
+{
+ state->pen.bold = 0; setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ state->pen.underline = 0; setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+ state->pen.italic = 0; setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ state->pen.blink = 0; setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ state->pen.reverse = 0; setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ state->pen.conceal = 0; setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ state->pen.strike = 0; setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ state->pen.font = 0; setpenattr_int(state, VTERM_ATTR_FONT, 0);
+ state->pen.small = 0; setpenattr_bool(state, VTERM_ATTR_SMALL, 0);
+ state->pen.baseline = 0; setpenattr_int(state, VTERM_ATTR_BASELINE, 0);
+
+ state->pen.fg = state->default_fg;
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->default_fg);
+ state->pen.bg = state->default_bg;
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->default_bg);
+
+ state->pen.uri = 0; setpenattr_int(state, VTERM_ATTR_URI, 0);
+}
+
+void vterm_state_savepen(VTermState *state, int save)
+{
+ if (save) {
+ state->saved.pen = state->pen;
+ } else {
+ state->pen = state->saved.pen;
+
+ setpenattr_bool(state, VTERM_ATTR_BOLD, state->pen.bold);
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, state->pen.italic);
+ setpenattr_bool(state, VTERM_ATTR_BLINK, state->pen.blink);
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, state->pen.reverse);
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, state->pen.conceal);
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, state->pen.strike);
+ setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
+
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+
+ setpenattr_int(state, VTERM_ATTR_URI, state->pen.uri);
+ }
+}
+
+void vterm_state_set_default_colors(VTermState *state, const VTermColor *default_fg,
+ const VTermColor *default_bg)
+{
+ if (default_fg) {
+ state->default_fg = *default_fg;
+ state->default_fg.type = (state->default_fg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_FG;
+ }
+
+ if (default_bg) {
+ state->default_bg = *default_bg;
+ state->default_bg.type = (state->default_bg.type & ~VTERM_COLOR_DEFAULT_MASK)
+ | VTERM_COLOR_DEFAULT_BG;
+ }
+}
+
+void vterm_state_set_palette_color(VTermState *state, int index, const VTermColor *col)
+{
+ if (index >= 0 && index < 16) {
+ state->colors[index] = *col;
+ }
+}
+
+/// Makes sure that the given color `col` is indeed an RGB colour. After this
+/// function returns, VTERM_COLOR_IS_RGB(col) will return true, while all other
+/// flags stored in `col->type` will have been reset.
+///
+/// @param state is the VTermState instance from which the colour palette should
+/// be extracted.
+/// @param col is a pointer at the VTermColor instance that should be converted
+/// to an RGB colour.
+void vterm_state_convert_color_to_rgb(const VTermState *state, VTermColor *col)
+{
+ if (VTERM_COLOR_IS_INDEXED(col)) { // Convert indexed colors to RGB
+ lookup_colour_palette(state, col->indexed.idx, col);
+ }
+ col->type &= VTERM_COLOR_TYPE_MASK; // Reset any metadata but the type
+}
+
+void vterm_state_setpen(VTermState *state, const long args[], int argcount)
+{
+ // SGR - ECMA-48 8.3.117
+
+ int argi = 0;
+ int value;
+
+ while (argi < argcount) {
+ // This logic is easier to do 'done' backwards; set it true, and make it
+ // false again in the 'default' case
+ int done = 1;
+
+ long arg;
+ switch (arg = CSI_ARG(args[argi])) {
+ case CSI_ARG_MISSING:
+ case 0: // Reset
+ vterm_state_resetpen(state);
+ break;
+
+ case 1: { // Bold on
+ const VTermColor *fg = &state->pen.fg;
+ state->pen.bold = 1;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 1);
+ if (!VTERM_COLOR_IS_DEFAULT_FG(fg) && VTERM_COLOR_IS_INDEXED(fg) && fg->indexed.idx < 8
+ && state->bold_is_highbright) {
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, fg->indexed.idx + (state->pen.bold ? 8 : 0));
+ }
+ break;
+ }
+
+ case 3: // Italic on
+ state->pen.italic = 1;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 1);
+ break;
+
+ case 4: // Underline
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ if (CSI_ARG_HAS_MORE(args[argi])) {
+ argi++;
+ switch (CSI_ARG(args[argi])) {
+ case 0:
+ state->pen.underline = 0;
+ break;
+ case 1:
+ state->pen.underline = VTERM_UNDERLINE_SINGLE;
+ break;
+ case 2:
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ break;
+ case 3:
+ state->pen.underline = VTERM_UNDERLINE_CURLY;
+ break;
+ }
+ }
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 5: // Blink
+ state->pen.blink = 1;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 1);
+ break;
+
+ case 7: // Reverse on
+ state->pen.reverse = 1;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 1);
+ break;
+
+ case 8: // Conceal on
+ state->pen.conceal = 1;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 1);
+ break;
+
+ case 9: // Strikethrough on
+ state->pen.strike = 1;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 1);
+ break;
+
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19: // Select font
+ state->pen.font = CSI_ARG(args[argi]) - 10;
+ setpenattr_int(state, VTERM_ATTR_FONT, state->pen.font);
+ break;
+
+ case 21: // Underline double
+ state->pen.underline = VTERM_UNDERLINE_DOUBLE;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, state->pen.underline);
+ break;
+
+ case 22: // Bold off
+ state->pen.bold = 0;
+ setpenattr_bool(state, VTERM_ATTR_BOLD, 0);
+ break;
+
+ case 23: // Italic and Gothic (currently unsupported) off
+ state->pen.italic = 0;
+ setpenattr_bool(state, VTERM_ATTR_ITALIC, 0);
+ break;
+
+ case 24: // Underline off
+ state->pen.underline = 0;
+ setpenattr_int(state, VTERM_ATTR_UNDERLINE, 0);
+ break;
+
+ case 25: // Blink off
+ state->pen.blink = 0;
+ setpenattr_bool(state, VTERM_ATTR_BLINK, 0);
+ break;
+
+ case 27: // Reverse off
+ state->pen.reverse = 0;
+ setpenattr_bool(state, VTERM_ATTR_REVERSE, 0);
+ break;
+
+ case 28: // Conceal off (Reveal)
+ state->pen.conceal = 0;
+ setpenattr_bool(state, VTERM_ATTR_CONCEAL, 0);
+ break;
+
+ case 29: // Strikethrough off
+ state->pen.strike = 0;
+ setpenattr_bool(state, VTERM_ATTR_STRIKE, 0);
+ break;
+
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37: // Foreground colour palette
+ value = CSI_ARG(args[argi]) - 30;
+ if (state->pen.bold && state->bold_is_highbright) {
+ value += 8;
+ }
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 38: // Foreground colour alternative palette
+ if (argcount - argi < 1) {
+ return;
+ }
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
+ argcount - argi - 2, &state->pen.fg);
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 39: // Foreground colour default
+ state->pen.fg = state->default_fg;
+ setpenattr_col(state, VTERM_ATTR_FOREGROUND, state->pen.fg);
+ break;
+
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47: // Background colour palette
+ value = CSI_ARG(args[argi]) - 40;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ case 48: // Background colour alternative palette
+ if (argcount - argi < 1) {
+ return;
+ }
+ argi += 1 + lookup_colour(state, CSI_ARG(args[argi + 1]), args + argi + 2,
+ argcount - argi - 2, &state->pen.bg);
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 49: // Default background
+ state->pen.bg = state->default_bg;
+ setpenattr_col(state, VTERM_ATTR_BACKGROUND, state->pen.bg);
+ break;
+
+ case 73: // Superscript
+ case 74: // Subscript
+ case 75: // Superscript/subscript off
+ state->pen.small = (arg != 75);
+ state->pen.baseline =
+ (arg == 73) ? VTERM_BASELINE_RAISE
+ : (arg == 74) ? VTERM_BASELINE_LOWER
+ : VTERM_BASELINE_NORMAL;
+ setpenattr_bool(state, VTERM_ATTR_SMALL, state->pen.small);
+ setpenattr_int(state, VTERM_ATTR_BASELINE, state->pen.baseline);
+ break;
+
+ case 90:
+ case 91:
+ case 92:
+ case 93:
+ case 94:
+ case 95:
+ case 96:
+ case 97: // Foreground colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 90 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_FOREGROUND, value);
+ break;
+
+ case 100:
+ case 101:
+ case 102:
+ case 103:
+ case 104:
+ case 105:
+ case 106:
+ case 107: // Background colour high-intensity palette
+ value = CSI_ARG(args[argi]) - 100 + 8;
+ set_pen_col_ansi(state, VTERM_ATTR_BACKGROUND, value);
+ break;
+
+ default:
+ done = 0;
+ break;
+ }
+
+ if (!done) {
+ DEBUG_LOG("libvterm: Unhandled CSI SGR %ld\n", arg);
+ }
+
+ while (CSI_ARG_HAS_MORE(args[argi++])) {}
+ }
+}
+
+static int vterm_state_getpen_color(const VTermColor *col, int argi, long args[], int fg)
+{
+ // Do nothing if the given color is the default color
+ if ((fg && VTERM_COLOR_IS_DEFAULT_FG(col))
+ || (!fg && VTERM_COLOR_IS_DEFAULT_BG(col))) {
+ return argi;
+ }
+
+ // Decide whether to send an indexed color or an RGB color
+ if (VTERM_COLOR_IS_INDEXED(col)) {
+ const uint8_t idx = col->indexed.idx;
+ if (idx < 8) {
+ args[argi++] = (idx + (fg ? 30 : 40));
+ } else if (idx < 16) {
+ args[argi++] = (idx - 8 + (fg ? 90 : 100));
+ } else {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 5;
+ args[argi++] = idx;
+ }
+ } else if (VTERM_COLOR_IS_RGB(col)) {
+ args[argi++] = CSI_ARG_FLAG_MORE | (fg ? 38 : 48);
+ args[argi++] = CSI_ARG_FLAG_MORE | 2;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.red;
+ args[argi++] = CSI_ARG_FLAG_MORE | col->rgb.green;
+ args[argi++] = col->rgb.blue;
+ }
+ return argi;
+}
+
+int vterm_state_getpen(VTermState *state, long args[], int argcount)
+{
+ int argi = 0;
+
+ if (state->pen.bold) {
+ args[argi++] = 1;
+ }
+
+ if (state->pen.italic) {
+ args[argi++] = 3;
+ }
+
+ if (state->pen.underline == VTERM_UNDERLINE_SINGLE) {
+ args[argi++] = 4;
+ }
+ if (state->pen.underline == VTERM_UNDERLINE_CURLY) {
+ args[argi++] = 4 | CSI_ARG_FLAG_MORE, args[argi++] = 3;
+ }
+
+ if (state->pen.blink) {
+ args[argi++] = 5;
+ }
+
+ if (state->pen.reverse) {
+ args[argi++] = 7;
+ }
+
+ if (state->pen.conceal) {
+ args[argi++] = 8;
+ }
+
+ if (state->pen.strike) {
+ args[argi++] = 9;
+ }
+
+ if (state->pen.font) {
+ args[argi++] = 10 + state->pen.font;
+ }
+
+ if (state->pen.underline == VTERM_UNDERLINE_DOUBLE) {
+ args[argi++] = 21;
+ }
+
+ argi = vterm_state_getpen_color(&state->pen.fg, argi, args, true);
+
+ argi = vterm_state_getpen_color(&state->pen.bg, argi, args, false);
+
+ if (state->pen.small) {
+ if (state->pen.baseline == VTERM_BASELINE_RAISE) {
+ args[argi++] = 73;
+ } else if (state->pen.baseline == VTERM_BASELINE_LOWER) {
+ args[argi++] = 74;
+ }
+ }
+
+ return argi;
+}
+
+int vterm_state_set_penattr(VTermState *state, VTermAttr attr, VTermValueType type, VTermValue *val)
+{
+ if (!val) {
+ return 0;
+ }
+
+ if (type != vterm_get_attr_type(attr)) {
+ DEBUG_LOG("Cannot set attr %d as it has type %d, not type %d\n",
+ attr, vterm_get_attr_type(attr), type);
+ return 0;
+ }
+
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ state->pen.bold = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_UNDERLINE:
+ state->pen.underline = (unsigned)val->number;
+ break;
+ case VTERM_ATTR_ITALIC:
+ state->pen.italic = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_BLINK:
+ state->pen.blink = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_REVERSE:
+ state->pen.reverse = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_CONCEAL:
+ state->pen.conceal = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_STRIKE:
+ state->pen.strike = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_FONT:
+ state->pen.font = (unsigned)val->number;
+ break;
+ case VTERM_ATTR_FOREGROUND:
+ state->pen.fg = val->color;
+ break;
+ case VTERM_ATTR_BACKGROUND:
+ state->pen.bg = val->color;
+ break;
+ case VTERM_ATTR_SMALL:
+ state->pen.small = (unsigned)val->boolean;
+ break;
+ case VTERM_ATTR_BASELINE:
+ state->pen.baseline = (unsigned)val->number;
+ break;
+ case VTERM_ATTR_URI:
+ state->pen.uri = val->number;
+ break;
+ default:
+ return 0;
+ }
+
+ if (state->callbacks && state->callbacks->setpenattr) {
+ (*state->callbacks->setpenattr)(attr, val, state->cbdata);
+ }
+
+ return 1;
+}
diff --git a/src/nvim/vterm/pen.h b/src/nvim/vterm/pen.h
new file mode 100644
index 0000000000..c5f5217420
--- /dev/null
+++ b/src/nvim/vterm/pen.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/pen.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/screen.c b/src/nvim/vterm/screen.c
new file mode 100644
index 0000000000..45bd5e2a27
--- /dev/null
+++ b/src/nvim/vterm/screen.c
@@ -0,0 +1,1115 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/grid.h"
+#include "nvim/mbyte.h"
+#include "nvim/tui/termkey/termkey.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/screen.c.generated.h"
+#endif
+
+#define UNICODE_SPACE 0x20
+#define UNICODE_LINEFEED 0x0a
+
+#undef DEBUG_REFLOW
+
+static inline void clearcell(const VTermScreen *screen, ScreenCell *cell)
+{
+ cell->schar = 0;
+ cell->pen = screen->pen;
+}
+
+ScreenCell *getcell(const VTermScreen *screen, int row, int col)
+{
+ if (row < 0 || row >= screen->rows) {
+ return NULL;
+ }
+ if (col < 0 || col >= screen->cols) {
+ return NULL;
+ }
+ return screen->buffer + (screen->cols * row) + col;
+}
+
+static ScreenCell *alloc_buffer(VTermScreen *screen, int rows, int cols)
+{
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(ScreenCell) * (size_t)rows * (size_t)cols);
+
+ for (int row = 0; row < rows; row++) {
+ for (int col = 0; col < cols; col++) {
+ clearcell(screen, &new_buffer[row * cols + col]);
+ }
+ }
+
+ return new_buffer;
+}
+
+static void damagerect(VTermScreen *screen, VTermRect rect)
+{
+ VTermRect emit;
+
+ switch (screen->damage_merge) {
+ case VTERM_DAMAGE_CELL:
+ // Always emit damage event
+ emit = rect;
+ break;
+
+ case VTERM_DAMAGE_ROW:
+ // Emit damage longer than one row. Try to merge with existing damage in the same row
+ if (rect.end_row > rect.start_row + 1) {
+ // Bigger than 1 line - flush existing, emit this
+ vterm_screen_flush_damage(screen);
+ emit = rect;
+ } else if (screen->damaged.start_row == -1) {
+ // None stored yet
+ screen->damaged = rect;
+ return;
+ } else if (rect.start_row == screen->damaged.start_row) {
+ // Merge with the stored line
+ if (screen->damaged.start_col > rect.start_col) {
+ screen->damaged.start_col = rect.start_col;
+ }
+ if (screen->damaged.end_col < rect.end_col) {
+ screen->damaged.end_col = rect.end_col;
+ }
+ return;
+ } else {
+ // Emit the currently stored line, store a new one
+ emit = screen->damaged;
+ screen->damaged = rect;
+ }
+ break;
+
+ case VTERM_DAMAGE_SCREEN:
+ case VTERM_DAMAGE_SCROLL:
+ // Never emit damage event
+ if (screen->damaged.start_row == -1) {
+ screen->damaged = rect;
+ } else {
+ rect_expand(&screen->damaged, &rect);
+ }
+ return;
+
+ default:
+ DEBUG_LOG("TODO: Maybe merge damage for level %d\n", screen->damage_merge);
+ return;
+ }
+
+ if (screen->callbacks && screen->callbacks->damage) {
+ (*screen->callbacks->damage)(emit, screen->cbdata);
+ }
+}
+
+static void damagescreen(VTermScreen *screen)
+{
+ VTermRect rect = {
+ .start_row = 0,
+ .end_row = screen->rows,
+ .start_col = 0,
+ .end_col = screen->cols,
+ };
+
+ damagerect(screen, rect);
+}
+
+static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user)
+{
+ VTermScreen *screen = user;
+ ScreenCell *cell = getcell(screen, pos.row, pos.col);
+
+ if (!cell) {
+ return 0;
+ }
+
+ cell->schar = info->schar;
+ if (info->schar != 0) {
+ cell->pen = screen->pen;
+ }
+
+ for (int col = 1; col < info->width; col++) {
+ getcell(screen, pos.row, pos.col + col)->schar = (uint32_t)-1;
+ }
+
+ VTermRect rect = {
+ .start_row = pos.row,
+ .end_row = pos.row + 1,
+ .start_col = pos.col,
+ .end_col = pos.col + info->width,
+ };
+
+ cell->pen.protected_cell = info->protected_cell;
+ cell->pen.dwl = info->dwl;
+ cell->pen.dhl = info->dhl;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static void sb_pushline_from_row(VTermScreen *screen, int row)
+{
+ VTermPos pos = { .row = row };
+ for (pos.col = 0; pos.col < screen->cols; pos.col++) {
+ vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col);
+ }
+
+ (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata);
+}
+
+static int moverect_internal(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->sb_pushline
+ && dest.start_row == 0 && dest.start_col == 0 // starts top-left corner
+ && dest.end_col == screen->cols // full width
+ && screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen
+ for (int row = 0; row < src.start_row; row++) {
+ sb_pushline_from_row(screen, row);
+ }
+ }
+
+ int cols = src.end_col - src.start_col;
+ int downward = src.start_row - dest.start_row;
+
+ int init_row, test_row, inc_row;
+ if (downward < 0) {
+ init_row = dest.end_row - 1;
+ test_row = dest.start_row - 1;
+ inc_row = -1;
+ } else {
+ init_row = dest.start_row;
+ test_row = dest.end_row;
+ inc_row = +1;
+ }
+
+ for (int row = init_row; row != test_row; row += inc_row) {
+ memmove(getcell(screen, row, dest.start_col),
+ getcell(screen, row + downward, src.start_col),
+ (size_t)cols * sizeof(ScreenCell));
+ }
+
+ return 1;
+}
+
+static int moverect_user(VTermRect dest, VTermRect src, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->moverect) {
+ if (screen->damage_merge != VTERM_DAMAGE_SCROLL) {
+ // Avoid an infinite loop
+ vterm_screen_flush_damage(screen);
+ }
+
+ if ((*screen->callbacks->moverect)(dest, src, screen->cbdata)) {
+ return 1;
+ }
+ }
+
+ damagerect(screen, dest);
+
+ return 1;
+}
+
+static int erase_internal(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ for (int row = rect.start_row; row < screen->state->rows && row < rect.end_row; row++) {
+ const VTermLineInfo *info = vterm_state_get_lineinfo(screen->state, row);
+
+ for (int col = rect.start_col; col < rect.end_col; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+
+ if (selective && cell->pen.protected_cell) {
+ continue;
+ }
+
+ cell->schar = 0;
+ cell->pen = (ScreenPen){
+ // Only copy .fg and .bg; leave things like rv in reset state
+ .fg = screen->pen.fg,
+ .bg = screen->pen.bg,
+ };
+ cell->pen.dwl = info->doublewidth;
+ cell->pen.dhl = info->doubleheight;
+ }
+ }
+
+ return 1;
+}
+
+static int erase_user(VTermRect rect, int selective, void *user)
+{
+ VTermScreen *screen = user;
+
+ damagerect(screen, rect);
+
+ return 1;
+}
+
+static int erase(VTermRect rect, int selective, void *user)
+{
+ erase_internal(rect, selective, user);
+ return erase_user(rect, 0, user);
+}
+
+static int scrollrect(VTermRect rect, int downward, int rightward, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->damage_merge != VTERM_DAMAGE_SCROLL) {
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ vterm_screen_flush_damage(screen);
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_user, erase_user, screen);
+
+ return 1;
+ }
+
+ if (screen->damaged.start_row != -1
+ && !rect_intersects(&rect, &screen->damaged)) {
+ vterm_screen_flush_damage(screen);
+ }
+
+ if (screen->pending_scrollrect.start_row == -1) {
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ } else if (rect_equal(&screen->pending_scrollrect, &rect)
+ && ((screen->pending_scroll_downward == 0 && downward == 0)
+ || (screen->pending_scroll_rightward == 0 && rightward == 0))) {
+ screen->pending_scroll_downward += downward;
+ screen->pending_scroll_rightward += rightward;
+ } else {
+ vterm_screen_flush_damage(screen);
+
+ screen->pending_scrollrect = rect;
+ screen->pending_scroll_downward = downward;
+ screen->pending_scroll_rightward = rightward;
+ }
+
+ vterm_scroll_rect(rect, downward, rightward,
+ moverect_internal, erase_internal, screen);
+
+ if (screen->damaged.start_row == -1) {
+ return 1;
+ }
+
+ if (rect_contains(&rect, &screen->damaged)) {
+ // Scroll region entirely contains the damage; just move it
+ vterm_rect_move(&screen->damaged, -downward, -rightward);
+ rect_clip(&screen->damaged, &rect);
+ }
+ // There are a number of possible cases here, but lets restrict this to only the common case where
+ // we might actually gain some performance by optimising it. Namely, a vertical scroll that neatly
+ // cuts the damage region in half.
+ else if (rect.start_col <= screen->damaged.start_col
+ && rect.end_col >= screen->damaged.end_col
+ && rightward == 0) {
+ if (screen->damaged.start_row >= rect.start_row
+ && screen->damaged.start_row < rect.end_row) {
+ screen->damaged.start_row -= downward;
+ if (screen->damaged.start_row < rect.start_row) {
+ screen->damaged.start_row = rect.start_row;
+ }
+ if (screen->damaged.start_row > rect.end_row) {
+ screen->damaged.start_row = rect.end_row;
+ }
+ }
+ if (screen->damaged.end_row >= rect.start_row
+ && screen->damaged.end_row < rect.end_row) {
+ screen->damaged.end_row -= downward;
+ if (screen->damaged.end_row < rect.start_row) {
+ screen->damaged.end_row = rect.start_row;
+ }
+ if (screen->damaged.end_row > rect.end_row) {
+ screen->damaged.end_row = rect.end_row;
+ }
+ }
+ } else {
+ DEBUG_LOG("TODO: Just flush and redo damaged=" STRFrect " rect=" STRFrect "\n",
+ ARGSrect(screen->damaged), ARGSrect(rect));
+ }
+
+ return 1;
+}
+
+static int movecursor(VTermPos pos, VTermPos oldpos, int visible, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->movecursor) {
+ return (*screen->callbacks->movecursor)(pos, oldpos, visible, screen->cbdata);
+ }
+
+ return 0;
+}
+
+static int setpenattr(VTermAttr attr, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ screen->pen.bold = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_UNDERLINE:
+ screen->pen.underline = (unsigned)val->number;
+ return 1;
+ case VTERM_ATTR_ITALIC:
+ screen->pen.italic = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_BLINK:
+ screen->pen.blink = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_REVERSE:
+ screen->pen.reverse = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_CONCEAL:
+ screen->pen.conceal = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_STRIKE:
+ screen->pen.strike = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_FONT:
+ screen->pen.font = (unsigned)val->number;
+ return 1;
+ case VTERM_ATTR_FOREGROUND:
+ screen->pen.fg = val->color;
+ return 1;
+ case VTERM_ATTR_BACKGROUND:
+ screen->pen.bg = val->color;
+ return 1;
+ case VTERM_ATTR_SMALL:
+ screen->pen.small = (unsigned)val->boolean;
+ return 1;
+ case VTERM_ATTR_BASELINE:
+ screen->pen.baseline = (unsigned)val->number;
+ return 1;
+ case VTERM_ATTR_URI:
+ screen->pen.uri = val->number;
+ return 1;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int settermprop(VTermProp prop, VTermValue *val, void *user)
+{
+ VTermScreen *screen = user;
+
+ switch (prop) {
+ case VTERM_PROP_ALTSCREEN:
+ if (val->boolean && !screen->buffers[BUFIDX_ALTSCREEN]) {
+ return 0;
+ }
+
+ screen->buffer =
+ val->boolean ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+ // only send a damage event on disable; because during enable there's an erase that sends a
+ // damage anyway
+ if (!val->boolean) {
+ damagescreen(screen);
+ }
+ break;
+ case VTERM_PROP_REVERSE:
+ screen->global_reverse = (unsigned)val->boolean;
+ damagescreen(screen);
+ break;
+ default:
+ ; // ignore
+ }
+
+ if (screen->callbacks && screen->callbacks->settermprop) {
+ return (*screen->callbacks->settermprop)(prop, val, screen->cbdata);
+ }
+
+ return 1;
+}
+
+static int bell(void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->bell) {
+ return (*screen->callbacks->bell)(screen->cbdata);
+ }
+
+ return 0;
+}
+
+/// How many cells are non-blank Returns the position of the first blank cell in the trailing blank
+/// end
+static int line_popcount(ScreenCell *buffer, int row, int rows, int cols)
+{
+ int col = cols - 1;
+ while (col >= 0 && buffer[row * cols + col].schar == 0) {
+ col--;
+ }
+ return col + 1;
+}
+
+static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new_cols, bool active,
+ VTermStateFields *statefields)
+{
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ ScreenCell *old_buffer = screen->buffers[bufidx];
+ VTermLineInfo *old_lineinfo = statefields->lineinfos[bufidx];
+
+ ScreenCell *new_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(ScreenCell) * (size_t)new_rows *
+ (size_t)new_cols);
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt,
+ sizeof(new_lineinfo[0]) * (size_t)new_rows);
+
+ int old_row = old_rows - 1;
+ int new_row = new_rows - 1;
+
+ VTermPos old_cursor = statefields->pos;
+ VTermPos new_cursor = { -1, -1 };
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, "Resizing from %dx%d to %dx%d; cursor was at (%d,%d)\n",
+ old_cols, old_rows, new_cols, new_rows, old_cursor.col, old_cursor.row);
+#endif
+
+ // Keep track of the final row that is knonw to be blank, so we know what spare space we have for
+ // scrolling into
+ int final_blank_row = new_rows;
+
+ while (old_row >= 0) {
+ int old_row_end = old_row;
+ // TODO(vterm): Stop if dwl or dhl
+ while (screen->reflow && old_lineinfo && old_row > 0 && old_lineinfo[old_row].continuation) {
+ old_row--;
+ }
+ int old_row_start = old_row;
+
+ int width = 0;
+ for (int row = old_row_start; row <= old_row_end; row++) {
+ if (screen->reflow && row < (old_rows - 1) && old_lineinfo[row + 1].continuation) {
+ width += old_cols;
+ } else {
+ width += line_popcount(old_buffer, row, old_rows, old_cols);
+ }
+ }
+
+ if (final_blank_row == (new_row + 1) && width == 0) {
+ final_blank_row = new_row;
+ }
+
+ int new_height = screen->reflow
+ ? width ? (width + new_cols - 1) / new_cols : 1
+ : 1;
+
+ int new_row_end = new_row;
+ int new_row_start = new_row - new_height + 1;
+
+ old_row = old_row_start;
+ int old_col = 0;
+
+ int spare_rows = new_rows - final_blank_row;
+
+ if (new_row_start < 0 // we'd fall off the top
+ && spare_rows >= 0 // we actually have spare rows
+ && (!active || new_cursor.row == -1 || (new_cursor.row - new_row_start) < new_rows)) {
+ // Attempt to scroll content down into the blank rows at the bottom to make it fit
+ int downwards = -new_row_start;
+ if (downwards > spare_rows) {
+ downwards = spare_rows;
+ }
+ int rowcount = new_rows - downwards;
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " scroll %d rows +%d downwards\n", rowcount, downwards);
+#endif
+
+ memmove(&new_buffer[downwards * new_cols], &new_buffer[0],
+ (size_t)rowcount * (size_t)new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[downwards], &new_lineinfo[0],
+ (size_t)rowcount * sizeof(new_lineinfo[0]));
+
+ new_row += downwards;
+ new_row_start += downwards;
+ new_row_end += downwards;
+
+ if (new_cursor.row >= 0) {
+ new_cursor.row += downwards;
+ }
+
+ final_blank_row += downwards;
+ }
+
+#ifdef DEBUG_REFLOW
+ fprintf(stderr, " rows [%d..%d] <- [%d..%d] width=%d\n",
+ new_row_start, new_row_end, old_row_start, old_row_end, width);
+#endif
+
+ if (new_row_start < 0) {
+ if (old_row_start <= old_cursor.row && old_cursor.row <= old_row_end) {
+ new_cursor.row = 0;
+ new_cursor.col = old_cursor.col;
+ if (new_cursor.col >= new_cols) {
+ new_cursor.col = new_cols - 1;
+ }
+ }
+ break;
+ }
+
+ for (new_row = new_row_start, old_row = old_row_start; new_row <= new_row_end; new_row++) {
+ int count = width >= new_cols ? new_cols : width;
+ width -= count;
+
+ int new_col = 0;
+
+ while (count) {
+ // TODO(vterm): This could surely be done a lot faster by memcpy()'ing the entire range
+ new_buffer[new_row * new_cols + new_col] = old_buffer[old_row * old_cols + old_col];
+
+ if (old_cursor.row == old_row && old_cursor.col == old_col) {
+ new_cursor.row = new_row, new_cursor.col = new_col;
+ }
+
+ old_col++;
+ if (old_col == old_cols) {
+ old_row++;
+
+ if (!screen->reflow) {
+ new_col++;
+ break;
+ }
+ old_col = 0;
+ }
+
+ new_col++;
+ count--;
+ }
+
+ if (old_cursor.row == old_row && old_cursor.col >= old_col) {
+ new_cursor.row = new_row, new_cursor.col = (old_cursor.col - old_col + new_col);
+ if (new_cursor.col >= new_cols) {
+ new_cursor.col = new_cols - 1;
+ }
+ }
+
+ while (new_col < new_cols) {
+ clearcell(screen, &new_buffer[new_row * new_cols + new_col]);
+ new_col++;
+ }
+
+ new_lineinfo[new_row].continuation = (new_row > new_row_start);
+ }
+
+ old_row = old_row_start - 1;
+ new_row = new_row_start - 1;
+ }
+
+ if (old_cursor.row <= old_row) {
+ // cursor would have moved entirely off the top of the screen; lets just bring it within range
+ new_cursor.row = 0, new_cursor.col = old_cursor.col;
+ if (new_cursor.col >= new_cols) {
+ new_cursor.col = new_cols - 1;
+ }
+ }
+
+ // We really expect the cursor position to be set by now
+ if (active && (new_cursor.row == -1 || new_cursor.col == -1)) {
+ fprintf(stderr, "screen_resize failed to update cursor position\n");
+ abort();
+ }
+
+ if (old_row >= 0 && bufidx == BUFIDX_PRIMARY) {
+ // Push spare lines to scrollback buffer
+ if (screen->callbacks && screen->callbacks->sb_pushline) {
+ for (int row = 0; row <= old_row; row++) {
+ sb_pushline_from_row(screen, row);
+ }
+ }
+ if (active) {
+ statefields->pos.row -= (old_row + 1);
+ }
+ }
+ if (new_row >= 0 && bufidx == BUFIDX_PRIMARY
+ && screen->callbacks && screen->callbacks->sb_popline) {
+ // Try to backfill rows by popping scrollback buffer
+ while (new_row >= 0) {
+ if (!(screen->callbacks->sb_popline(old_cols, screen->sb_buffer, screen->cbdata))) {
+ break;
+ }
+
+ VTermPos pos = { .row = new_row };
+ for (pos.col = 0; pos.col < old_cols && pos.col < new_cols;
+ pos.col += screen->sb_buffer[pos.col].width) {
+ VTermScreenCell *src = &screen->sb_buffer[pos.col];
+ ScreenCell *dst = &new_buffer[pos.row * new_cols + pos.col];
+
+ dst->schar = src->schar;
+
+ dst->pen.bold = src->attrs.bold;
+ dst->pen.underline = src->attrs.underline;
+ dst->pen.italic = src->attrs.italic;
+ dst->pen.blink = src->attrs.blink;
+ dst->pen.reverse = src->attrs.reverse ^ screen->global_reverse;
+ dst->pen.conceal = src->attrs.conceal;
+ dst->pen.strike = src->attrs.strike;
+ dst->pen.font = src->attrs.font;
+ dst->pen.small = src->attrs.small;
+ dst->pen.baseline = src->attrs.baseline;
+
+ dst->pen.fg = src->fg;
+ dst->pen.bg = src->bg;
+
+ dst->pen.uri = src->uri;
+
+ if (src->width == 2 && pos.col < (new_cols - 1)) {
+ (dst + 1)->schar = (uint32_t)-1;
+ }
+ }
+ for (; pos.col < new_cols; pos.col++) {
+ clearcell(screen, &new_buffer[pos.row * new_cols + pos.col]);
+ }
+ new_row--;
+
+ if (active) {
+ statefields->pos.row++;
+ }
+ }
+ }
+ if (new_row >= 0) {
+ // Scroll new rows back up to the top and fill in blanks at the bottom
+ int moverows = new_rows - new_row - 1;
+ memmove(&new_buffer[0], &new_buffer[(new_row + 1) * new_cols],
+ (size_t)moverows * (size_t)new_cols * sizeof(ScreenCell));
+ memmove(&new_lineinfo[0], &new_lineinfo[new_row + 1],
+ (size_t)moverows * sizeof(new_lineinfo[0]));
+
+ new_cursor.row -= (new_row + 1);
+
+ for (new_row = moverows; new_row < new_rows; new_row++) {
+ for (int col = 0; col < new_cols; col++) {
+ clearcell(screen, &new_buffer[new_row * new_cols + col]);
+ }
+ new_lineinfo[new_row] = (VTermLineInfo){ 0 };
+ }
+ }
+
+ vterm_allocator_free(screen->vt, old_buffer);
+ screen->buffers[bufidx] = new_buffer;
+
+ vterm_allocator_free(screen->vt, old_lineinfo);
+ statefields->lineinfos[bufidx] = new_lineinfo;
+
+ if (active) {
+ statefields->pos = new_cursor;
+ }
+}
+
+static int resize(int new_rows, int new_cols, VTermStateFields *fields, void *user)
+{
+ VTermScreen *screen = user;
+
+ int altscreen_active = (screen->buffers[BUFIDX_ALTSCREEN]
+ && screen->buffer == screen->buffers[BUFIDX_ALTSCREEN]);
+
+ int old_rows = screen->rows;
+ int old_cols = screen->cols;
+
+ if (new_cols > old_cols) {
+ // Ensure that ->sb_buffer is large enough for a new or and old row
+ if (screen->sb_buffer) {
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+ }
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(VTermScreenCell) * (size_t)new_cols);
+ }
+
+ resize_buffer(screen, 0, new_rows, new_cols, !altscreen_active, fields);
+ if (screen->buffers[BUFIDX_ALTSCREEN]) {
+ resize_buffer(screen, 1, new_rows, new_cols, altscreen_active, fields);
+ } else if (new_rows != old_rows) {
+ // We don't need a full resize of the altscreen because it isn't enabled but we should at least
+ // keep the lineinfo the right size
+ vterm_allocator_free(screen->vt, fields->lineinfos[BUFIDX_ALTSCREEN]);
+
+ VTermLineInfo *new_lineinfo = vterm_allocator_malloc(screen->vt,
+ sizeof(new_lineinfo[0]) *
+ (size_t)new_rows);
+ for (int row = 0; row < new_rows; row++) {
+ new_lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+
+ fields->lineinfos[BUFIDX_ALTSCREEN] = new_lineinfo;
+ }
+
+ screen->buffer =
+ altscreen_active ? screen->buffers[BUFIDX_ALTSCREEN] : screen->buffers[BUFIDX_PRIMARY];
+
+ screen->rows = new_rows;
+ screen->cols = new_cols;
+
+ if (new_cols <= old_cols) {
+ if (screen->sb_buffer) {
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+ }
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt,
+ sizeof(VTermScreenCell) * (size_t)new_cols);
+ }
+
+ // TODO(vterm): Maaaaybe we can optimise this if there's no reflow happening
+ damagescreen(screen);
+
+ if (screen->callbacks && screen->callbacks->resize) {
+ return (*screen->callbacks->resize)(new_rows, new_cols, screen->cbdata);
+ }
+
+ return 1;
+}
+
+static int theme(bool *dark, void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->theme) {
+ return (*screen->callbacks->theme)(dark, screen->cbdata);
+ }
+
+ return 1;
+}
+
+static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
+ void *user)
+{
+ VTermScreen *screen = user;
+
+ if (newinfo->doublewidth != oldinfo->doublewidth
+ || newinfo->doubleheight != oldinfo->doubleheight) {
+ for (int col = 0; col < screen->cols; col++) {
+ ScreenCell *cell = getcell(screen, row, col);
+ cell->pen.dwl = newinfo->doublewidth;
+ cell->pen.dhl = newinfo->doubleheight;
+ }
+
+ VTermRect rect = {
+ .start_row = row,
+ .end_row = row + 1,
+ .start_col = 0,
+ .end_col = newinfo->doublewidth ? screen->cols / 2 : screen->cols,
+ };
+ damagerect(screen, rect);
+
+ if (newinfo->doublewidth) {
+ rect.start_col = screen->cols / 2;
+ rect.end_col = screen->cols;
+
+ erase_internal(rect, 0, user);
+ }
+ }
+
+ return 1;
+}
+
+static int sb_clear(void *user)
+{
+ VTermScreen *screen = user;
+
+ if (screen->callbacks && screen->callbacks->sb_clear) {
+ if ((*screen->callbacks->sb_clear)(screen->cbdata)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static VTermStateCallbacks state_cbs = {
+ .putglyph = &putglyph,
+ .movecursor = &movecursor,
+ .scrollrect = &scrollrect,
+ .erase = &erase,
+ .setpenattr = &setpenattr,
+ .settermprop = &settermprop,
+ .bell = &bell,
+ .resize = &resize,
+ .theme = &theme,
+ .setlineinfo = &setlineinfo,
+ .sb_clear = &sb_clear,
+};
+
+static VTermScreen *screen_new(VTerm *vt)
+{
+ VTermState *state = vterm_obtain_state(vt);
+ if (!state) {
+ return NULL;
+ }
+
+ VTermScreen *screen = vterm_allocator_malloc(vt, sizeof(VTermScreen));
+ int rows, cols;
+
+ vterm_get_size(vt, &rows, &cols);
+
+ screen->vt = vt;
+ screen->state = state;
+
+ screen->damage_merge = VTERM_DAMAGE_CELL;
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+
+ screen->rows = rows;
+ screen->cols = cols;
+
+ screen->global_reverse = false;
+ screen->reflow = false;
+
+ screen->callbacks = NULL;
+ screen->cbdata = NULL;
+
+ screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols);
+
+ screen->buffer = screen->buffers[BUFIDX_PRIMARY];
+
+ screen->sb_buffer = vterm_allocator_malloc(screen->vt, sizeof(VTermScreenCell) * (size_t)cols);
+
+ vterm_state_set_callbacks(screen->state, &state_cbs, screen);
+
+ return screen;
+}
+
+void vterm_screen_free(VTermScreen *screen)
+{
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_PRIMARY]);
+ if (screen->buffers[BUFIDX_ALTSCREEN]) {
+ vterm_allocator_free(screen->vt, screen->buffers[BUFIDX_ALTSCREEN]);
+ }
+
+ vterm_allocator_free(screen->vt, screen->sb_buffer);
+
+ vterm_allocator_free(screen->vt, screen);
+}
+
+void vterm_screen_reset(VTermScreen *screen, int hard)
+{
+ screen->damaged.start_row = -1;
+ screen->pending_scrollrect.start_row = -1;
+ vterm_state_reset(screen->state, hard);
+ vterm_screen_flush_damage(screen);
+}
+
+// Copy internal to external representation of a screen cell
+int vterm_screen_get_cell(const VTermScreen *screen, VTermPos pos, VTermScreenCell *cell)
+{
+ ScreenCell *intcell = getcell(screen, pos.row, pos.col);
+ if (!intcell) {
+ return 0;
+ }
+
+ cell->schar = (intcell->schar == (uint32_t)-1) ? 0 : intcell->schar;
+
+ cell->attrs.bold = intcell->pen.bold;
+ cell->attrs.underline = intcell->pen.underline;
+ cell->attrs.italic = intcell->pen.italic;
+ cell->attrs.blink = intcell->pen.blink;
+ cell->attrs.reverse = intcell->pen.reverse ^ screen->global_reverse;
+ cell->attrs.conceal = intcell->pen.conceal;
+ cell->attrs.strike = intcell->pen.strike;
+ cell->attrs.font = intcell->pen.font;
+ cell->attrs.small = intcell->pen.small;
+ cell->attrs.baseline = intcell->pen.baseline;
+
+ cell->attrs.dwl = intcell->pen.dwl;
+ cell->attrs.dhl = intcell->pen.dhl;
+
+ cell->fg = intcell->pen.fg;
+ cell->bg = intcell->pen.bg;
+
+ cell->uri = intcell->pen.uri;
+
+ if (pos.col < (screen->cols - 1)
+ && getcell(screen, pos.row, pos.col + 1)->schar == (uint32_t)-1) {
+ cell->width = 2;
+ } else {
+ cell->width = 1;
+ }
+
+ return 1;
+}
+
+VTermScreen *vterm_obtain_screen(VTerm *vt)
+{
+ if (vt->screen) {
+ return vt->screen;
+ }
+
+ VTermScreen *screen = screen_new(vt);
+ vt->screen = screen;
+
+ return screen;
+}
+
+void vterm_screen_enable_reflow(VTermScreen *screen, bool reflow)
+{
+ screen->reflow = reflow;
+}
+
+#undef vterm_screen_set_reflow
+void vterm_screen_set_reflow(VTermScreen *screen, bool reflow)
+{
+ vterm_screen_enable_reflow(screen, reflow);
+}
+
+void vterm_screen_enable_altscreen(VTermScreen *screen, int altscreen)
+{
+ if (!screen->buffers[BUFIDX_ALTSCREEN] && altscreen) {
+ int rows, cols;
+ vterm_get_size(screen->vt, &rows, &cols);
+
+ screen->buffers[BUFIDX_ALTSCREEN] = alloc_buffer(screen, rows, cols);
+ }
+}
+
+void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks,
+ void *user)
+{
+ screen->callbacks = callbacks;
+ screen->cbdata = user;
+}
+
+void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen,
+ const VTermStateFallbacks *fallbacks, void *user)
+{
+ vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user);
+}
+
+void vterm_screen_flush_damage(VTermScreen *screen)
+{
+ if (screen->pending_scrollrect.start_row != -1) {
+ vterm_scroll_rect(screen->pending_scrollrect, screen->pending_scroll_downward,
+ screen->pending_scroll_rightward,
+ moverect_user, erase_user, screen);
+
+ screen->pending_scrollrect.start_row = -1;
+ }
+
+ if (screen->damaged.start_row != -1) {
+ if (screen->callbacks && screen->callbacks->damage) {
+ (*screen->callbacks->damage)(screen->damaged, screen->cbdata);
+ }
+
+ screen->damaged.start_row = -1;
+ }
+}
+
+void vterm_screen_set_damage_merge(VTermScreen *screen, VTermDamageSize size)
+{
+ vterm_screen_flush_damage(screen);
+ screen->damage_merge = size;
+}
+
+/// Same as vterm_state_convert_color_to_rgb(), but takes a `screen` instead of a `state` instance.
+void vterm_screen_convert_color_to_rgb(const VTermScreen *screen, VTermColor *col)
+{
+ vterm_state_convert_color_to_rgb(screen->state, col);
+}
+
+// Some utility functions on VTermRect structures
+
+#define STRFrect "(%d,%d-%d,%d)"
+#define ARGSrect(r) (r).start_row, (r).start_col, (r).end_row, (r).end_col
+
+// Expand dst to contain src as well
+void rect_expand(VTermRect *dst, VTermRect *src)
+{
+ if (dst->start_row > src->start_row) {
+ dst->start_row = src->start_row;
+ }
+ if (dst->start_col > src->start_col) {
+ dst->start_col = src->start_col;
+ }
+ if (dst->end_row < src->end_row) {
+ dst->end_row = src->end_row;
+ }
+ if (dst->end_col < src->end_col) {
+ dst->end_col = src->end_col;
+ }
+}
+
+// Clip the dst to ensure it does not step outside of bounds
+void rect_clip(VTermRect *dst, VTermRect *bounds)
+{
+ if (dst->start_row < bounds->start_row) {
+ dst->start_row = bounds->start_row;
+ }
+ if (dst->start_col < bounds->start_col) {
+ dst->start_col = bounds->start_col;
+ }
+ if (dst->end_row > bounds->end_row) {
+ dst->end_row = bounds->end_row;
+ }
+ if (dst->end_col > bounds->end_col) {
+ dst->end_col = bounds->end_col;
+ }
+ // Ensure it doesn't end up negatively-sized
+ if (dst->end_row < dst->start_row) {
+ dst->end_row = dst->start_row;
+ }
+ if (dst->end_col < dst->start_col) {
+ dst->end_col = dst->start_col;
+ }
+}
+
+// True if the two rectangles are equal
+int rect_equal(VTermRect *a, VTermRect *b)
+{
+ return (a->start_row == b->start_row)
+ && (a->start_col == b->start_col)
+ && (a->end_row == b->end_row)
+ && (a->end_col == b->end_col);
+}
+
+// True if small is contained entirely within big
+int rect_contains(VTermRect *big, VTermRect *small)
+{
+ if (small->start_row < big->start_row) {
+ return 0;
+ }
+ if (small->start_col < big->start_col) {
+ return 0;
+ }
+ if (small->end_row > big->end_row) {
+ return 0;
+ }
+ if (small->end_col > big->end_col) {
+ return 0;
+ }
+ return 1;
+}
+
+// True if the rectangles overlap at all
+int rect_intersects(VTermRect *a, VTermRect *b)
+{
+ if (a->start_row > b->end_row || b->start_row > a->end_row) {
+ return 0;
+ }
+ if (a->start_col > b->end_col || b->start_col > a->end_col) {
+ return 0;
+ }
+ return 1;
+}
diff --git a/src/nvim/vterm/screen.h b/src/nvim/vterm/screen.h
new file mode 100644
index 0000000000..fa7520d75a
--- /dev/null
+++ b/src/nvim/vterm/screen.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <stddef.h>
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/screen.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/state.c b/src/nvim/vterm/state.c
new file mode 100644
index 0000000000..0e43107347
--- /dev/null
+++ b/src/nvim/vterm/state.c
@@ -0,0 +1,2467 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nvim/grid.h"
+#include "nvim/mbyte.h"
+#include "nvim/vterm/encoding.h"
+#include "nvim/vterm/parser.h"
+#include "nvim/vterm/pen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/state.c.generated.h"
+#endif
+
+#define strneq(a, b, n) (strncmp(a, b, n) == 0)
+
+// Some convenient wrappers to make callback functions easier
+
+static void putglyph(VTermState *state, const schar_T schar, int width, VTermPos pos)
+{
+ VTermGlyphInfo info = {
+ .schar = schar,
+ .width = width,
+ .protected_cell = state->protected_cell,
+ .dwl = state->lineinfo[pos.row].doublewidth,
+ .dhl = state->lineinfo[pos.row].doubleheight,
+ };
+
+ if (state->callbacks && state->callbacks->putglyph) {
+ if ((*state->callbacks->putglyph)(&info, pos, state->cbdata)) {
+ return;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled putglyph U+%04x at (%d,%d)\n", chars[0], pos.col, pos.row);
+}
+
+static void updatecursor(VTermState *state, VTermPos *oldpos, int cancel_phantom)
+{
+ if (state->pos.col == oldpos->col && state->pos.row == oldpos->row) {
+ return;
+ }
+
+ if (cancel_phantom) {
+ state->at_phantom = 0;
+ }
+
+ if (state->callbacks && state->callbacks->movecursor) {
+ if ((*state->callbacks->movecursor)(state->pos, *oldpos, state->mode.cursor_visible,
+ state->cbdata)) {
+ return;
+ }
+ }
+}
+
+static void erase(VTermState *state, VTermRect rect, int selective)
+{
+ if (rect.end_col == state->cols) {
+ // If we're erasing the final cells of any lines, cancel the continuation marker on the
+ // subsequent line
+ for (int row = rect.start_row + 1; row < rect.end_row + 1 && row < state->rows; row++) {
+ state->lineinfo[row].continuation = 0;
+ }
+ }
+
+ if (state->callbacks && state->callbacks->erase) {
+ if ((*state->callbacks->erase)(rect, selective, state->cbdata)) {
+ return;
+ }
+ }
+}
+
+static VTermState *vterm_state_new(VTerm *vt)
+{
+ VTermState *state = vterm_allocator_malloc(vt, sizeof(VTermState));
+
+ state->vt = vt;
+
+ state->rows = vt->rows;
+ state->cols = vt->cols;
+
+ state->mouse_col = 0;
+ state->mouse_row = 0;
+ state->mouse_buttons = 0;
+
+ state->mouse_protocol = MOUSE_X10;
+
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+
+ state->selection.callbacks = NULL;
+ state->selection.user = NULL;
+ state->selection.buffer = NULL;
+
+ vterm_state_newpen(state);
+
+ state->bold_is_highbright = 0;
+
+ state->combine_pos.row = -1;
+
+ state->tabstops = vterm_allocator_malloc(state->vt, ((size_t)state->cols + 7) / 8);
+
+ state->lineinfos[BUFIDX_PRIMARY] = vterm_allocator_malloc(state->vt,
+ (size_t)state->rows *
+ sizeof(VTermLineInfo));
+ // TODO(vterm): Make an 'enable' function
+ state->lineinfos[BUFIDX_ALTSCREEN] = vterm_allocator_malloc(state->vt,
+ (size_t)state->rows *
+ sizeof(VTermLineInfo));
+ state->lineinfo = state->lineinfos[BUFIDX_PRIMARY];
+
+ state->encoding_utf8.enc = vterm_lookup_encoding(ENC_UTF8, 'u');
+ if (*state->encoding_utf8.enc->init) {
+ (*state->encoding_utf8.enc->init)(state->encoding_utf8.enc, state->encoding_utf8.data);
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(state->key_encoding_stacks); i++) {
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[i];
+ for (size_t j = 0; j < ARRAY_SIZE(stack->items); j++) {
+ memset(&stack->items[j], 0, sizeof(stack->items[j]));
+ }
+
+ stack->size = 1;
+ }
+
+ return state;
+}
+
+void vterm_state_free(VTermState *state)
+{
+ vterm_allocator_free(state->vt, state->tabstops);
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_PRIMARY]);
+ if (state->lineinfos[BUFIDX_ALTSCREEN]) {
+ vterm_allocator_free(state->vt, state->lineinfos[BUFIDX_ALTSCREEN]);
+ }
+ vterm_allocator_free(state->vt, state);
+}
+
+static void scroll(VTermState *state, VTermRect rect, int downward, int rightward)
+{
+ if (!downward && !rightward) {
+ return;
+ }
+
+ int rows = rect.end_row - rect.start_row;
+ if (downward > rows) {
+ downward = rows;
+ } else if (downward < -rows) {
+ downward = -rows;
+ }
+
+ int cols = rect.end_col - rect.start_col;
+ if (rightward > cols) {
+ rightward = cols;
+ } else if (rightward < -cols) {
+ rightward = -cols;
+ }
+
+ // Update lineinfo if full line
+ if (rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) {
+ int height = rect.end_row - rect.start_row - abs(downward);
+
+ if (downward > 0) {
+ memmove(state->lineinfo + rect.start_row,
+ state->lineinfo + rect.start_row + downward,
+ (size_t)height * sizeof(state->lineinfo[0]));
+ for (int row = rect.end_row - downward; row < rect.end_row; row++) {
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ } else {
+ memmove(state->lineinfo + rect.start_row - downward,
+ state->lineinfo + rect.start_row,
+ (size_t)height * sizeof(state->lineinfo[0]));
+ for (int row = rect.start_row; row < rect.start_row - downward; row++) {
+ state->lineinfo[row] = (VTermLineInfo){ 0 };
+ }
+ }
+ }
+
+ if (state->callbacks && state->callbacks->scrollrect) {
+ if ((*state->callbacks->scrollrect)(rect, downward, rightward, state->cbdata)) {
+ return;
+ }
+ }
+
+ if (state->callbacks) {
+ vterm_scroll_rect(rect, downward, rightward,
+ state->callbacks->moverect, state->callbacks->erase, state->cbdata);
+ }
+}
+
+static void linefeed(VTermState *state)
+{
+ if (state->pos.row == SCROLLREGION_BOTTOM(state) - 1) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, 1, 0);
+ } else if (state->pos.row < state->rows - 1) {
+ state->pos.row++;
+ }
+}
+
+static void set_col_tabstop(VTermState *state, int col)
+{
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ state->tabstops[col >> 3] |= mask;
+}
+
+static void clear_col_tabstop(VTermState *state, int col)
+{
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ state->tabstops[col >> 3] &= ~mask;
+}
+
+static int is_col_tabstop(VTermState *state, int col)
+{
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ return state->tabstops[col >> 3] & mask;
+}
+
+static int is_cursor_in_scrollregion(const VTermState *state)
+{
+ if (state->pos.row < state->scrollregion_top
+ || state->pos.row >= SCROLLREGION_BOTTOM(state)) {
+ return 0;
+ }
+ if (state->pos.col < SCROLLREGION_LEFT(state)
+ || state->pos.col >= SCROLLREGION_RIGHT(state)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void tab(VTermState *state, int count, int direction)
+{
+ while (count > 0) {
+ if (direction > 0) {
+ if (state->pos.col >= THISROWWIDTH(state) - 1) {
+ return;
+ }
+
+ state->pos.col++;
+ } else if (direction < 0) {
+ if (state->pos.col < 1) {
+ return;
+ }
+
+ state->pos.col--;
+ }
+
+ if (is_col_tabstop(state, state->pos.col)) {
+ count--;
+ }
+ }
+}
+
+#define NO_FORCE 0
+#define FORCE 1
+
+#define DWL_OFF 0
+#define DWL_ON 1
+
+#define DHL_OFF 0
+#define DHL_TOP 1
+#define DHL_BOTTOM 2
+
+static void set_lineinfo(VTermState *state, int row, int force, int dwl, int dhl)
+{
+ VTermLineInfo info = state->lineinfo[row];
+
+ if (dwl == DWL_OFF) {
+ info.doublewidth = DWL_OFF;
+ } else if (dwl == DWL_ON) {
+ info.doublewidth = DWL_ON;
+ }
+ // else -1 to ignore
+
+ if (dhl == DHL_OFF) {
+ info.doubleheight = DHL_OFF;
+ } else if (dhl == DHL_TOP) {
+ info.doubleheight = DHL_TOP;
+ } else if (dhl == DHL_BOTTOM) {
+ info.doubleheight = DHL_BOTTOM;
+ }
+
+ if ((state->callbacks
+ && state->callbacks->setlineinfo
+ && (*state->callbacks->setlineinfo)(row, &info, state->lineinfo + row, state->cbdata))
+ || force) {
+ state->lineinfo[row] = info;
+ }
+}
+
+static int on_text(const char bytes[], size_t len, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ uint32_t *codepoints = (uint32_t *)(state->vt->tmpbuffer);
+ size_t maxpoints = (state->vt->tmpbuffer_len) / sizeof(uint32_t);
+
+ int npoints = 0;
+ size_t eaten = 0;
+
+ VTermEncodingInstance *encoding =
+ state->gsingle_set ? &state->encoding[state->gsingle_set]
+ : !(bytes[eaten] & 0x80) ? &state->encoding[state->gl_set]
+ : state->vt->mode.utf8 ? &state->encoding_utf8
+ : &state->encoding[state->
+ gr_set];
+
+ (*encoding->enc->decode)(encoding->enc, encoding->data,
+ codepoints, &npoints, state->gsingle_set ? 1 : (int)maxpoints,
+ bytes, &eaten, len);
+
+ // There's a chance an encoding (e.g. UTF-8) hasn't found enough bytes yet for even a single codepoint
+ if (!npoints) {
+ return (int)eaten;
+ }
+
+ if (state->gsingle_set && npoints) {
+ state->gsingle_set = 0;
+ }
+
+ int i = 0;
+ GraphemeState grapheme_state = GRAPHEME_STATE_INIT;
+ size_t grapheme_len = 0;
+ bool recombine = false;
+
+ // See if the cursor has moved since
+ if (state->pos.row == state->combine_pos.row
+ && state->pos.col == state->combine_pos.col + state->combine_width) {
+ // This is a combining char. that needs to be merged with the previous glyph output
+ if (utf_iscomposing((int)state->grapheme_last, (int)codepoints[i], &state->grapheme_state)) {
+ // Find where we need to append these combining chars
+ grapheme_len = state->grapheme_len;
+ grapheme_state = state->grapheme_state;
+ state->pos.col = state->combine_pos.col;
+ recombine = true;
+ } else {
+ DEBUG_LOG("libvterm: TODO: Skip over split char+combining\n");
+ }
+ }
+
+ while (i < npoints) {
+ // Try to find combining characters following this
+ do {
+ if (grapheme_len < sizeof(state->grapheme_buf) - 4) {
+ grapheme_len += (size_t)utf_char2bytes((int)codepoints[i],
+ state->grapheme_buf + grapheme_len);
+ }
+ i++;
+ } while (i < npoints && utf_iscomposing((int)codepoints[i - 1], (int)codepoints[i],
+ &grapheme_state));
+
+ int width = utf_ptr2cells_len(state->grapheme_buf, (int)grapheme_len);
+
+ if (state->at_phantom || state->pos.col + width > THISROWWIDTH(state)) {
+ linefeed(state);
+ state->pos.col = 0;
+ state->at_phantom = 0;
+ state->lineinfo[state->pos.row].continuation = 1;
+ }
+
+ if (state->mode.insert && !recombine) {
+ // TODO(vterm): This will be a little inefficient for large bodies of text, as it'll have to
+ // 'ICH' effectively before every glyph. We should scan ahead and ICH as many times as
+ // required
+ VTermRect rect = {
+ .start_row = state->pos.row,
+ .end_row = state->pos.row + 1,
+ .start_col = state->pos.col,
+ .end_col = THISROWWIDTH(state),
+ };
+ scroll(state, rect, 0, -1);
+ }
+
+ schar_T sc = schar_from_buf(state->grapheme_buf, grapheme_len);
+ putglyph(state, sc, width, state->pos);
+
+ if (i == npoints) {
+ // End of the buffer. Save the chars in case we have to combine with more on the next call
+ state->grapheme_len = grapheme_len;
+ state->grapheme_last = codepoints[i - 1];
+ state->grapheme_state = grapheme_state;
+ state->combine_width = width;
+ state->combine_pos = state->pos;
+ } else {
+ grapheme_len = 0;
+ recombine = false;
+ }
+
+ if (state->pos.col + width >= THISROWWIDTH(state)) {
+ if (state->mode.autowrap) {
+ state->at_phantom = 1;
+ }
+ } else {
+ state->pos.col += width;
+ }
+ }
+
+ updatecursor(state, &oldpos, 0);
+
+#ifdef DEBUG
+ if (state->pos.row < 0 || state->pos.row >= state->rows
+ || state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after text: (%d,%d)\n",
+ state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return (int)eaten;
+}
+
+static int on_control(uint8_t control, void *user)
+{
+ VTermState *state = user;
+
+ VTermPos oldpos = state->pos;
+
+ switch (control) {
+ case 0x07: // BEL - ECMA-48 8.3.3
+ if (state->callbacks && state->callbacks->bell) {
+ (*state->callbacks->bell)(state->cbdata);
+ }
+ break;
+
+ case 0x08: // BS - ECMA-48 8.3.5
+ if (state->pos.col > 0) {
+ state->pos.col--;
+ }
+ break;
+
+ case 0x09: // HT - ECMA-48 8.3.60
+ tab(state, 1, +1);
+ break;
+
+ case 0x0a: // LF - ECMA-48 8.3.74
+ case 0x0b: // VT
+ case 0x0c: // FF
+ linefeed(state);
+ if (state->mode.newline) {
+ state->pos.col = 0;
+ }
+ break;
+
+ case 0x0d: // CR - ECMA-48 8.3.15
+ state->pos.col = 0;
+ break;
+
+ case 0x0e: // LS1 - ECMA-48 8.3.76
+ state->gl_set = 1;
+ break;
+
+ case 0x0f: // LS0 - ECMA-48 8.3.75
+ state->gl_set = 0;
+ break;
+
+ case 0x84: // IND - DEPRECATED but implemented for completeness
+ linefeed(state);
+ break;
+
+ case 0x85: // NEL - ECMA-48 8.3.86
+ linefeed(state);
+ state->pos.col = 0;
+ break;
+
+ case 0x88: // HTS - ECMA-48 8.3.62
+ set_col_tabstop(state, state->pos.col);
+ break;
+
+ case 0x8d: // RI - ECMA-48 8.3.104
+ if (state->pos.row == state->scrollregion_top) {
+ VTermRect rect = {
+ .start_row = state->scrollregion_top,
+ .end_row = SCROLLREGION_BOTTOM(state),
+ .start_col = SCROLLREGION_LEFT(state),
+ .end_col = SCROLLREGION_RIGHT(state),
+ };
+
+ scroll(state, rect, -1, 0);
+ } else if (state->pos.row > 0) {
+ state->pos.row--;
+ }
+ break;
+
+ case 0x8e: // SS2 - ECMA-48 8.3.141
+ state->gsingle_set = 2;
+ break;
+
+ case 0x8f: // SS3 - ECMA-48 8.3.142
+ state->gsingle_set = 3;
+ break;
+
+ default:
+ if (state->fallbacks && state->fallbacks->control) {
+ if ((*state->fallbacks->control)(control, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ updatecursor(state, &oldpos, 1);
+
+#ifdef DEBUG
+ if (state->pos.row < 0 || state->pos.row >= state->rows
+ || state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after Ctrl %02x: (%d,%d)\n",
+ control, state->pos.row, state->pos.col);
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static int settermprop_bool(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .boolean = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_int(VTermState *state, VTermProp prop, int v)
+{
+ VTermValue val = { .number = v };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static int settermprop_string(VTermState *state, VTermProp prop, VTermStringFragment frag)
+{
+ VTermValue val = { .string = frag };
+ return vterm_state_set_termprop(state, prop, &val);
+}
+
+static void savecursor(VTermState *state, int save)
+{
+ if (save) {
+ state->saved.pos = state->pos;
+ state->saved.mode.cursor_visible = state->mode.cursor_visible;
+ state->saved.mode.cursor_blink = state->mode.cursor_blink;
+ state->saved.mode.cursor_shape = state->mode.cursor_shape;
+
+ vterm_state_savepen(state, 1);
+ } else {
+ VTermPos oldpos = state->pos;
+
+ state->pos = state->saved.pos;
+
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, state->saved.mode.cursor_visible);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, state->saved.mode.cursor_blink);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, state->saved.mode.cursor_shape);
+
+ vterm_state_savepen(state, 0);
+
+ updatecursor(state, &oldpos, 1);
+ }
+}
+
+static int on_escape(const char *bytes, size_t len, void *user)
+{
+ VTermState *state = user;
+
+ // Easier to decode this from the first byte, even though the final byte terminates it
+ switch (bytes[0]) {
+ case ' ':
+ if (len != 2) {
+ return 0;
+ }
+
+ switch (bytes[1]) {
+ case 'F': // S7C1T
+ state->vt->mode.ctrl8bit = 0;
+ break;
+
+ case 'G': // S8C1T
+ state->vt->mode.ctrl8bit = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '#':
+ if (len != 2) {
+ return 0;
+ }
+
+ switch (bytes[1]) {
+ case '3': // DECDHL top
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_TOP);
+ break;
+
+ case '4': // DECDHL bottom
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_BOTTOM);
+ break;
+
+ case '5': // DECSWL
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_OFF, DHL_OFF);
+ break;
+
+ case '6': // DECDWL
+ if (state->mode.leftrightmargin) {
+ break;
+ }
+ set_lineinfo(state, state->pos.row, NO_FORCE, DWL_ON, DHL_OFF);
+ break;
+
+ case '8': // DECALN
+ {
+ VTermPos pos;
+ schar_T E = schar_from_ascii('E'); // E
+ for (pos.row = 0; pos.row < state->rows; pos.row++) {
+ for (pos.col = 0; pos.col < ROWWIDTH(state, pos.row); pos.col++) {
+ putglyph(state, E, 1, pos);
+ }
+ }
+ break;
+ }
+
+ default:
+ return 0;
+ }
+ return 2;
+
+ case '(':
+ case ')':
+ case '*':
+ case '+': // SCS
+ if (len != 2) {
+ return 0;
+ }
+
+ {
+ int setnum = bytes[0] - 0x28;
+ VTermEncoding *newenc = vterm_lookup_encoding(ENC_SINGLE_94, bytes[1]);
+
+ if (newenc) {
+ state->encoding[setnum].enc = newenc;
+
+ if (newenc->init) {
+ (*newenc->init)(newenc, state->encoding[setnum].data);
+ }
+ }
+ }
+
+ return 2;
+
+ case '7': // DECSC
+ savecursor(state, 1);
+ return 1;
+
+ case '8': // DECRC
+ savecursor(state, 0);
+ return 1;
+
+ case '<': // Ignored by VT100. Used in VT52 mode to switch up to VT100
+ return 1;
+
+ case '=': // DECKPAM
+ state->mode.keypad = 1;
+ return 1;
+
+ case '>': // DECKPNM
+ state->mode.keypad = 0;
+ return 1;
+
+ case 'c': // RIS - ECMA-48 8.3.105
+ {
+ VTermPos oldpos = state->pos;
+ vterm_state_reset(state, 1);
+ if (state->callbacks && state->callbacks->movecursor) {
+ (*state->callbacks->movecursor)(state->pos, oldpos, state->mode.cursor_visible,
+ state->cbdata);
+ }
+ return 1;
+ }
+
+ case 'n': // LS2 - ECMA-48 8.3.78
+ state->gl_set = 2;
+ return 1;
+
+ case 'o': // LS3 - ECMA-48 8.3.80
+ state->gl_set = 3;
+ return 1;
+
+ case '~': // LS1R - ECMA-48 8.3.77
+ state->gr_set = 1;
+ return 1;
+
+ case '}': // LS2R - ECMA-48 8.3.79
+ state->gr_set = 2;
+ return 1;
+
+ case '|': // LS3R - ECMA-48 8.3.81
+ state->gr_set = 3;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static void set_mode(VTermState *state, int num, int val)
+{
+ switch (num) {
+ case 4: // IRM - ECMA-48 7.2.10
+ state->mode.insert = (unsigned)val;
+ break;
+
+ case 20: // LNM - ANSI X3.4-1977
+ state->mode.newline = (unsigned)val;
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown mode %d\n", num);
+ return;
+ }
+}
+
+static void set_dec_mode(VTermState *state, int num, int val)
+{
+ switch (num) {
+ case 1:
+ state->mode.cursor = (unsigned)val;
+ break;
+
+ case 5: // DECSCNM - screen mode
+ settermprop_bool(state, VTERM_PROP_REVERSE, val);
+ break;
+
+ case 6: // DECOM - origin mode
+ {
+ VTermPos oldpos = state->pos;
+ state->mode.origin = (unsigned)val;
+ state->pos.row = state->mode.origin ? state->scrollregion_top : 0;
+ state->pos.col = state->mode.origin ? SCROLLREGION_LEFT(state) : 0;
+ updatecursor(state, &oldpos, 1);
+ }
+ break;
+
+ case 7:
+ state->mode.autowrap = (unsigned)val;
+ break;
+
+ case 12:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, val);
+ break;
+
+ case 25:
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, val);
+ break;
+
+ case 69: // DECVSSM - vertical split screen mode
+ // DECLRMM - left/right margin mode
+ state->mode.leftrightmargin = (unsigned)val;
+ if (val) {
+ // Setting DECVSSM must clear doublewidth/doubleheight state of every line
+ for (int row = 0; row < state->rows; row++) {
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ }
+ }
+
+ break;
+
+ case 1000:
+ case 1002:
+ case 1003:
+ settermprop_int(state, VTERM_PROP_MOUSE,
+ !val ? VTERM_PROP_MOUSE_NONE
+ : (num == 1000) ? VTERM_PROP_MOUSE_CLICK
+ : (num == 1002) ? VTERM_PROP_MOUSE_DRAG
+ : VTERM_PROP_MOUSE_MOVE);
+ break;
+
+ case 1004:
+ settermprop_bool(state, VTERM_PROP_FOCUSREPORT, val);
+ state->mode.report_focus = (unsigned)val;
+ break;
+
+ case 1005:
+ state->mouse_protocol = val ? MOUSE_UTF8 : MOUSE_X10;
+ break;
+
+ case 1006:
+ state->mouse_protocol = val ? MOUSE_SGR : MOUSE_X10;
+ break;
+
+ case 1015:
+ state->mouse_protocol = val ? MOUSE_RXVT : MOUSE_X10;
+ break;
+
+ case 1047:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ break;
+
+ case 1048:
+ savecursor(state, val);
+ break;
+
+ case 1049:
+ settermprop_bool(state, VTERM_PROP_ALTSCREEN, val);
+ savecursor(state, val);
+ break;
+
+ case 2004:
+ state->mode.bracketpaste = (unsigned)val;
+ break;
+
+ case 2031:
+ settermprop_bool(state, VTERM_PROP_THEMEUPDATES, val);
+ break;
+
+ default:
+ DEBUG_LOG("libvterm: Unknown DEC mode %d\n", num);
+ return;
+ }
+}
+
+static void request_dec_mode(VTermState *state, int num)
+{
+ int reply;
+
+ switch (num) {
+ case 1:
+ reply = state->mode.cursor;
+ break;
+
+ case 5:
+ reply = state->mode.screen;
+ break;
+
+ case 6:
+ reply = state->mode.origin;
+ break;
+
+ case 7:
+ reply = state->mode.autowrap;
+ break;
+
+ case 12:
+ reply = state->mode.cursor_blink;
+ break;
+
+ case 25:
+ reply = state->mode.cursor_visible;
+ break;
+
+ case 69:
+ reply = state->mode.leftrightmargin;
+ break;
+
+ case 1000:
+ reply = state->mouse_flags == MOUSE_WANT_CLICK;
+ break;
+
+ case 1002:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_DRAG);
+ break;
+
+ case 1003:
+ reply = state->mouse_flags == (MOUSE_WANT_CLICK|MOUSE_WANT_MOVE);
+ break;
+
+ case 1004:
+ reply = state->mode.report_focus;
+ break;
+
+ case 1005:
+ reply = state->mouse_protocol == MOUSE_UTF8;
+ break;
+
+ case 1006:
+ reply = state->mouse_protocol == MOUSE_SGR;
+ break;
+
+ case 1015:
+ reply = state->mouse_protocol == MOUSE_RXVT;
+ break;
+
+ case 1047:
+ reply = state->mode.alt_screen;
+ break;
+
+ case 2004:
+ reply = state->mode.bracketpaste;
+ break;
+
+ case 2031:
+ reply = state->mode.theme_updates;
+ break;
+
+ default:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, 0);
+ return;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%d;%d$y", num, reply ? 1 : 2);
+}
+
+static void request_version_string(VTermState *state)
+{
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, ">|libvterm(%d.%d)",
+ VTERM_VERSION_MAJOR, VTERM_VERSION_MINOR);
+}
+
+static void request_key_encoding_flags(VTermState *state)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+
+ int reply = 0;
+
+ assert(stack->size > 0);
+ VTermKeyEncodingFlags flags = stack->items[stack->size - 1];
+
+ if (flags.disambiguate) {
+ reply |= KEY_ENCODING_DISAMBIGUATE;
+ }
+
+ if (flags.report_events) {
+ reply |= KEY_ENCODING_REPORT_EVENTS;
+ }
+
+ if (flags.report_alternate) {
+ reply |= KEY_ENCODING_REPORT_ALTERNATE;
+ }
+
+ if (flags.report_all_keys) {
+ reply |= KEY_ENCODING_REPORT_ALL_KEYS;
+ }
+
+ if (flags.report_associated) {
+ reply |= KEY_ENCODING_REPORT_ASSOCIATED;
+ }
+
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?%du", reply);
+}
+
+static void set_key_encoding_flags(VTermState *state, int arg, int mode)
+{
+ // When mode is 3, bits set in arg reset the corresponding mode
+ bool set = mode != 3;
+
+ // When mode is 1, unset bits are reset
+ bool reset_unset = mode == 1;
+
+ struct VTermKeyEncodingFlags flags = { 0 };
+ if (arg & KEY_ENCODING_DISAMBIGUATE) {
+ flags.disambiguate = set;
+ } else if (reset_unset) {
+ flags.disambiguate = false;
+ }
+
+ if (arg & KEY_ENCODING_REPORT_EVENTS) {
+ flags.report_events = set;
+ } else if (reset_unset) {
+ flags.report_events = false;
+ }
+
+ if (arg & KEY_ENCODING_REPORT_ALTERNATE) {
+ flags.report_alternate = set;
+ } else if (reset_unset) {
+ flags.report_alternate = false;
+ }
+ if (arg & KEY_ENCODING_REPORT_ALL_KEYS) {
+ flags.report_all_keys = set;
+ } else if (reset_unset) {
+ flags.report_all_keys = false;
+ }
+
+ if (arg & KEY_ENCODING_REPORT_ASSOCIATED) {
+ flags.report_associated = set;
+ } else if (reset_unset) {
+ flags.report_associated = false;
+ }
+
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size > 0);
+ stack->items[stack->size - 1] = flags;
+}
+
+static void push_key_encoding_flags(VTermState *state, int arg)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ assert(stack->size <= ARRAY_SIZE(stack->items));
+
+ if (stack->size == ARRAY_SIZE(stack->items)) {
+ // Evict oldest entry when stack is full
+ for (size_t i = 0; i < ARRAY_SIZE(stack->items) - 1; i++) {
+ stack->items[i] = stack->items[i + 1];
+ }
+ } else {
+ stack->size++;
+ }
+
+ set_key_encoding_flags(state, arg, 1);
+}
+
+static void pop_key_encoding_flags(VTermState *state, int arg)
+{
+ int screen = state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY;
+ struct VTermKeyEncodingStack *stack = &state->key_encoding_stacks[screen];
+ if (arg >= stack->size) {
+ stack->size = 1;
+
+ // If a pop request is received that empties the stack, all flags are reset.
+ memset(&stack->items[0], 0, sizeof(stack->items[0]));
+ } else if (arg > 0) {
+ stack->size -= arg;
+ }
+}
+
+static int on_csi(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user)
+{
+ VTermState *state = user;
+ int leader_byte = 0;
+ int intermed_byte = 0;
+ int cancel_phantom = 1;
+
+ if (leader && leader[0]) {
+ if (leader[1]) { // longer than 1 char
+ return 0;
+ }
+
+ switch (leader[0]) {
+ case '?':
+ case '>':
+ case '<':
+ case '=':
+ leader_byte = (int)leader[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if (intermed && intermed[0]) {
+ if (intermed[1]) { // longer than 1 char
+ return 0;
+ }
+
+ switch (intermed[0]) {
+ case ' ':
+ case '!':
+ case '"':
+ case '$':
+ case '\'':
+ intermed_byte = (int)intermed[0];
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ VTermPos oldpos = state->pos;
+
+ // Some temporaries for later code
+ int count, val;
+ int row, col;
+ VTermRect rect;
+ int selective;
+
+#define LBOUND(v, min) if ((v) < (min))(v) = (min)
+#define UBOUND(v, max) if ((v) > (max))(v) = (max)
+
+#define LEADER(l, b) ((l << 8) | b)
+#define INTERMED(i, b) ((i << 16) | b)
+
+ switch (intermed_byte << 16 | leader_byte << 8 | command) {
+ case 0x40: // ICH - ECMA-48 8.3.64
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if (state->mode.leftrightmargin) {
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ } else {
+ rect.end_col = THISROWWIDTH(state);
+ }
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case 0x41: // CUU - ECMA-48 8.3.22
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x42: // CUD - ECMA-48 8.3.19
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x43: // CUF - ECMA-48 8.3.20
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x44: // CUB - ECMA-48 8.3.18
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x45: // CNL - ECMA-48 8.3.12
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x46: // CPL - ECMA-48 8.3.13
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col = 0;
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x47: // CHA - ECMA-48 8.3.9
+ val = CSI_ARG_OR(args[0], 1);
+ state->pos.col = val - 1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x48: // CUP - ECMA-48 8.3.21
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row - 1;
+ state->pos.col = col - 1;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x49: // CHT - ECMA-48 8.3.10
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, +1);
+ break;
+
+ case 0x4a: // ED - ECMA-48 8.3.39
+ case LEADER('?', 0x4a): // DECSED - Selective Erase in Display
+ selective = (leader_byte == '?');
+ switch (CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col; rect.end_col = state->cols;
+ if (rect.end_col > rect.start_col) {
+ erase(state, rect, selective);
+ }
+
+ rect.start_row = state->pos.row + 1; rect.end_row = state->rows;
+ rect.start_col = 0;
+ for (int row_ = rect.start_row; row_ < rect.end_row; row_++) {
+ set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
+ }
+ if (rect.end_row > rect.start_row) {
+ erase(state, rect, selective);
+ }
+ break;
+
+ case 1:
+ rect.start_row = 0; rect.end_row = state->pos.row;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for (int row_ = rect.start_row; row_ < rect.end_row; row_++) {
+ set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
+ }
+ if (rect.end_col > rect.start_col) {
+ erase(state, rect, selective);
+ }
+
+ rect.start_row = state->pos.row; rect.end_row = state->pos.row + 1;
+ rect.end_col = state->pos.col + 1;
+ if (rect.end_row > rect.start_row) {
+ erase(state, rect, selective);
+ }
+ break;
+
+ case 2:
+ rect.start_row = 0; rect.end_row = state->rows;
+ rect.start_col = 0; rect.end_col = state->cols;
+ for (int row_ = rect.start_row; row_ < rect.end_row; row_++) {
+ set_lineinfo(state, row_, FORCE, DWL_OFF, DHL_OFF);
+ }
+ erase(state, rect, selective);
+ break;
+
+ case 3:
+ if (state->callbacks && state->callbacks->sb_clear) {
+ if ((*state->callbacks->sb_clear)(state->cbdata)) {
+ return 1;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 0x4b: // EL - ECMA-48 8.3.41
+ case LEADER('?', 0x4b): // DECSEL - Selective Erase in Line
+ selective = (leader_byte == '?');
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+
+ switch (CSI_ARG(args[0])) {
+ case CSI_ARG_MISSING:
+ case 0:
+ rect.start_col = state->pos.col; rect.end_col = THISROWWIDTH(state); break;
+ case 1:
+ rect.start_col = 0; rect.end_col = state->pos.col + 1; break;
+ case 2:
+ rect.start_col = 0; rect.end_col = THISROWWIDTH(state); break;
+ default:
+ return 0;
+ }
+
+ if (rect.end_col > rect.start_col) {
+ erase(state, rect, selective);
+ }
+
+ break;
+
+ case 0x4c: // IL - ECMA-48 8.3.67
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x4d: // DL - ECMA-48 8.3.32
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x50: // DCH - ECMA-48 8.3.26
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ if (state->mode.leftrightmargin) {
+ rect.end_col = SCROLLREGION_RIGHT(state);
+ } else {
+ rect.end_col = THISROWWIDTH(state);
+ }
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ case 0x53: // SU - ECMA-48 8.3.147
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, count, 0);
+
+ break;
+
+ case 0x54: // SD - ECMA-48 8.3.113
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = SCROLLREGION_LEFT(state);
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, -count, 0);
+
+ break;
+
+ case 0x58: // ECH - ECMA-48 8.3.38
+ count = CSI_ARG_COUNT(args[0]);
+
+ rect.start_row = state->pos.row;
+ rect.end_row = state->pos.row + 1;
+ rect.start_col = state->pos.col;
+ rect.end_col = state->pos.col + count;
+ UBOUND(rect.end_col, THISROWWIDTH(state));
+
+ erase(state, rect, 0);
+ break;
+
+ case 0x5a: // CBT - ECMA-48 8.3.7
+ count = CSI_ARG_COUNT(args[0]);
+ tab(state, count, -1);
+ break;
+
+ case 0x60: // HPA - ECMA-48 8.3.57
+ col = CSI_ARG_OR(args[0], 1);
+ state->pos.col = col - 1;
+ state->at_phantom = 0;
+ break;
+
+ case 0x61: // HPR - ECMA-48 8.3.59
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x62: { // REP - ECMA-48 8.3.103
+ const int row_width = THISROWWIDTH(state);
+ count = CSI_ARG_COUNT(args[0]);
+ col = state->pos.col + count;
+ UBOUND(col, row_width);
+ schar_T sc = schar_from_buf(state->grapheme_buf, state->grapheme_len);
+ while (state->pos.col < col) {
+ putglyph(state, sc, state->combine_width, state->pos);
+ state->pos.col += state->combine_width;
+ }
+ if (state->pos.col + state->combine_width >= row_width) {
+ if (state->mode.autowrap) {
+ state->at_phantom = 1;
+ cancel_phantom = 0;
+ }
+ }
+ break;
+ }
+
+ case 0x63: // DA - ECMA-48 8.3.24
+ val = CSI_ARG_OR(args[0], 0);
+ if (val == 0) {
+ // DEC VT100 response
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?1;2c");
+ }
+ break;
+
+ case LEADER('>', 0x63): // DEC secondary Device Attributes
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, ">%d;%d;%dc", 0, 100, 0);
+ break;
+
+ case 0x64: // VPA - ECMA-48 8.3.158
+ row = CSI_ARG_OR(args[0], 1);
+ state->pos.row = row - 1;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x65: // VPR - ECMA-48 8.3.160
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row += count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x66: // HVP - ECMA-48 8.3.63
+ row = CSI_ARG_OR(args[0], 1);
+ col = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ // zero-based
+ state->pos.row = row - 1;
+ state->pos.col = col - 1;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+ state->at_phantom = 0;
+ break;
+
+ case 0x67: // TBC - ECMA-48 8.3.154
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch (val) {
+ case 0:
+ clear_col_tabstop(state, state->pos.col);
+ break;
+ case 3:
+ case 5:
+ for (col = 0; col < state->cols; col++) {
+ clear_col_tabstop(state, col);
+ }
+ break;
+ case 1:
+ case 2:
+ case 4:
+ break;
+ // TODO(vterm): 1, 2 and 4 aren't meaningful yet without line tab stops
+ default:
+ return 0;
+ }
+ break;
+
+ case 0x68: // SM - ECMA-48 8.3.125
+ if (!CSI_ARG_IS_MISSING(args[0])) {
+ set_mode(state, CSI_ARG(args[0]), 1);
+ }
+ break;
+
+ case LEADER('?', 0x68): // DEC private mode set
+ for (int i = 0; i < argcount; i++) {
+ if (!CSI_ARG_IS_MISSING(args[i])) {
+ set_dec_mode(state, CSI_ARG(args[i]), 1);
+ }
+ }
+ break;
+
+ case 0x6a: // HPB - ECMA-48 8.3.58
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.col -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6b: // VPB - ECMA-48 8.3.159
+ count = CSI_ARG_COUNT(args[0]);
+ state->pos.row -= count;
+ state->at_phantom = 0;
+ break;
+
+ case 0x6c: // RM - ECMA-48 8.3.106
+ if (!CSI_ARG_IS_MISSING(args[0])) {
+ set_mode(state, CSI_ARG(args[0]), 0);
+ }
+ break;
+
+ case LEADER('?', 0x6c): // DEC private mode reset
+ for (int i = 0; i < argcount; i++) {
+ if (!CSI_ARG_IS_MISSING(args[i])) {
+ set_dec_mode(state, CSI_ARG(args[i]), 0);
+ }
+ }
+ break;
+
+ case 0x6d: // SGR - ECMA-48 8.3.117
+ vterm_state_setpen(state, args, argcount);
+ break;
+
+ case LEADER('?', 0x6d): // DECSGR
+ // No actual DEC terminal recognised these, but some printers did. These are alternative ways to
+ // request subscript/superscript/off
+ for (int argi = 0; argi < argcount; argi++) {
+ long arg;
+ switch (arg = CSI_ARG(args[argi])) {
+ case 4: // Superscript on
+ arg = 73;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 5: // Subscript on
+ arg = 74;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ case 24: // Super+subscript off
+ arg = 75;
+ vterm_state_setpen(state, &arg, 1);
+ break;
+ }
+ }
+ break;
+
+ case 0x6e: // DSR - ECMA-48 8.3.35
+ case LEADER('?', 0x6e): // DECDSR
+ val = CSI_ARG_OR(args[0], 0);
+
+ {
+ char *qmark = (leader_byte == '?') ? "?" : "";
+ bool dark = false;
+
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ // ignore - these are replies
+ break;
+ case 5:
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s0n", qmark);
+ break;
+ case 6: // CPR - cursor position report
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "%s%d;%dR", qmark, state->pos.row + 1,
+ state->pos.col + 1);
+ break;
+ case 996:
+ if (state->callbacks && state->callbacks->theme) {
+ if (state->callbacks->theme(&dark, state->cbdata)) {
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "?997;%cn", dark ? '1' : '2');
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case INTERMED('!', 0x70): // DECSTR - DEC soft terminal reset
+ vterm_state_reset(state, 0);
+ break;
+
+ case LEADER('?', INTERMED('$', 0x70)):
+ request_dec_mode(state, CSI_ARG(args[0]));
+ break;
+
+ case LEADER('>', 0x71): // XTVERSION - xterm query version string
+ request_version_string(state);
+ break;
+
+ case INTERMED(' ', 0x71): // DECSCUSR - DEC set cursor shape
+ val = CSI_ARG_OR(args[0], 1);
+
+ switch (val) {
+ case 0:
+ case 1:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 2:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+ break;
+ case 3:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 4:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_UNDERLINE);
+ break;
+ case 5:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ case 6:
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 0);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BAR_LEFT);
+ break;
+ }
+
+ break;
+
+ case INTERMED('"', 0x71): // DECSCA - DEC select character protection attribute
+ val = CSI_ARG_OR(args[0], 0);
+
+ switch (val) {
+ case 0:
+ case 2:
+ state->protected_cell = 0;
+ break;
+ case 1:
+ state->protected_cell = 1;
+ break;
+ }
+
+ break;
+
+ case 0x72: // DECSTBM - DEC custom
+ state->scrollregion_top = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_bottom = argcount < 2
+ || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_top, 0);
+ UBOUND(state->scrollregion_top, state->rows);
+ LBOUND(state->scrollregion_bottom, -1);
+ if (state->scrollregion_top == 0 && state->scrollregion_bottom == state->rows) {
+ state->scrollregion_bottom = -1;
+ } else {
+ UBOUND(state->scrollregion_bottom, state->rows);
+ }
+
+ if (SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ // Invalid
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case 0x73: // DECSLRM - DEC custom
+ // Always allow setting these margins, just they won't take effect without DECVSSM
+ state->scrollregion_left = CSI_ARG_OR(args[0], 1) - 1;
+ state->scrollregion_right = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? -1 : CSI_ARG(args[1]);
+ LBOUND(state->scrollregion_left, 0);
+ UBOUND(state->scrollregion_left, state->cols);
+ LBOUND(state->scrollregion_right, -1);
+ if (state->scrollregion_left == 0 && state->scrollregion_right == state->cols) {
+ state->scrollregion_right = -1;
+ } else {
+ UBOUND(state->scrollregion_right, state->cols);
+ }
+
+ if (state->scrollregion_right > -1
+ && state->scrollregion_right <= state->scrollregion_left) {
+ // Invalid
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+ }
+
+ // Setting the scrolling region restores the cursor to the home position
+ state->pos.row = 0;
+ state->pos.col = 0;
+ if (state->mode.origin) {
+ state->pos.row += state->scrollregion_top;
+ state->pos.col += SCROLLREGION_LEFT(state);
+ }
+
+ break;
+
+ case LEADER('?', 0x75): // Kitty query
+ request_key_encoding_flags(state);
+ break;
+
+ case LEADER('>', 0x75): // Kitty push flags
+ push_key_encoding_flags(state, CSI_ARG_OR(args[0], 0));
+ break;
+
+ case LEADER('<', 0x75): // Kitty pop flags
+ pop_key_encoding_flags(state, CSI_ARG_OR(args[0], 1));
+ break;
+
+ case LEADER('=', 0x75): // Kitty set flags
+ val = argcount < 2 || CSI_ARG_IS_MISSING(args[1]) ? 1 : CSI_ARG(args[1]);
+ set_key_encoding_flags(state, CSI_ARG_OR(args[0], 0), val);
+ break;
+
+ case INTERMED('\'', 0x7D): // DECIC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, -count);
+
+ break;
+
+ case INTERMED('\'', 0x7E): // DECDC
+ count = CSI_ARG_COUNT(args[0]);
+
+ if (!is_cursor_in_scrollregion(state)) {
+ break;
+ }
+
+ rect.start_row = state->scrollregion_top;
+ rect.end_row = SCROLLREGION_BOTTOM(state);
+ rect.start_col = state->pos.col;
+ rect.end_col = SCROLLREGION_RIGHT(state);
+
+ scroll(state, rect, 0, count);
+
+ break;
+
+ default:
+ if (state->fallbacks && state->fallbacks->csi) {
+ if ((*state->fallbacks->csi)(leader, args, argcount, intermed, command, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ if (state->mode.origin) {
+ LBOUND(state->pos.row, state->scrollregion_top);
+ UBOUND(state->pos.row, SCROLLREGION_BOTTOM(state) - 1);
+ LBOUND(state->pos.col, SCROLLREGION_LEFT(state));
+ UBOUND(state->pos.col, SCROLLREGION_RIGHT(state) - 1);
+ } else {
+ LBOUND(state->pos.row, 0);
+ UBOUND(state->pos.row, state->rows - 1);
+ LBOUND(state->pos.col, 0);
+ UBOUND(state->pos.col, THISROWWIDTH(state) - 1);
+ }
+
+ updatecursor(state, &oldpos, cancel_phantom);
+
+#ifdef DEBUG
+ if (state->pos.row < 0 || state->pos.row >= state->rows
+ || state->pos.col < 0 || state->pos.col >= state->cols) {
+ fprintf(stderr, "Position out of bounds after CSI %c: (%d,%d)\n",
+ command, state->pos.row, state->pos.col);
+ abort();
+ }
+
+ if (SCROLLREGION_BOTTOM(state) <= state->scrollregion_top) {
+ fprintf(stderr, "Scroll region height out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_BOTTOM(state), state->scrollregion_top);
+ abort();
+ }
+
+ if (SCROLLREGION_RIGHT(state) <= SCROLLREGION_LEFT(state)) {
+ fprintf(stderr, "Scroll region width out of bounds after CSI %c: %d <= %d\n",
+ command, SCROLLREGION_RIGHT(state), SCROLLREGION_LEFT(state));
+ abort();
+ }
+#endif
+
+ return 1;
+}
+
+static uint8_t unbase64one(char c)
+{
+ if (c >= 'A' && c <= 'Z') {
+ return (uint8_t)c - 'A';
+ } else if (c >= 'a' && c <= 'z') {
+ return (uint8_t)c - 'a' + 26;
+ } else if (c >= '0' && c <= '9') {
+ return (uint8_t)c - '0' + 52;
+ } else if (c == '+') {
+ return 62;
+ } else if (c == '/') {
+ return 63;
+ }
+
+ return 0xFF;
+}
+
+static void osc_selection(VTermState *state, VTermStringFragment frag)
+{
+ if (frag.initial) {
+ state->tmp.selection.mask = 0;
+ state->tmp.selection.state = SELECTION_INITIAL;
+ }
+
+ while (!state->tmp.selection.state && frag.len) {
+ // Parse selection parameter
+ switch (frag.str[0]) {
+ case 'c':
+ state->tmp.selection.mask |= VTERM_SELECTION_CLIPBOARD;
+ break;
+ case 'p':
+ state->tmp.selection.mask |= VTERM_SELECTION_PRIMARY;
+ break;
+ case 'q':
+ state->tmp.selection.mask |= VTERM_SELECTION_SECONDARY;
+ break;
+ case 's':
+ state->tmp.selection.mask |= VTERM_SELECTION_SELECT;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state->tmp.selection.mask |= (VTERM_SELECTION_CUT0 << (frag.str[0] - '0'));
+ break;
+
+ case ';':
+ state->tmp.selection.state = SELECTION_SELECTED;
+ if (!state->tmp.selection.mask) {
+ state->tmp.selection.mask = VTERM_SELECTION_SELECT|VTERM_SELECTION_CUT0;
+ }
+ break;
+ }
+
+ frag.str++;
+ frag.len--;
+ }
+
+ if (!frag.len) {
+ // Clear selection if we're already finished but didn't do anything
+ if (frag.final && state->selection.callbacks->set) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = NULL,
+ .len = 0,
+ .initial = state->tmp.selection.state != SELECTION_SET,
+ .final = true,
+ }, state->selection.user);
+ }
+ return;
+ }
+
+ if (state->tmp.selection.state == SELECTION_SELECTED) {
+ if (frag.str[0] == '?') {
+ state->tmp.selection.state = SELECTION_QUERY;
+ } else {
+ state->tmp.selection.state = SELECTION_SET_INITIAL;
+ state->tmp.selection.recvpartial = 0;
+ }
+ }
+
+ if (state->tmp.selection.state == SELECTION_QUERY) {
+ if (state->selection.callbacks->query) {
+ (*state->selection.callbacks->query)(state->tmp.selection.mask, state->selection.user);
+ }
+ return;
+ }
+
+ if (state->tmp.selection.state == SELECTION_INVALID) {
+ return;
+ }
+
+ if (state->selection.callbacks->set) {
+ size_t bufcur = 0;
+ char *buffer = state->selection.buffer;
+
+ uint32_t x = 0; // Current decoding value
+ int n = 0; // Number of sextets consumed
+
+ if (state->tmp.selection.recvpartial) {
+ n = state->tmp.selection.recvpartial >> 24;
+ x = state->tmp.selection.recvpartial & 0x03FFFF; // could be up to 18 bits of state in here
+
+ state->tmp.selection.recvpartial = 0;
+ }
+
+ while ((state->selection.buflen - bufcur) >= 3 && frag.len) {
+ if (frag.str[0] == '=') {
+ if (n == 2) {
+ buffer[0] = (char)(x >> 4 & 0xFF);
+ buffer += 1, bufcur += 1;
+ }
+ if (n == 3) {
+ buffer[0] = (char)(x >> 10 & 0xFF);
+ buffer[1] = (char)(x >> 2 & 0xFF);
+ buffer += 2, bufcur += 2;
+ }
+
+ while (frag.len && frag.str[0] == '=') {
+ frag.str++, frag.len--;
+ }
+
+ n = 0;
+ } else {
+ uint8_t b = unbase64one(frag.str[0]);
+ if (b == 0xFF) {
+ DEBUG_LOG("base64decode bad input %02X\n", (uint8_t)frag.str[0]);
+
+ state->tmp.selection.state = SELECTION_INVALID;
+ if (state->selection.callbacks->set) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = NULL,
+ .len = 0,
+ .initial = true,
+ .final = true,
+ }, state->selection.user);
+ }
+ break;
+ }
+
+ x = (x << 6) | b;
+ n++;
+ frag.str++, frag.len--;
+
+ if (n == 4) {
+ buffer[0] = (char)(x >> 16 & 0xFF);
+ buffer[1] = (char)(x >> 8 & 0xFF);
+ buffer[2] = (char)(x >> 0 & 0xFF);
+
+ buffer += 3, bufcur += 3;
+ x = 0;
+ n = 0;
+ }
+ }
+
+ if (!frag.len || (state->selection.buflen - bufcur) < 3) {
+ if (bufcur) {
+ (*state->selection.callbacks->set)(state->tmp.selection.mask, (VTermStringFragment){
+ .str = state->selection.buffer,
+ .len = bufcur,
+ .initial = state->tmp.selection.state == SELECTION_SET_INITIAL,
+ .final = frag.final && !frag.len,
+ }, state->selection.user);
+ state->tmp.selection.state = SELECTION_SET;
+ }
+
+ buffer = state->selection.buffer;
+ bufcur = 0;
+ }
+ }
+
+ if (n) {
+ state->tmp.selection.recvpartial = (uint32_t)(n << 24) | x;
+ }
+ }
+}
+
+static int on_osc(int command, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ switch (command) {
+ case 0:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 1:
+ settermprop_string(state, VTERM_PROP_ICONNAME, frag);
+ return 1;
+
+ case 2:
+ settermprop_string(state, VTERM_PROP_TITLE, frag);
+ return 1;
+
+ case 52:
+ if (state->selection.callbacks) {
+ osc_selection(state, frag);
+ }
+
+ return 1;
+
+ default:
+ if (state->fallbacks && state->fallbacks->osc) {
+ if ((*state->fallbacks->osc)(command, frag, state->fbdata)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void request_status_string(VTermState *state, VTermStringFragment frag)
+{
+ VTerm *vt = state->vt;
+
+ char *tmp = state->tmp.decrqss;
+
+ if (frag.initial) {
+ tmp[0] = tmp[1] = tmp[2] = tmp[3] = 0;
+ }
+
+ size_t i = 0;
+ while (i < sizeof(state->tmp.decrqss) - 1 && tmp[i]) {
+ i++;
+ }
+ while (i < sizeof(state->tmp.decrqss) - 1 && frag.len--) {
+ tmp[i++] = (frag.str++)[0];
+ }
+ tmp[i] = 0;
+
+ if (!frag.final) {
+ return;
+ }
+
+ switch (tmp[0] | tmp[1] << 8 | tmp[2] << 16) {
+ case 'm': {
+ // Query SGR
+ long args[20];
+ int argc = vterm_state_getpen(state, args, sizeof(args)/sizeof(args[0]));
+ size_t cur = 0;
+
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x90" "1$r" : ESC_S "P" "1$r"); // DCS 1$r ...
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ for (int argi = 0; argi < argc; argi++) {
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ argi == argc - 1 ? "%ld"
+ : CSI_ARG_HAS_MORE(args[argi]) ? "%ld:"
+ : "%ld;",
+ CSI_ARG(args[argi]));
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+ }
+
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "m" "\x9C" : "m" ESC_S "\\"); // ... m ST
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+ return;
+ }
+
+ case 'r':
+ // Query DECSTBM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%dr", state->scrollregion_top + 1,
+ SCROLLREGION_BOTTOM(state));
+ return;
+
+ case 's':
+ // Query DECSLRM
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d;%ds", SCROLLREGION_LEFT(state) + 1,
+ SCROLLREGION_RIGHT(state));
+ return;
+
+ case ' '|('q' << 8): {
+ // Query DECSCUSR
+ int reply = 0;
+ switch (state->mode.cursor_shape) {
+ case VTERM_PROP_CURSORSHAPE_BLOCK:
+ reply = 2; break;
+ case VTERM_PROP_CURSORSHAPE_UNDERLINE:
+ reply = 4; break;
+ case VTERM_PROP_CURSORSHAPE_BAR_LEFT:
+ reply = 6; break;
+ }
+ if (state->mode.cursor_blink) {
+ reply--;
+ }
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d q", reply);
+ return;
+ }
+
+ case '\"'|('q' << 8):
+ // Query DECSCA
+ vterm_push_output_sprintf_str(vt, C1_DCS, true,
+ "1$r%d\"q", state->protected_cell ? 1 : 2);
+ return;
+ }
+
+ vterm_push_output_sprintf_str(state->vt, C1_DCS, true, "0$r");
+}
+
+static int on_dcs(const char *command, size_t commandlen, VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (commandlen == 2 && strneq(command, "$q", 2)) {
+ request_status_string(state, frag);
+ return 1;
+ } else if (state->fallbacks && state->fallbacks->dcs) {
+ if ((*state->fallbacks->dcs)(command, commandlen, frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ DEBUG_LOG("libvterm: Unhandled DCS %.*s\n", (int)commandlen, command);
+ return 0;
+}
+
+static int on_apc(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (state->fallbacks && state->fallbacks->apc) {
+ if ((*state->fallbacks->apc)(frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ // No DEBUG_LOG because all APCs are unhandled
+ return 0;
+}
+
+static int on_pm(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (state->fallbacks && state->fallbacks->pm) {
+ if ((*state->fallbacks->pm)(frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ // No DEBUG_LOG because all PMs are unhandled
+ return 0;
+}
+
+static int on_sos(VTermStringFragment frag, void *user)
+{
+ VTermState *state = user;
+
+ if (state->fallbacks && state->fallbacks->sos) {
+ if ((*state->fallbacks->sos)(frag, state->fbdata)) {
+ return 1;
+ }
+ }
+
+ // No DEBUG_LOG because all SOSs are unhandled
+ return 0;
+}
+
+static int on_resize(int rows, int cols, void *user)
+{
+ VTermState *state = user;
+ VTermPos oldpos = state->pos;
+
+ if (cols != state->cols) {
+ uint8_t *newtabstops = vterm_allocator_malloc(state->vt, ((size_t)cols + 7) / 8);
+
+ // TODO(vterm): This can all be done much more efficiently bytewise
+ int col;
+ for (col = 0; col < state->cols && col < cols; col++) {
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ if (state->tabstops[col >> 3] & mask) {
+ newtabstops[col >> 3] |= mask;
+ } else {
+ newtabstops[col >> 3] &= ~mask;
+ }
+ }
+
+ for (; col < cols; col++) {
+ uint8_t mask = (uint8_t)(1 << (col & 7));
+ if (col % 8 == 0) {
+ newtabstops[col >> 3] |= mask;
+ } else {
+ newtabstops[col >> 3] &= ~mask;
+ }
+ }
+
+ vterm_allocator_free(state->vt, state->tabstops);
+ state->tabstops = newtabstops;
+ }
+
+ state->rows = rows;
+ state->cols = cols;
+
+ if (state->scrollregion_bottom > -1) {
+ UBOUND(state->scrollregion_bottom, state->rows);
+ }
+ if (state->scrollregion_right > -1) {
+ UBOUND(state->scrollregion_right, state->cols);
+ }
+
+ VTermStateFields fields = {
+ .pos = state->pos,
+ .lineinfos = {[0] = state->lineinfos[0], [1] = state->lineinfos[1] },
+ };
+
+ if (state->callbacks && state->callbacks->resize) {
+ (*state->callbacks->resize)(rows, cols, &fields, state->cbdata);
+ state->pos = fields.pos;
+
+ state->lineinfos[0] = fields.lineinfos[0];
+ state->lineinfos[1] = fields.lineinfos[1];
+ } else {
+ if (rows != state->rows) {
+ for (int bufidx = BUFIDX_PRIMARY; bufidx <= BUFIDX_ALTSCREEN; bufidx++) {
+ VTermLineInfo *oldlineinfo = state->lineinfos[bufidx];
+ if (!oldlineinfo) {
+ continue;
+ }
+
+ VTermLineInfo *newlineinfo = vterm_allocator_malloc(state->vt,
+ (size_t)rows * sizeof(VTermLineInfo));
+
+ int row;
+ for (row = 0; row < state->rows && row < rows; row++) {
+ newlineinfo[row] = oldlineinfo[row];
+ }
+
+ for (; row < rows; row++) {
+ newlineinfo[row] = (VTermLineInfo){
+ .doublewidth = 0,
+ };
+ }
+
+ vterm_allocator_free(state->vt, state->lineinfos[bufidx]);
+ state->lineinfos[bufidx] = newlineinfo;
+ }
+ }
+ }
+
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+
+ if (state->at_phantom && state->pos.col < cols - 1) {
+ state->at_phantom = 0;
+ state->pos.col++;
+ }
+
+ if (state->pos.row < 0) {
+ state->pos.row = 0;
+ }
+ if (state->pos.row >= rows) {
+ state->pos.row = rows - 1;
+ }
+ if (state->pos.col < 0) {
+ state->pos.col = 0;
+ }
+ if (state->pos.col >= cols) {
+ state->pos.col = cols - 1;
+ }
+
+ updatecursor(state, &oldpos, 1);
+
+ return 1;
+}
+
+static const VTermParserCallbacks parser_callbacks = {
+ .text = on_text,
+ .control = on_control,
+ .escape = on_escape,
+ .csi = on_csi,
+ .osc = on_osc,
+ .dcs = on_dcs,
+ .apc = on_apc,
+ .pm = on_pm,
+ .sos = on_sos,
+ .resize = on_resize,
+};
+
+VTermState *vterm_obtain_state(VTerm *vt)
+{
+ if (vt->state) {
+ return vt->state;
+ }
+
+ VTermState *state = vterm_state_new(vt);
+ vt->state = state;
+
+ vterm_parser_set_callbacks(vt, &parser_callbacks, state);
+
+ return state;
+}
+
+void vterm_state_reset(VTermState *state, int hard)
+{
+ state->scrollregion_top = 0;
+ state->scrollregion_bottom = -1;
+ state->scrollregion_left = 0;
+ state->scrollregion_right = -1;
+
+ state->mode.keypad = 0;
+ state->mode.cursor = 0;
+ state->mode.autowrap = 1;
+ state->mode.insert = 0;
+ state->mode.newline = 0;
+ state->mode.alt_screen = 0;
+ state->mode.origin = 0;
+ state->mode.leftrightmargin = 0;
+ state->mode.bracketpaste = 0;
+ state->mode.report_focus = 0;
+
+ state->mouse_flags = 0;
+
+ state->vt->mode.ctrl8bit = 0;
+
+ for (int col = 0; col < state->cols; col++) {
+ if (col % 8 == 0) {
+ set_col_tabstop(state, col);
+ } else {
+ clear_col_tabstop(state, col);
+ }
+ }
+
+ for (int row = 0; row < state->rows; row++) {
+ set_lineinfo(state, row, FORCE, DWL_OFF, DHL_OFF);
+ }
+
+ if (state->callbacks && state->callbacks->initpen) {
+ (*state->callbacks->initpen)(state->cbdata);
+ }
+
+ vterm_state_resetpen(state);
+
+ VTermEncoding *default_enc = state->vt->mode.utf8
+ ? vterm_lookup_encoding(ENC_UTF8, 'u')
+ : vterm_lookup_encoding(ENC_SINGLE_94, 'B');
+
+ for (int i = 0; i < 4; i++) {
+ state->encoding[i].enc = default_enc;
+ if (default_enc->init) {
+ (*default_enc->init)(default_enc, state->encoding[i].data);
+ }
+ }
+
+ state->gl_set = 0;
+ state->gr_set = 1;
+ state->gsingle_set = 0;
+
+ state->protected_cell = 0;
+
+ // Initialise the props
+ settermprop_bool(state, VTERM_PROP_CURSORVISIBLE, 1);
+ settermprop_bool(state, VTERM_PROP_CURSORBLINK, 1);
+ settermprop_int(state, VTERM_PROP_CURSORSHAPE, VTERM_PROP_CURSORSHAPE_BLOCK);
+
+ if (hard) {
+ state->pos.row = 0;
+ state->pos.col = 0;
+ state->at_phantom = 0;
+
+ VTermRect rect = { 0, state->rows, 0, state->cols };
+ erase(state, rect, 0);
+ }
+}
+
+void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user)
+{
+ if (callbacks) {
+ state->callbacks = callbacks;
+ state->cbdata = user;
+
+ if (state->callbacks && state->callbacks->initpen) {
+ (*state->callbacks->initpen)(state->cbdata);
+ }
+ } else {
+ state->callbacks = NULL;
+ state->cbdata = NULL;
+ }
+}
+
+void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks,
+ void *user)
+{
+ if (fallbacks) {
+ state->fallbacks = fallbacks;
+ state->fbdata = user;
+ } else {
+ state->fallbacks = NULL;
+ state->fbdata = NULL;
+ }
+}
+
+int vterm_state_set_termprop(VTermState *state, VTermProp prop, VTermValue *val)
+{
+ // Only store the new value of the property if usercode said it was happy. This is especially
+ // important for altscreen switching
+ if (state->callbacks && state->callbacks->settermprop) {
+ if (!(*state->callbacks->settermprop)(prop, val, state->cbdata)) {
+ return 0;
+ }
+ }
+
+ switch (prop) {
+ case VTERM_PROP_TITLE:
+ case VTERM_PROP_ICONNAME:
+ // we don't store these, just transparently pass through
+ return 1;
+ case VTERM_PROP_CURSORVISIBLE:
+ state->mode.cursor_visible = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORBLINK:
+ state->mode.cursor_blink = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_CURSORSHAPE:
+ state->mode.cursor_shape = (unsigned)val->number;
+ return 1;
+ case VTERM_PROP_REVERSE:
+ state->mode.screen = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_ALTSCREEN:
+ state->mode.alt_screen = (unsigned)val->boolean;
+ state->lineinfo = state->lineinfos[state->mode.alt_screen ? BUFIDX_ALTSCREEN : BUFIDX_PRIMARY];
+ if (state->mode.alt_screen) {
+ VTermRect rect = {
+ .start_row = 0,
+ .start_col = 0,
+ .end_row = state->rows,
+ .end_col = state->cols,
+ };
+ erase(state, rect, 0);
+ }
+ return 1;
+ case VTERM_PROP_MOUSE:
+ state->mouse_flags = 0;
+ if (val->number) {
+ state->mouse_flags |= MOUSE_WANT_CLICK;
+ }
+ if (val->number == VTERM_PROP_MOUSE_DRAG) {
+ state->mouse_flags |= MOUSE_WANT_DRAG;
+ }
+ if (val->number == VTERM_PROP_MOUSE_MOVE) {
+ state->mouse_flags |= MOUSE_WANT_MOVE;
+ }
+ return 1;
+ case VTERM_PROP_FOCUSREPORT:
+ state->mode.report_focus = (unsigned)val->boolean;
+ return 1;
+ case VTERM_PROP_THEMEUPDATES:
+ state->mode.theme_updates = (unsigned)val->boolean;
+ return 1;
+
+ case VTERM_N_PROPS:
+ return 0;
+ }
+
+ return 0;
+}
+
+void vterm_state_focus_in(VTermState *state)
+{
+ if (state->mode.report_focus) {
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "I");
+ }
+}
+
+void vterm_state_focus_out(VTermState *state)
+{
+ if (state->mode.report_focus) {
+ vterm_push_output_sprintf_ctrl(state->vt, C1_CSI, "O");
+ }
+}
+
+const VTermLineInfo *vterm_state_get_lineinfo(const VTermState *state, int row)
+{
+ return state->lineinfo + row;
+}
+
+void vterm_state_set_selection_callbacks(VTermState *state,
+ const VTermSelectionCallbacks *callbacks, void *user,
+ char *buffer, size_t buflen)
+{
+ if (buflen && !buffer) {
+ buffer = vterm_allocator_malloc(state->vt, buflen);
+ }
+
+ state->selection.callbacks = callbacks;
+ state->selection.user = user;
+ state->selection.buffer = buffer;
+ state->selection.buflen = buflen;
+}
diff --git a/src/nvim/vterm/state.h b/src/nvim/vterm/state.h
new file mode 100644
index 0000000000..2f59cf7eec
--- /dev/null
+++ b/src/nvim/vterm/state.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/state.h.generated.h"
+#endif
diff --git a/src/nvim/vterm/vterm.c b/src/nvim/vterm/vterm.c
new file mode 100644
index 0000000000..76d5dc3808
--- /dev/null
+++ b/src/nvim/vterm/vterm.c
@@ -0,0 +1,335 @@
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auto/config.h"
+#include "nvim/memory.h"
+#include "nvim/vterm/screen.h"
+#include "nvim/vterm/state.h"
+#include "nvim/vterm/vterm.h"
+#include "nvim/vterm/vterm_internal_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/vterm.c.generated.h"
+#endif
+
+// *****************
+// * API functions *
+// *****************
+
+static void *default_malloc(size_t size, void *allocdata)
+{
+ void *ptr = xmalloc(size);
+ if (ptr) {
+ memset(ptr, 0, size);
+ }
+ return ptr;
+}
+
+static void default_free(void *ptr, void *allocdata)
+{
+ xfree(ptr);
+}
+
+static VTermAllocatorFunctions default_allocator = {
+ .malloc = &default_malloc,
+ .free = &default_free,
+};
+
+/// Convenient shortcut for default cases
+VTerm *vterm_new(int rows, int cols)
+{
+ return vterm_build(&(const struct VTermBuilder){
+ .rows = rows,
+ .cols = cols,
+ });
+}
+
+// A handy macro for defaulting values out of builder fields
+#define DEFAULT(v, def) ((v) ? (v) : (def))
+
+VTerm *vterm_build(const struct VTermBuilder *builder)
+{
+ const VTermAllocatorFunctions *allocator = DEFAULT(builder->allocator, &default_allocator);
+
+ // Need to bootstrap using the allocator function directly
+ VTerm *vt = (*allocator->malloc)(sizeof(VTerm), builder->allocdata);
+
+ vt->allocator = allocator;
+ vt->allocdata = builder->allocdata;
+
+ vt->rows = builder->rows;
+ vt->cols = builder->cols;
+
+ vt->parser.state = NORMAL;
+
+ vt->parser.callbacks = NULL;
+ vt->parser.cbdata = NULL;
+
+ vt->parser.emit_nul = false;
+
+ vt->outfunc = NULL;
+ vt->outdata = NULL;
+
+ vt->outbuffer_len = DEFAULT(builder->outbuffer_len, 4096);
+ vt->outbuffer_cur = 0;
+ vt->outbuffer = vterm_allocator_malloc(vt, vt->outbuffer_len);
+
+ vt->tmpbuffer_len = DEFAULT(builder->tmpbuffer_len, 4096);
+ vt->tmpbuffer = vterm_allocator_malloc(vt, vt->tmpbuffer_len);
+
+ return vt;
+}
+
+void vterm_free(VTerm *vt)
+{
+ if (vt->screen) {
+ vterm_screen_free(vt->screen);
+ }
+
+ if (vt->state) {
+ vterm_state_free(vt->state);
+ }
+
+ vterm_allocator_free(vt, vt->outbuffer);
+ vterm_allocator_free(vt, vt->tmpbuffer);
+
+ vterm_allocator_free(vt, vt);
+}
+
+void *vterm_allocator_malloc(VTerm *vt, size_t size)
+{
+ return (*vt->allocator->malloc)(size, vt->allocdata);
+}
+
+void vterm_allocator_free(VTerm *vt, void *ptr)
+{
+ (*vt->allocator->free)(ptr, vt->allocdata);
+}
+
+void vterm_get_size(const VTerm *vt, int *rowsp, int *colsp)
+{
+ if (rowsp) {
+ *rowsp = vt->rows;
+ }
+ if (colsp) {
+ *colsp = vt->cols;
+ }
+}
+
+void vterm_set_size(VTerm *vt, int rows, int cols)
+{
+ if (rows < 1 || cols < 1) {
+ return;
+ }
+
+ vt->rows = rows;
+ vt->cols = cols;
+
+ if (vt->parser.callbacks && vt->parser.callbacks->resize) {
+ (*vt->parser.callbacks->resize)(rows, cols, vt->parser.cbdata);
+ }
+}
+
+void vterm_set_utf8(VTerm *vt, int is_utf8)
+{
+ vt->mode.utf8 = (unsigned)is_utf8;
+}
+
+void vterm_output_set_callback(VTerm *vt, VTermOutputCallback *func, void *user)
+{
+ vt->outfunc = func;
+ vt->outdata = user;
+}
+
+void vterm_push_output_bytes(VTerm *vt, const char *bytes, size_t len)
+{
+ if (vt->outfunc) {
+ (vt->outfunc)(bytes, len, vt->outdata);
+ return;
+ }
+
+ if (len > vt->outbuffer_len - vt->outbuffer_cur) {
+ return;
+ }
+
+ memcpy(vt->outbuffer + vt->outbuffer_cur, bytes, len);
+ vt->outbuffer_cur += len;
+}
+
+void vterm_push_output_sprintf(VTerm *vt, const char *format, ...)
+ FUNC_ATTR_PRINTF(2, 3)
+{
+ va_list args;
+ va_start(args, format);
+ size_t len = (size_t)vsnprintf(vt->tmpbuffer, vt->tmpbuffer_len, format, args);
+ vterm_push_output_bytes(vt, vt->tmpbuffer, len);
+ va_end(args);
+}
+
+void vterm_push_output_sprintf_ctrl(VTerm *vt, uint8_t ctrl, const char *fmt, ...)
+ FUNC_ATTR_PRINTF(3, 4)
+{
+ size_t cur;
+
+ if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
+ } else {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
+ }
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
+ va_end(args);
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+void vterm_push_output_sprintf_str(VTerm *vt, uint8_t ctrl, bool term, const char *fmt, ...)
+ FUNC_ATTR_PRINTF(4, 5)
+{
+ size_t cur = 0;
+
+ if (ctrl) {
+ if (ctrl >= 0x80 && !vt->mode.ctrl8bit) {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, ESC_S "%c", ctrl - 0x40);
+ } else {
+ cur = (size_t)snprintf(vt->tmpbuffer, vt->tmpbuffer_len, "%c", ctrl);
+ }
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+ }
+
+ va_list args;
+ va_start(args, fmt);
+ cur += (size_t)vsnprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur, fmt, args);
+ va_end(args);
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+
+ if (term) {
+ cur += (size_t)snprintf(vt->tmpbuffer + cur, vt->tmpbuffer_len - cur,
+ vt->mode.ctrl8bit ? "\x9C" : ESC_S "\\"); // ST
+
+ if (cur >= vt->tmpbuffer_len) {
+ return;
+ }
+ }
+
+ vterm_push_output_bytes(vt, vt->tmpbuffer, cur);
+}
+
+VTermValueType vterm_get_attr_type(VTermAttr attr)
+{
+ switch (attr) {
+ case VTERM_ATTR_BOLD:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_UNDERLINE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_ITALIC:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BLINK:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_REVERSE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_CONCEAL:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_STRIKE:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_FONT:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_FOREGROUND:
+ return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_BACKGROUND:
+ return VTERM_VALUETYPE_COLOR;
+ case VTERM_ATTR_SMALL:
+ return VTERM_VALUETYPE_BOOL;
+ case VTERM_ATTR_BASELINE:
+ return VTERM_VALUETYPE_INT;
+ case VTERM_ATTR_URI:
+ return VTERM_VALUETYPE_INT;
+
+ case VTERM_N_ATTRS:
+ return 0;
+ }
+ return 0; // UNREACHABLE
+}
+
+void vterm_scroll_rect(VTermRect rect, int downward, int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user), void *user)
+{
+ VTermRect src;
+ VTermRect dest;
+
+ if (abs(downward) >= rect.end_row - rect.start_row
+ || abs(rightward) >= rect.end_col - rect.start_col) {
+ // Scroll more than area; just erase the lot
+ (*eraserect)(rect, 0, user);
+ return;
+ }
+
+ if (rightward >= 0) {
+ // rect: [XXX................]
+ // src: [----------------]
+ // dest: [----------------]
+ dest.start_col = rect.start_col;
+ dest.end_col = rect.end_col - rightward;
+ src.start_col = rect.start_col + rightward;
+ src.end_col = rect.end_col;
+ } else {
+ // rect: [................XXX]
+ // src: [----------------]
+ // dest: [----------------]
+ int leftward = -rightward;
+ dest.start_col = rect.start_col + leftward;
+ dest.end_col = rect.end_col;
+ src.start_col = rect.start_col;
+ src.end_col = rect.end_col - leftward;
+ }
+
+ if (downward >= 0) {
+ dest.start_row = rect.start_row;
+ dest.end_row = rect.end_row - downward;
+ src.start_row = rect.start_row + downward;
+ src.end_row = rect.end_row;
+ } else {
+ int upward = -downward;
+ dest.start_row = rect.start_row + upward;
+ dest.end_row = rect.end_row;
+ src.start_row = rect.start_row;
+ src.end_row = rect.end_row - upward;
+ }
+
+ if (moverect) {
+ (*moverect)(dest, src, user);
+ }
+
+ if (downward > 0) {
+ rect.start_row = rect.end_row - downward;
+ } else if (downward < 0) {
+ rect.end_row = rect.start_row - downward;
+ }
+
+ if (rightward > 0) {
+ rect.start_col = rect.end_col - rightward;
+ } else if (rightward < 0) {
+ rect.end_col = rect.start_col - rightward;
+ }
+
+ (*eraserect)(rect, 0, user);
+}
diff --git a/src/nvim/vterm/vterm.h b/src/nvim/vterm/vterm.h
new file mode 100644
index 0000000000..e66f40425a
--- /dev/null
+++ b/src/nvim/vterm/vterm.h
@@ -0,0 +1,161 @@
+#pragma once
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "nvim/macros_defs.h"
+#include "nvim/types_defs.h"
+#include "nvim/vterm/vterm_defs.h"
+#include "nvim/vterm/vterm_keycodes_defs.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "vterm/vterm.h.generated.h"
+#endif
+
+#define VTERM_VERSION_MAJOR 0
+#define VTERM_VERSION_MINOR 3
+
+// move a rect
+static inline void vterm_rect_move(VTermRect *rect, int row_delta, int col_delta)
+{
+ rect->start_row += row_delta; rect->end_row += row_delta;
+ rect->start_col += col_delta; rect->end_col += col_delta;
+}
+
+// Bit-field describing the content of the tagged union `VTermColor`.
+typedef enum {
+ // If the lower bit of `type` is not set, the colour is 24-bit RGB.
+ VTERM_COLOR_RGB = 0x00,
+
+ // The colour is an index into a palette of 256 colours.
+ VTERM_COLOR_INDEXED = 0x01,
+
+ // Mask that can be used to extract the RGB/Indexed bit.
+ VTERM_COLOR_TYPE_MASK = 0x01,
+
+ // If set, indicates that this colour should be the default foreground color, i.e. there was no
+ // SGR request for another colour. When rendering this colour it is possible to ignore "idx" and
+ // just use a colour that is not in the palette.
+ VTERM_COLOR_DEFAULT_FG = 0x02,
+
+ // If set, indicates that this colour should be the default background color, i.e. there was no
+ // SGR request for another colour. A common option when rendering this colour is to not render a
+ // background at all, for example by rendering the window transparently at this spot.
+ VTERM_COLOR_DEFAULT_BG = 0x04,
+
+ // Mask that can be used to extract the default foreground/background bit.
+ VTERM_COLOR_DEFAULT_MASK = 0x06,
+} VTermColorType;
+
+// Returns true if the VTERM_COLOR_RGB `type` flag is set, indicating that the given VTermColor
+// instance is an indexed colour.
+#define VTERM_COLOR_IS_INDEXED(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_INDEXED)
+
+// Returns true if the VTERM_COLOR_INDEXED `type` flag is set, indicating that the given VTermColor
+// instance is an rgb colour.
+#define VTERM_COLOR_IS_RGB(col) \
+ (((col)->type & VTERM_COLOR_TYPE_MASK) == VTERM_COLOR_RGB)
+
+// Returns true if the VTERM_COLOR_DEFAULT_FG `type` flag is set, indicating that the given
+// VTermColor instance corresponds to the default foreground color.
+#define VTERM_COLOR_IS_DEFAULT_FG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_FG))
+
+// Returns true if the VTERM_COLOR_DEFAULT_BG `type` flag is set, indicating that the given
+// VTermColor instance corresponds to the default background color.
+#define VTERM_COLOR_IS_DEFAULT_BG(col) \
+ (!!((col)->type & VTERM_COLOR_DEFAULT_BG))
+
+// Constructs a new VTermColor instance representing the given RGB values.
+static inline void vterm_color_rgb(VTermColor *col, uint8_t red, uint8_t green, uint8_t blue)
+{
+ col->type = VTERM_COLOR_RGB;
+ col->rgb.red = red;
+ col->rgb.green = green;
+ col->rgb.blue = blue;
+}
+
+// Construct a new VTermColor instance representing an indexed color with the given index.
+static inline void vterm_color_indexed(VTermColor *col, uint8_t idx)
+{
+ col->type = VTERM_COLOR_INDEXED;
+ col->indexed.idx = idx;
+}
+
+// ------------
+// Parser layer
+// ------------
+
+/// Flag to indicate non-final subparameters in a single CSI parameter.
+/// Consider
+/// CSI 1;2:3:4;5a
+/// 1 4 and 5 are final.
+/// 2 and 3 are non-final and will have this bit set
+///
+/// Don't confuse this with the final byte of the CSI escape; 'a' in this case.
+#define CSI_ARG_FLAG_MORE (1U << 31)
+#define CSI_ARG_MASK (~(1U << 31))
+
+#define CSI_ARG_HAS_MORE(a) ((a)& CSI_ARG_FLAG_MORE)
+#define CSI_ARG(a) ((a)& CSI_ARG_MASK)
+
+// Can't use -1 to indicate a missing argument; use this instead
+#define CSI_ARG_MISSING ((1UL<<31) - 1)
+
+#define CSI_ARG_IS_MISSING(a) (CSI_ARG(a) == CSI_ARG_MISSING)
+#define CSI_ARG_OR(a, def) (CSI_ARG(a) == CSI_ARG_MISSING ? (def) : CSI_ARG(a))
+#define CSI_ARG_COUNT(a) (CSI_ARG(a) == CSI_ARG_MISSING || CSI_ARG(a) == 0 ? 1 : CSI_ARG(a))
+
+enum {
+ VTERM_UNDERLINE_OFF,
+ VTERM_UNDERLINE_SINGLE,
+ VTERM_UNDERLINE_DOUBLE,
+ VTERM_UNDERLINE_CURLY,
+};
+
+enum {
+ VTERM_BASELINE_NORMAL,
+ VTERM_BASELINE_RAISE,
+ VTERM_BASELINE_LOWER,
+};
+
+// Back-compat alias for the brief time it was in 0.3-RC1
+#define vterm_screen_set_reflow vterm_screen_enable_reflow
+
+void vterm_scroll_rect(VTermRect rect, int downward, int rightward,
+ int (*moverect)(VTermRect src, VTermRect dest, void *user),
+ int (*eraserect)(VTermRect rect, int selective, void *user), void *user);
+
+struct VTermScreen {
+ VTerm *vt;
+ VTermState *state;
+
+ const VTermScreenCallbacks *callbacks;
+ void *cbdata;
+
+ VTermDamageSize damage_merge;
+ // start_row == -1 => no damage
+ VTermRect damaged;
+ VTermRect pending_scrollrect;
+ int pending_scroll_downward, pending_scroll_rightward;
+
+ int rows;
+ int cols;
+
+ unsigned global_reverse : 1;
+ unsigned reflow : 1;
+
+ // Primary and Altscreen. buffers[1] is lazily allocated as needed
+ ScreenCell *buffers[2];
+
+ // buffer will == buffers[0] or buffers[1], depending on altscreen
+ ScreenCell *buffer;
+
+ // buffer for a single screen row used in scrollback storage callbacks
+ VTermScreenCell *sb_buffer;
+
+ ScreenPen pen;
+};
diff --git a/src/nvim/vterm/vterm_defs.h b/src/nvim/vterm/vterm_defs.h
new file mode 100644
index 0000000000..9aa933bef0
--- /dev/null
+++ b/src/nvim/vterm/vterm_defs.h
@@ -0,0 +1,322 @@
+#pragma once
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "nvim/types_defs.h"
+
+typedef struct VTerm VTerm;
+typedef struct VTermState VTermState;
+typedef struct VTermScreen VTermScreen;
+
+typedef struct {
+ int row;
+ int col;
+} VTermPos;
+
+// some small utility functions; we can just keep these static here
+
+typedef struct {
+ int start_row;
+ int end_row;
+ int start_col;
+ int end_col;
+} VTermRect;
+
+// Tagged union storing either an RGB color or an index into a colour palette. In order to convert
+// indexed colours to RGB, you may use the vterm_state_convert_color_to_rgb() or
+// vterm_screen_convert_color_to_rgb() functions which lookup the RGB colour from the palette
+// maintained by a VTermState or VTermScreen instance.
+typedef union {
+ // Tag indicating which union member is actually valid. This variable coincides with the `type`
+ // member of the `rgb` and the `indexed` struct in memory. Please use the `VTERM_COLOR_IS_*` test
+ // macros to check whether a particular type flag is set.
+ uint8_t type;
+
+ // Valid if `VTERM_COLOR_IS_RGB(type)` is true. Holds the RGB colour values.
+ struct {
+ // Same as the top-level `type` member stored in VTermColor.
+ uint8_t type;
+
+ // The actual 8-bit red, green, blue colour values.
+ uint8_t red, green, blue;
+ } rgb;
+
+ // If `VTERM_COLOR_IS_INDEXED(type)` is true, this member holds the index into the colour palette.
+ struct {
+ // Same as the top-level `type` member stored in VTermColor.
+ uint8_t type;
+
+ // Index into the colour map.
+ uint8_t idx;
+ } indexed;
+} VTermColor;
+
+typedef struct {
+ unsigned bold : 1;
+ unsigned underline : 2;
+ unsigned italic : 1;
+ unsigned blink : 1;
+ unsigned reverse : 1;
+ unsigned conceal : 1;
+ unsigned strike : 1;
+ unsigned font : 4; // 0 to 9
+ unsigned dwl : 1; // On a DECDWL or DECDHL line
+ unsigned dhl : 2; // On a DECDHL line (1=top 2=bottom)
+ unsigned small : 1;
+ unsigned baseline : 2;
+} VTermScreenCellAttrs;
+
+typedef struct {
+ schar_T schar;
+ char width;
+ VTermScreenCellAttrs attrs;
+ VTermColor fg, bg;
+ int uri;
+} VTermScreenCell;
+
+typedef enum {
+ // VTERM_PROP_NONE = 0
+ VTERM_PROP_CURSORVISIBLE = 1, // bool
+ VTERM_PROP_CURSORBLINK, // bool
+ VTERM_PROP_ALTSCREEN, // bool
+ VTERM_PROP_TITLE, // string
+ VTERM_PROP_ICONNAME, // string
+ VTERM_PROP_REVERSE, // bool
+ VTERM_PROP_CURSORSHAPE, // number
+ VTERM_PROP_MOUSE, // number
+ VTERM_PROP_FOCUSREPORT, // bool
+ VTERM_PROP_THEMEUPDATES, // bool
+
+ VTERM_N_PROPS,
+} VTermProp;
+
+typedef struct {
+ const char *str;
+ size_t len : 30;
+ bool initial : 1;
+ bool final : 1;
+} VTermStringFragment;
+
+typedef union {
+ int boolean;
+ int number;
+ VTermStringFragment string;
+ VTermColor color;
+} VTermValue;
+
+typedef struct {
+ int (*damage)(VTermRect rect, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, void *user);
+ int (*theme)(bool *dark, void *user);
+ int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user);
+ int (*sb_popline)(int cols, VTermScreenCell *cells, void *user);
+ int (*sb_clear)(void *user);
+} VTermScreenCallbacks;
+
+typedef struct {
+ int (*control)(uint8_t control, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+} VTermStateFallbacks;
+
+typedef enum {
+ VTERM_DAMAGE_CELL, // every cell
+ VTERM_DAMAGE_ROW, // entire rows
+ VTERM_DAMAGE_SCREEN, // entire screen
+ VTERM_DAMAGE_SCROLL, // entire screen + scrollrect
+
+ VTERM_N_DAMAGES,
+} VTermDamageSize;
+
+typedef enum {
+ VTERM_ATTR_BOLD_MASK = 1 << 0,
+ VTERM_ATTR_UNDERLINE_MASK = 1 << 1,
+ VTERM_ATTR_ITALIC_MASK = 1 << 2,
+ VTERM_ATTR_BLINK_MASK = 1 << 3,
+ VTERM_ATTR_REVERSE_MASK = 1 << 4,
+ VTERM_ATTR_STRIKE_MASK = 1 << 5,
+ VTERM_ATTR_FONT_MASK = 1 << 6,
+ VTERM_ATTR_FOREGROUND_MASK = 1 << 7,
+ VTERM_ATTR_BACKGROUND_MASK = 1 << 8,
+ VTERM_ATTR_CONCEAL_MASK = 1 << 9,
+ VTERM_ATTR_SMALL_MASK = 1 << 10,
+ VTERM_ATTR_BASELINE_MASK = 1 << 11,
+ VTERM_ATTR_URI_MASK = 1 << 12,
+
+ VTERM_ALL_ATTRS_MASK = (1 << 13) - 1,
+} VTermAttrMask;
+
+typedef enum {
+ // VTERM_VALUETYPE_NONE = 0
+ VTERM_VALUETYPE_BOOL = 1,
+ VTERM_VALUETYPE_INT,
+ VTERM_VALUETYPE_STRING,
+ VTERM_VALUETYPE_COLOR,
+
+ VTERM_N_VALUETYPES,
+} VTermValueType;
+
+typedef enum {
+ // VTERM_ATTR_NONE = 0
+ VTERM_ATTR_BOLD = 1, // bool: 1, 22
+ VTERM_ATTR_UNDERLINE, // number: 4, 21, 24
+ VTERM_ATTR_ITALIC, // bool: 3, 23
+ VTERM_ATTR_BLINK, // bool: 5, 25
+ VTERM_ATTR_REVERSE, // bool: 7, 27
+ VTERM_ATTR_CONCEAL, // bool: 8, 28
+ VTERM_ATTR_STRIKE, // bool: 9, 29
+ VTERM_ATTR_FONT, // number: 10-19
+ VTERM_ATTR_FOREGROUND, // color: 30-39 90-97
+ VTERM_ATTR_BACKGROUND, // color: 40-49 100-107
+ VTERM_ATTR_SMALL, // bool: 73, 74, 75
+ VTERM_ATTR_BASELINE, // number: 73, 74, 75
+ VTERM_ATTR_URI, // number
+
+ VTERM_N_ATTRS,
+} VTermAttr;
+
+enum {
+ VTERM_PROP_CURSORSHAPE_BLOCK = 1,
+ VTERM_PROP_CURSORSHAPE_UNDERLINE,
+ VTERM_PROP_CURSORSHAPE_BAR_LEFT,
+
+ VTERM_N_PROP_CURSORSHAPES,
+};
+
+enum {
+ VTERM_PROP_MOUSE_NONE = 0,
+ VTERM_PROP_MOUSE_CLICK,
+ VTERM_PROP_MOUSE_DRAG,
+ VTERM_PROP_MOUSE_MOVE,
+
+ VTERM_N_PROP_MOUSES,
+};
+
+typedef enum {
+ VTERM_SELECTION_CLIPBOARD = (1<<0),
+ VTERM_SELECTION_PRIMARY = (1<<1),
+ VTERM_SELECTION_SECONDARY = (1<<2),
+ VTERM_SELECTION_SELECT = (1<<3),
+ VTERM_SELECTION_CUT0 = (1<<4), // also CUT1 .. CUT7 by bitshifting
+} VTermSelectionMask;
+
+typedef struct {
+ schar_T schar;
+ int width;
+ unsigned protected_cell:1; // DECSCA-protected against DECSEL/DECSED
+ unsigned dwl:1; // DECDWL or DECDHL double-width line
+ unsigned dhl:2; // DECDHL double-height line (1=top 2=bottom)
+} VTermGlyphInfo;
+
+typedef struct {
+ unsigned doublewidth:1; // DECDWL or DECDHL line
+ unsigned doubleheight:2; // DECDHL line (1=top 2=bottom)
+ unsigned continuation:1; // Line is a flow continuation of the previous
+} VTermLineInfo;
+
+// Copies of VTermState fields that the 'resize' callback might have reason to edit. 'resize'
+// callback gets total control of these fields and may free-and-reallocate them if required. They
+// will be copied back from the struct after the callback has returned.
+typedef struct {
+ VTermPos pos; // current cursor position
+ VTermLineInfo *lineinfos[2]; // [1] may be NULL
+} VTermStateFields;
+
+typedef struct {
+ // libvterm relies on this memory to be zeroed out before it is returned by the allocator.
+ void *(*malloc)(size_t size, void *allocdata);
+ void (*free)(void *ptr, void *allocdata);
+} VTermAllocatorFunctions;
+
+// Setting output callback will override the buffer logic
+typedef void VTermOutputCallback(const char *s, size_t len, void *user);
+
+struct VTermBuilder {
+ int ver; // currently unused but reserved for some sort of ABI version flag
+
+ int rows, cols;
+
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ // Override default sizes for various structures
+ size_t outbuffer_len; // default: 4096
+ size_t tmpbuffer_len; // default: 4096
+};
+
+typedef struct {
+ int (*putglyph)(VTermGlyphInfo *info, VTermPos pos, void *user);
+ int (*movecursor)(VTermPos pos, VTermPos oldpos, int visible, void *user);
+ int (*scrollrect)(VTermRect rect, int downward, int rightward, void *user);
+ int (*moverect)(VTermRect dest, VTermRect src, void *user);
+ int (*erase)(VTermRect rect, int selective, void *user);
+ int (*initpen)(void *user);
+ int (*setpenattr)(VTermAttr attr, VTermValue *val, void *user);
+ int (*settermprop)(VTermProp prop, VTermValue *val, void *user);
+ int (*bell)(void *user);
+ int (*resize)(int rows, int cols, VTermStateFields *fields, void *user);
+ int (*theme)(bool *dark, void *user);
+ int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo,
+ void *user);
+ int (*sb_clear)(void *user);
+} VTermStateCallbacks;
+
+typedef struct {
+ int (*set)(VTermSelectionMask mask, VTermStringFragment frag, void *user);
+ int (*query)(VTermSelectionMask mask, void *user);
+} VTermSelectionCallbacks;
+
+typedef struct {
+ int (*text)(const char *bytes, size_t len, void *user);
+ int (*control)(uint8_t control, void *user);
+ int (*escape)(const char *bytes, size_t len, void *user);
+ int (*csi)(const char *leader, const long args[], int argcount, const char *intermed,
+ char command, void *user);
+ int (*osc)(int command, VTermStringFragment frag, void *user);
+ int (*dcs)(const char *command, size_t commandlen, VTermStringFragment frag, void *user);
+ int (*apc)(VTermStringFragment frag, void *user);
+ int (*pm)(VTermStringFragment frag, void *user);
+ int (*sos)(VTermStringFragment frag, void *user);
+ int (*resize)(int rows, int cols, void *user);
+} VTermParserCallbacks;
+
+// State of the pen at some moment in time, also used in a cell
+typedef struct {
+ // After the bitfield
+ VTermColor fg, bg;
+
+ // Opaque ID that maps to a URI in a set
+ int uri;
+
+ unsigned bold : 1;
+ unsigned underline : 2;
+ unsigned italic : 1;
+ unsigned blink : 1;
+ unsigned reverse : 1;
+ unsigned conceal : 1;
+ unsigned strike : 1;
+ unsigned font : 4; // 0 to 9
+ unsigned small : 1;
+ unsigned baseline : 2;
+
+ // Extra state storage that isn't strictly pen-related
+ unsigned protected_cell : 1;
+ unsigned dwl : 1; // on a DECDWL or DECDHL line
+ unsigned dhl : 2; // on a DECDHL line (1=top 2=bottom)
+} ScreenPen;
+
+// Internal representation of a screen cell
+typedef struct {
+ schar_T schar;
+ ScreenPen pen;
+} ScreenCell;
diff --git a/src/nvim/vterm/vterm_internal_defs.h b/src/nvim/vterm/vterm_internal_defs.h
new file mode 100644
index 0000000000..19e809490f
--- /dev/null
+++ b/src/nvim/vterm/vterm_internal_defs.h
@@ -0,0 +1,292 @@
+#pragma once
+
+#include <stdarg.h>
+
+#include "nvim/mbyte_defs.h"
+#include "nvim/vterm/vterm_defs.h"
+
+#ifdef DEBUG
+# define DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DEBUG_LOG(...)
+#endif
+
+#define ESC_S "\x1b"
+
+#define INTERMED_MAX 16
+
+#define CSI_ARGS_MAX 16
+#define CSI_LEADER_MAX 16
+
+#define BUFIDX_PRIMARY 0
+#define BUFIDX_ALTSCREEN 1
+
+#define KEY_ENCODING_DISAMBIGUATE 0x1
+#define KEY_ENCODING_REPORT_EVENTS 0x2
+#define KEY_ENCODING_REPORT_ALTERNATE 0x4
+#define KEY_ENCODING_REPORT_ALL_KEYS 0x8
+#define KEY_ENCODING_REPORT_ASSOCIATED 0x10
+
+typedef struct VTermEncoding VTermEncoding;
+typedef struct VTermKeyEncodingFlags VTermKeyEncodingFlags;
+
+typedef struct {
+ VTermEncoding *enc;
+
+ // This size should be increased if required by other stateful encodings
+ char data[4 * sizeof(uint32_t)];
+} VTermEncodingInstance;
+
+struct VTermPen {
+ VTermColor fg;
+ VTermColor bg;
+ int uri;
+ unsigned bold:1;
+ unsigned underline:2;
+ unsigned italic:1;
+ unsigned blink:1;
+ unsigned reverse:1;
+ unsigned conceal:1;
+ unsigned strike:1;
+ unsigned font:4; // To store 0-9
+ unsigned small:1;
+ unsigned baseline:2;
+};
+
+// https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
+struct VTermKeyEncodingFlags {
+ bool disambiguate:1;
+ bool report_events:1;
+ bool report_alternate:1;
+ bool report_all_keys:1;
+ bool report_associated:1;
+};
+
+struct VTermKeyEncodingStack {
+ VTermKeyEncodingFlags items[16];
+ uint8_t size; ///< Number of items in the stack. This is at least 1 and at
+ ///< most the length of the "items" array.
+};
+
+struct VTermState {
+ VTerm *vt;
+
+ const VTermStateCallbacks *callbacks;
+ void *cbdata;
+
+ const VTermStateFallbacks *fallbacks;
+ void *fbdata;
+
+ int rows;
+ int cols;
+
+ // Current cursor position
+ VTermPos pos;
+
+ int at_phantom; // True if we're on the "81st" phantom column to defer a wraparound
+
+ int scrollregion_top;
+ int scrollregion_bottom; // -1 means unbounded
+#define SCROLLREGION_BOTTOM(state) ((state)->scrollregion_bottom > \
+ -1 ? (state)->scrollregion_bottom : (state)->rows)
+ int scrollregion_left;
+#define SCROLLREGION_LEFT(state) ((state)->mode.leftrightmargin ? (state)->scrollregion_left : 0)
+ int scrollregion_right; // -1 means unbounded
+#define SCROLLREGION_RIGHT(state) ((state)->mode.leftrightmargin \
+ && (state)->scrollregion_right > \
+ -1 ? (state)->scrollregion_right : (state)->cols)
+
+ // Bitvector of tab stops
+ uint8_t *tabstops;
+
+ // Primary and Altscreen; lineinfos[1] is lazily allocated as needed
+ VTermLineInfo *lineinfos[2];
+
+ // lineinfo will == lineinfos[0] or lineinfos[1], depending on altscreen
+ VTermLineInfo *lineinfo;
+#define ROWWIDTH(state, \
+ row) ((state)->lineinfo[(row)].doublewidth ? ((state)->cols / 2) : (state)->cols)
+#define THISROWWIDTH(state) ROWWIDTH(state, (state)->pos.row)
+
+ // Mouse state
+ int mouse_col, mouse_row;
+ int mouse_buttons;
+ int mouse_flags;
+#define MOUSE_WANT_CLICK 0x01
+#define MOUSE_WANT_DRAG 0x02
+#define MOUSE_WANT_MOVE 0x04
+
+ enum { MOUSE_X10, MOUSE_UTF8, MOUSE_SGR, MOUSE_RXVT, } mouse_protocol;
+
+// Last glyph output, for Unicode recombining purposes
+ char grapheme_buf[MAX_SCHAR_SIZE];
+ size_t grapheme_len;
+ uint32_t grapheme_last; // last added UTF-32 char
+ GraphemeState grapheme_state;
+ int combine_width; // The width of the glyph above
+ VTermPos combine_pos; // Position before movement
+
+ struct {
+ unsigned keypad:1;
+ unsigned cursor:1;
+ unsigned autowrap:1;
+ unsigned insert:1;
+ unsigned newline:1;
+ unsigned cursor_visible:1;
+ unsigned cursor_blink:1;
+ unsigned cursor_shape:2;
+ unsigned alt_screen:1;
+ unsigned origin:1;
+ unsigned screen:1;
+ unsigned leftrightmargin:1;
+ unsigned bracketpaste:1;
+ unsigned report_focus:1;
+ unsigned theme_updates:1;
+ } mode;
+
+ VTermEncodingInstance encoding[4], encoding_utf8;
+ int gl_set, gr_set, gsingle_set;
+
+ struct VTermPen pen;
+
+ VTermColor default_fg;
+ VTermColor default_bg;
+ VTermColor colors[16]; // Store the 8 ANSI and the 8 ANSI high-brights only
+
+ int bold_is_highbright;
+
+ unsigned protected_cell : 1;
+
+// Saved state under DEC mode 1048/1049
+ struct {
+ VTermPos pos;
+ struct VTermPen pen;
+
+ struct {
+ unsigned cursor_visible:1;
+ unsigned cursor_blink:1;
+ unsigned cursor_shape:2;
+ } mode;
+ } saved;
+
+// Temporary state for DECRQSS parsing
+ union {
+ char decrqss[4];
+ struct {
+ uint16_t mask;
+ enum {
+ SELECTION_INITIAL,
+ SELECTION_SELECTED,
+ SELECTION_QUERY,
+ SELECTION_SET_INITIAL,
+ SELECTION_SET,
+ SELECTION_INVALID,
+ } state : 8;
+ uint32_t recvpartial;
+ uint32_t sendpartial;
+ } selection;
+ } tmp;
+
+ struct {
+ const VTermSelectionCallbacks *callbacks;
+ void *user;
+ char *buffer;
+ size_t buflen;
+ } selection;
+
+ // Maintain two stacks, one for primary screen and one for altscreen
+ struct VTermKeyEncodingStack key_encoding_stacks[2];
+};
+
+struct VTerm {
+ const VTermAllocatorFunctions *allocator;
+ void *allocdata;
+
+ int rows;
+ int cols;
+
+ struct {
+ unsigned utf8:1;
+ unsigned ctrl8bit:1;
+ } mode;
+
+ struct {
+ enum VTermParserState {
+ NORMAL,
+ CSI_LEADER,
+ CSI_ARGS,
+ CSI_INTERMED,
+ DCS_COMMAND,
+ // below here are the "string states"
+ OSC_COMMAND,
+ OSC,
+ DCS_VTERM,
+ APC,
+ PM,
+ SOS,
+ } state;
+
+ bool in_esc : 1;
+
+ int intermedlen;
+ char intermed[INTERMED_MAX];
+
+ union {
+ struct {
+ int leaderlen;
+ char leader[CSI_LEADER_MAX];
+
+ int argi;
+ long args[CSI_ARGS_MAX];
+ } csi;
+ struct {
+ int command;
+ } osc;
+ struct {
+ int commandlen;
+ char command[CSI_LEADER_MAX];
+ } dcs;
+ } v;
+
+ const VTermParserCallbacks *callbacks;
+ void *cbdata;
+
+ bool string_initial;
+
+ bool emit_nul;
+ } parser;
+
+ // len == malloc()ed size; cur == number of valid bytes
+
+ VTermOutputCallback *outfunc;
+ void *outdata;
+
+ char *outbuffer;
+ size_t outbuffer_len;
+ size_t outbuffer_cur;
+
+ char *tmpbuffer;
+ size_t tmpbuffer_len;
+
+ VTermState *state;
+ VTermScreen *screen;
+};
+
+struct VTermEncoding {
+ void (*init)(VTermEncoding *enc, void *data);
+ void (*decode)(VTermEncoding *enc, void *data, uint32_t cp[], int *cpi, int cplen,
+ const char bytes[], size_t *pos, size_t len);
+};
+
+typedef enum {
+ ENC_UTF8,
+ ENC_SINGLE_94,
+} VTermEncodingType;
+
+enum {
+ C1_SS3 = 0x8f,
+ C1_DCS = 0x90,
+ C1_CSI = 0x9b,
+ C1_ST = 0x9c,
+ C1_OSC = 0x9d,
+};
diff --git a/src/nvim/vterm/vterm_keycodes_defs.h b/src/nvim/vterm/vterm_keycodes_defs.h
new file mode 100644
index 0000000000..70db05af54
--- /dev/null
+++ b/src/nvim/vterm/vterm_keycodes_defs.h
@@ -0,0 +1,58 @@
+#pragma once
+
+typedef enum {
+ VTERM_MOD_NONE = 0x00,
+ VTERM_MOD_SHIFT = 0x01,
+ VTERM_MOD_ALT = 0x02,
+ VTERM_MOD_CTRL = 0x04,
+
+ VTERM_ALL_MODS_MASK = 0x07,
+} VTermModifier;
+
+typedef enum {
+ VTERM_KEY_NONE,
+
+ VTERM_KEY_ENTER,
+ VTERM_KEY_TAB,
+ VTERM_KEY_BACKSPACE,
+ VTERM_KEY_ESCAPE,
+
+ VTERM_KEY_UP,
+ VTERM_KEY_DOWN,
+ VTERM_KEY_LEFT,
+ VTERM_KEY_RIGHT,
+
+ VTERM_KEY_INS,
+ VTERM_KEY_DEL,
+ VTERM_KEY_HOME,
+ VTERM_KEY_END,
+ VTERM_KEY_PAGEUP,
+ VTERM_KEY_PAGEDOWN,
+
+ VTERM_KEY_FUNCTION_0 = 256,
+ VTERM_KEY_FUNCTION_MAX = VTERM_KEY_FUNCTION_0 + 255,
+
+ VTERM_KEY_KP_0,
+ VTERM_KEY_KP_1,
+ VTERM_KEY_KP_2,
+ VTERM_KEY_KP_3,
+ VTERM_KEY_KP_4,
+ VTERM_KEY_KP_5,
+ VTERM_KEY_KP_6,
+ VTERM_KEY_KP_7,
+ VTERM_KEY_KP_8,
+ VTERM_KEY_KP_9,
+ VTERM_KEY_KP_MULT,
+ VTERM_KEY_KP_PLUS,
+ VTERM_KEY_KP_COMMA,
+ VTERM_KEY_KP_MINUS,
+ VTERM_KEY_KP_PERIOD,
+ VTERM_KEY_KP_DIVIDE,
+ VTERM_KEY_KP_ENTER,
+ VTERM_KEY_KP_EQUAL,
+
+ VTERM_KEY_MAX, // Must be last
+ VTERM_N_KEYS = VTERM_KEY_MAX,
+} VTermKey;
+
+#define VTERM_KEY_FUNCTION(n) (VTERM_KEY_FUNCTION_0 + (n))
diff --git a/src/nvim/vvars.lua b/src/nvim/vvars.lua
index 5fc014a50c..056e281c0b 100644
--- a/src/nvim/vvars.lua
+++ b/src/nvim/vvars.lua
@@ -10,6 +10,7 @@ M.vars = {
]=],
},
char = {
+ type = 'string',
desc = [=[
Argument for evaluating 'formatexpr' and used for the typed
character when using <expr> in an abbreviation |:map-<expr>|.
@@ -63,6 +64,7 @@ M.vars = {
]=],
},
completed_item = {
+ type = 'vim.v.completed_item',
desc = [=[
Dictionary containing the |complete-items| for the most
recently completed word after |CompleteDone|. Empty if the
@@ -94,6 +96,7 @@ M.vars = {
]=],
},
ctype = {
+ type = 'string',
desc = [=[
The current locale setting for characters of the runtime
environment. This allows Vim scripts to be aware of the
@@ -158,6 +161,7 @@ M.vars = {
]=],
},
event = {
+ type = 'vim.v.event',
desc = [=[
Dictionary of event data for the current |autocommand|. Valid
only during the event lifetime; storing or passing v:event is
@@ -208,13 +212,16 @@ M.vars = {
changing window (or tab) on |DirChanged|.
status Job status or exit code, -1 means "unknown". |TermClose|
reason Reason for completion being done. |CompleteDone|
+ complete_word The word that was selected, empty if abandoned complete.
+ complete_type See |complete_info_mode|
]=],
},
exception = {
type = 'string',
desc = [=[
The value of the exception most recently caught and not
- finished. See also |v:throwpoint| and |throw-variables|.
+ finished. See also |v:stacktrace|, |v:throwpoint|, and
+ |throw-variables|.
Example: >vim
try
throw "oops"
@@ -236,6 +243,7 @@ M.vars = {
]=],
},
exiting = {
+ type = 'integer?',
desc = [=[
Exit code, or |v:null| before invoking the |VimLeavePre|
and |VimLeave| autocmds. See |:q|, |:x| and |:cquit|.
@@ -468,6 +476,7 @@ M.vars = {
]=],
},
msgpack_types = {
+ type = 'table',
desc = [=[
Dictionary containing msgpack types used by |msgpackparse()|
and |msgpackdump()|. All types inside dictionary are fixed
@@ -633,6 +642,7 @@ M.vars = {
]=],
},
scrollstart = {
+ type = 'string',
desc = [=[
String describing the script or function that caused the
screen to scroll up. It's only set when it is empty, thus the
@@ -692,6 +702,15 @@ M.vars = {
<
]=],
},
+ stacktrace = {
+ type = 'table[]',
+ desc = [=[
+ The stack trace of the exception most recently caught and
+ not finished. Refer to |getstacktrace()| for the structure of
+ stack trace. See also |v:exception|, |v:throwpoint|, and
+ |throw-variables|.
+ ]=],
+ },
statusmsg = {
type = 'string',
desc = [=[
@@ -796,11 +815,13 @@ M.vars = {
]=],
},
testing = {
+ type = 'integer',
desc = [=[
Must be set before using `test_garbagecollect_now()`.
]=],
},
this_session = {
+ type = 'string',
desc = [=[
Full filename of the last loaded or saved session file.
Empty when no session file has been saved. See |:mksession|.
@@ -808,10 +829,11 @@ M.vars = {
]=],
},
throwpoint = {
+ type = 'string',
desc = [=[
The point where the exception most recently caught and not
finished was thrown. Not set when commands are typed. See
- also |v:exception| and |throw-variables|.
+ also |v:exception|, |v:stacktrace|, and |throw-variables|.
Example: >vim
try
throw "oops"
diff --git a/src/nvim/window.c b/src/nvim/window.c
index c3f3e075f1..fa2bfec138 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -1,9 +1,7 @@
#include <assert.h>
-#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -52,7 +50,6 @@
#include "nvim/mark.h"
#include "nvim/mark_defs.h"
#include "nvim/match.h"
-#include "nvim/mbyte.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/mouse.h"
@@ -186,13 +183,13 @@ win_T *swbuf_goto_win_with_buf(buf_T *buf)
// If 'switchbuf' contains "useopen": jump to first window in the current
// tab page containing "buf" if one exists.
- if (swb_flags & SWB_USEOPEN) {
+ if (swb_flags & kOptSwbFlagUseopen) {
wp = buf_jump_open_win(buf);
}
// If 'switchbuf' contains "usetab": jump to first window in any tab page
// containing "buf" if one exists.
- if (wp == NULL && (swb_flags & SWB_USETAB)) {
+ if (wp == NULL && (swb_flags & kOptSwbFlagUsetab)) {
wp = buf_jump_open_tab(buf);
}
@@ -583,7 +580,7 @@ wingotofile:
// If 'switchbuf' is set to 'useopen' or 'usetab' and the
// file is already opened in a window, then jump to it.
win_T *wp = NULL;
- if ((swb_flags & (SWB_USEOPEN | SWB_USETAB))
+ if ((swb_flags & (kOptSwbFlagUseopen | kOptSwbFlagUsetab))
&& cmdmod.cmod_tab == 0) {
wp = swbuf_goto_win_with_buf(buflist_findname_exp(ptr));
}
@@ -746,40 +743,28 @@ void win_set_buf(win_T *win, buf_T *buf, Error *err)
RedrawingDisabled++;
switchwin_T switchwin;
- if (switch_win_noblock(&switchwin, win, tab, true) == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to switch to window %d",
- win->handle);
- goto cleanup;
- }
-
- try_start();
-
- const int save_acd = p_acd;
- if (!switchwin.sw_same_win) {
- // Temporarily disable 'autochdir' when setting buffer in another window.
- p_acd = false;
- }
- int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
+ TRY_WRAP(err, {
+ int win_result = switch_win_noblock(&switchwin, win, tab, true);
+ if (win_result != FAIL) {
+ const int save_acd = p_acd;
+ if (!switchwin.sw_same_win) {
+ // Temporarily disable 'autochdir' when setting buffer in another window.
+ p_acd = false;
+ }
- if (!switchwin.sw_same_win) {
- p_acd = save_acd;
- }
+ do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
- if (!try_end(err) && result == FAIL) {
- api_set_error(err,
- kErrorTypeException,
- "Failed to set buffer %d",
- buf->handle);
- }
+ if (!switchwin.sw_same_win) {
+ p_acd = save_acd;
+ }
+ }
+ });
// If window is not current, state logic will not validate its cursor. So do it now.
// Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set).
validate_cursor(curwin);
-cleanup:
restore_win_noblock(&switchwin, true);
RedrawingDisabled--;
}
@@ -850,9 +835,19 @@ void ui_ext_win_position(win_T *wp, bool validate)
col += tcol - 1;
}
}
+ } else if (c.relative == kFloatRelativeLaststatus) {
+ row += Rows - (int)p_ch - last_stl_height(false);
+ } else if (c.relative == kFloatRelativeTabline) {
+ row += tabline_height();
}
+ bool resort = wp->w_grid_alloc.comp_index != 0
+ && wp->w_grid_alloc.zindex != wp->w_config.zindex;
+ bool raise = resort && wp->w_grid_alloc.zindex < wp->w_config.zindex;
wp->w_grid_alloc.zindex = wp->w_config.zindex;
+ if (resort) {
+ ui_comp_layers_adjust(wp->w_grid_alloc.comp_index, raise);
+ }
if (ui_has(kUIMultigrid)) {
String anchor = cstr_as_string(float_anchor_str[c.anchor]);
if (!c.hide) {
@@ -1075,6 +1070,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_fl
return NULL;
}
need_status = STATUS_HEIGHT;
+ win_float_anchor_laststatus();
}
bool do_equal = false;
@@ -1471,7 +1467,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_fl
frame_add_statusline(curfrp);
}
}
- frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false);
+ frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false, false);
} else {
win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
}
@@ -2145,7 +2141,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int
|| topfr->fr_width != width
|| topfr->fr_win->w_wincol != col) {
topfr->fr_win->w_winrow = row;
- frame_new_height(topfr, height, false, false);
+ frame_new_height(topfr, height, false, false, false);
topfr->fr_win->w_wincol = col;
frame_new_width(topfr, width, false, false);
redraw_all_later(UPD_NOT_VALID);
@@ -3163,7 +3159,7 @@ win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp, frame_T **unflat_al
if (*dirp == 'v') {
frame_new_height(altfr, altfr->fr_height + frp_close->fr_height,
- altfr == frp_close->fr_next, false);
+ altfr == frp_close->fr_next, false, false);
} else {
assert(*dirp == 'h');
frame_new_width(altfr, altfr->fr_width + frp_close->fr_width,
@@ -3365,7 +3361,7 @@ void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr)
// adjusts window sizes to fit restored statuslines/separators, if needed.
if (dir == 'v') {
frame_new_height(unflat_altfr, unflat_altfr->fr_height - frp->fr_height,
- unflat_altfr == frp->fr_next, false);
+ unflat_altfr == frp->fr_next, false, false);
} else if (dir == 'h') {
frame_new_width(unflat_altfr, unflat_altfr->fr_width - frp->fr_width,
unflat_altfr == frp->fr_next, false);
@@ -3448,13 +3444,13 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp)
static tabpage_T *alt_tabpage(void)
{
// Use the last accessed tab page, if possible.
- if ((tcl_flags & TCL_USELAST) && valid_tabpage(lastused_tabpage)) {
+ if ((tcl_flags & kOptTclFlagUselast) && valid_tabpage(lastused_tabpage)) {
return lastused_tabpage;
}
// Use the next tab page, if possible.
bool forward = curtab->tp_next != NULL
- && ((tcl_flags & TCL_LEFT) == 0 || curtab == first_tabpage);
+ && ((tcl_flags & kOptTclFlagLeft) == 0 || curtab == first_tabpage);
tabpage_T *tp;
if (forward) {
@@ -3508,15 +3504,31 @@ static bool is_bottom_win(win_T *wp)
}
return true;
}
+
+// 'cmdheight' value explicitly set by the user: window commands are allowed to
+// resize the topframe to values higher than this minimum, but not lower.
+static OptInt min_set_ch = 1;
+
/// Set a new height for a frame. Recursively sets the height for contained
/// frames and windows. Caller must take care of positions.
///
/// @param topfirst resize topmost contained frame first.
/// @param wfh obey 'winfixheight' when there is a choice;
/// may cause the height not to be set.
-void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
+/// @param set_ch set 'cmdheight' to resize topframe.
+void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh, bool set_ch)
FUNC_ATTR_NONNULL_ALL
{
+ if (topfrp->fr_parent == NULL && set_ch) {
+ // topframe: update the command line height, with side effects.
+ OptInt new_ch = MAX(min_set_ch, p_ch + topfrp->fr_height - height);
+ if (new_ch != p_ch) {
+ const OptInt save_ch = min_set_ch;
+ set_option_value(kOptCmdheight, NUMBER_OPTVAL(new_ch), 0);
+ min_set_ch = save_ch;
+ }
+ height = (int)MIN(ROWS_AVAIL, height);
+ }
if (topfrp->fr_win != NULL) {
// Simple case: just one window.
win_T *wp = topfrp->fr_win;
@@ -3529,7 +3541,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
do {
// All frames in this row get the same new height.
FOR_ALL_FRAMES(frp, topfrp->fr_child) {
- frame_new_height(frp, height, topfirst, wfh);
+ frame_new_height(frp, height, topfirst, wfh, set_ch);
if (frp->fr_height > height) {
// Could not fit the windows, make the whole row higher.
height = frp->fr_height;
@@ -3571,10 +3583,9 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
int h = frame_minheight(frp, NULL);
if (frp->fr_height + extra_lines < h) {
extra_lines += frp->fr_height - h;
- frame_new_height(frp, h, topfirst, wfh);
+ frame_new_height(frp, h, topfirst, wfh, set_ch);
} else {
- frame_new_height(frp, frp->fr_height + extra_lines,
- topfirst, wfh);
+ frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh, set_ch);
break;
}
if (topfirst) {
@@ -3593,7 +3604,7 @@ void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh)
}
} else if (extra_lines > 0) {
// increase height of bottom or top frame
- frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh);
+ frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh, set_ch);
}
}
topfrp->fr_height = height;
@@ -4010,6 +4021,10 @@ void unuse_tabpage(tabpage_T *tp)
tp->tp_curwin = curwin;
}
+// When switching tabpage, handle other side-effects in command_height(), but
+// avoid setting frame sizes which are still correct.
+static bool command_frame_height = true;
+
/// Set the relevant pointers to use tab page "tp". May want to call
/// unuse_tabpage() first.
void use_tabpage(tabpage_T *tp)
@@ -4411,6 +4426,16 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
use_tabpage(tp);
+ if (p_ch != curtab->tp_ch_used) {
+ // Use the stored value of p_ch, so that it can be different for each tab page.
+ // Handle other side-effects but avoid setting frame sizes, which are still correct.
+ OptInt new_ch = curtab->tp_ch_used;
+ curtab->tp_ch_used = p_ch;
+ command_frame_height = false;
+ set_option_value(kOptCmdheight, NUMBER_OPTVAL(new_ch), 0);
+ command_frame_height = true;
+ }
+
if (old_curtab != curtab) {
tabpage_check_windows(old_curtab);
}
@@ -4424,27 +4449,9 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a
prevwin = next_prevwin;
last_status(false); // status line may appear or disappear
- const int row = win_comp_pos(); // recompute w_winrow for all windows
+ win_comp_pos(); // recompute w_winrow for all windows
diff_need_scrollbind = true;
- // 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;
-
- // When cmdheight is changed in a tab page with '<C-w>-', cmdline_row is
- // changed but p_ch and tp_ch_used are not changed. Thus we also need to
- // check cmdline_row.
- if (row < cmdline_row && cmdline_row <= Rows - p_ch) {
- clear_cmdline = true;
- }
-
// If there was a click in a window, it won't be usable for a following
// drag.
reset_dragwin();
@@ -5169,8 +5176,8 @@ win_T *win_alloc(win_T *after, bool hidden)
return new_wp;
}
-// Free one wininfo_T.
-void free_wininfo(wininfo_T *wip, buf_T *bp)
+// Free one WinInfo.
+void free_wininfo(WinInfo *wip, buf_T *bp)
{
if (wip->wi_optset) {
clear_winopt(&wip->wi_opt);
@@ -5235,30 +5242,27 @@ void win_free(win_T *wp, tabpage_T *tp)
// Remove the window from the b_wininfo lists, it may happen that the
// freed memory is re-used for another window.
FOR_ALL_BUFFERS(buf) {
- for (wininfo_T *wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ WinInfo *wip_wp = NULL;
+ size_t pos_wip = kv_size(buf->b_wininfo);
+ size_t pos_null = kv_size(buf->b_wininfo);
+ for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
+ WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win == wp) {
- wininfo_T *wip2;
-
- // If there already is an entry with "wi_win" set to NULL it
- // must be removed, it would never be used.
- // Skip "wip" itself, otherwise Coverity complains.
- for (wip2 = buf->b_wininfo; wip2 != NULL; wip2 = wip2->wi_next) {
- // `wip2 != wip` to satisfy Coverity. #14884
- if (wip2 != wip && wip2->wi_win == NULL) {
- if (wip2->wi_next != NULL) {
- wip2->wi_next->wi_prev = wip2->wi_prev;
- }
- if (wip2->wi_prev == NULL) {
- buf->b_wininfo = wip2->wi_next;
- } else {
- wip2->wi_prev->wi_next = wip2->wi_next;
- }
- free_wininfo(wip2, buf);
- break;
- }
- }
+ wip_wp = wip;
+ pos_wip = i;
+ } else if (wip->wi_win == NULL) {
+ pos_null = i;
+ }
+ }
- wip->wi_win = NULL;
+ if (wip_wp) {
+ wip_wp->wi_win = NULL;
+ // If there already is an entry with "wi_win" set to NULL, only
+ // the first entry with NULL will ever be used, delete the other one.
+ if (pos_null < kv_size(buf->b_wininfo)) {
+ size_t pos_delete = MAX(pos_null, pos_wip);
+ free_wininfo(kv_A(buf->b_wininfo, pos_delete), buf);
+ kv_shift(buf->b_wininfo, pos_delete, 1);
}
}
}
@@ -5422,9 +5426,9 @@ void win_new_screen_rows(void)
// First try setting the heights of windows with 'winfixheight'. If
// that doesn't result in the right height, forget about that option.
- frame_new_height(topframe, h, false, true);
+ frame_new_height(topframe, h, false, true, false);
if (!frame_check_height(topframe, h)) {
- frame_new_height(topframe, h, false, false);
+ frame_new_height(topframe, h, false, false, false);
}
win_comp_pos(); // recompute w_winrow and w_wincol
@@ -5859,22 +5863,7 @@ void win_setheight_win(int height, win_T *win)
frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height);
// recompute the window positions
- int row = win_comp_pos();
-
- // If there is extra space created between the last window and the command
- // line, clear it.
- if (full_screen && msg_scrolled == 0 && row < cmdline_row) {
- grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0);
- if (msg_grid.chars) {
- clear_cmdline = true;
- }
- }
- cmdline_row = row;
- p_ch = MAX(Rows - cmdline_row, 0);
- curtab->tp_ch_used = p_ch;
- msg_row = row;
- msg_col = 0;
-
+ win_comp_pos();
win_fix_scroll(true);
redraw_all_later(UPD_NOT_VALID);
@@ -5902,14 +5891,8 @@ static void frame_setheight(frame_T *curfrp, int height)
if (curfrp->fr_parent == NULL) {
// 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
- // `cmdheight` doesn't go below 1 if it wasn't set to 0 explicitly.
- height = (int)MIN(ROWS_AVAIL + p_ch - !p_ch_was_zero, height);
- }
if (height > 0) {
- frame_new_height(curfrp, height, false, false);
+ frame_new_height(curfrp, height, false, false, true);
}
} else if (curfrp->fr_parent->fr_layout == FR_ROW) {
// Row of frames: Also need to resize frames left and right of this
@@ -5988,7 +5971,7 @@ static void frame_setheight(frame_T *curfrp, int height)
}
// set the current frame to the new height
- frame_new_height(curfrp, height, false, false);
+ frame_new_height(curfrp, height, false, false, true);
// First take lines from the frames after the current frame. If
// that is not enough, takes lines from frames above the current
@@ -6010,15 +5993,15 @@ static void frame_setheight(frame_T *curfrp, int height)
room_reserved = frp->fr_height - take;
}
take -= frp->fr_height - room_reserved;
- frame_new_height(frp, room_reserved, false, false);
+ frame_new_height(frp, room_reserved, false, false, true);
room_reserved = 0;
}
} else {
if (frp->fr_height - take < h) {
take -= frp->fr_height - h;
- frame_new_height(frp, h, false, false);
+ frame_new_height(frp, h, false, false, true);
} else {
- frame_new_height(frp, frp->fr_height - take, false, false);
+ frame_new_height(frp, frp->fr_height - take, false, false, true);
take = 0;
}
}
@@ -6189,7 +6172,7 @@ const char *did_set_winminheight(optset_T *args FUNC_ATTR_UNUSED)
// loop until there is a 'winminheight' that is possible
while (p_wmh > 0) {
const int room = Rows - (int)p_ch;
- const int needed = min_rows();
+ const int needed = min_rows_for_all_tabpages();
if (room >= needed) {
break;
}
@@ -6276,7 +6259,7 @@ 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) {
+ } else if (min_set_ch > 0) {
room--;
}
room = MAX(room, 0);
@@ -6296,7 +6279,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
// Grow frame fr by "offset" lines.
// Doesn't happen when dragging the last status line up.
if (fr != NULL) {
- frame_new_height(fr, fr->fr_height + offset, up, false);
+ frame_new_height(fr, fr->fr_height + offset, up, false, true);
}
if (up) {
@@ -6309,9 +6292,9 @@ void win_drag_status_line(win_T *dragwin, int offset)
int n = frame_minheight(fr, NULL);
if (fr->fr_height - offset <= n) {
offset -= fr->fr_height - n;
- frame_new_height(fr, n, !up, false);
+ frame_new_height(fr, n, !up, false, true);
} else {
- frame_new_height(fr, fr->fr_height - offset, !up, false);
+ frame_new_height(fr, fr->fr_height - offset, !up, false, true);
break;
}
if (up) {
@@ -6320,15 +6303,7 @@ void win_drag_status_line(win_T *dragwin, int offset)
fr = fr->fr_next;
}
}
- int row = win_comp_pos();
- grid_clear(&default_grid, row, cmdline_row, 0, Columns, 0);
- if (msg_grid.chars) {
- clear_cmdline = true;
- }
- cmdline_row = row;
- p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1);
- curtab->tp_ch_used = p_ch;
-
+ win_comp_pos();
win_fix_scroll(true);
redraw_all_later(UPD_SOME_VALID);
@@ -6390,7 +6365,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset)
fr = curfr; // put fr at window that grows
}
- // If not enough room thn move as far as we can
+ // If not enough room then move as far as we can
offset = MIN(offset, room);
// No room at all, quit.
@@ -6765,21 +6740,6 @@ void command_height(void)
{
int old_p_ch = (int)curtab->tp_ch_used;
- // Use the value of p_ch that we remembered. This is needed for when the
- // GUI starts up, we can't be sure in what order things happen. And when
- // p_ch was changed in another tab page.
- curtab->tp_ch_used = p_ch;
-
- // Update cmdline_row to what it should be: just below the last window.
- cmdline_row = topframe->fr_height + tabline_height() + global_stl_height();
-
- // 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.
frame_T *frp = lastwin_nofloating()->w_frame;
while (frp->fr_width != Columns && frp->fr_parent != NULL) {
@@ -6787,60 +6747,53 @@ void command_height(void)
}
// Avoid changing the height of a window with 'winfixheight' set.
- while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF
- && frp->fr_win->w_p_wfh) {
+ while (frp->fr_prev != NULL && frp->fr_layout == FR_LEAF && frp->fr_win->w_p_wfh) {
frp = frp->fr_prev;
}
- if (starting != NO_SCREEN) {
- cmdline_row = Rows - (int)p_ch;
-
- if (p_ch > old_p_ch) { // p_ch got bigger
- while (p_ch > old_p_ch) {
- if (frp == NULL) {
- emsg(_(e_noroom));
- p_ch = old_p_ch;
- curtab->tp_ch_used = p_ch;
- cmdline_row = Rows - (int)p_ch;
- break;
- }
- int h = frp->fr_height - frame_minheight(frp, NULL);
- h = MIN(h, (int)p_ch - old_p_ch);
- old_p_ch += h;
- frame_add_height(frp, -h);
- frp = frp->fr_prev;
- }
-
- // Recompute window positions.
- win_comp_pos();
-
- if (!need_wait_return) {
- // clear the lines added to cmdline
- if (full_screen) {
- grid_clear(&default_grid, cmdline_row, Rows, 0, Columns, 0);
- }
- msg_row = cmdline_row;
- }
- redraw_cmdline = true;
- return;
+ while (p_ch > old_p_ch && command_frame_height) {
+ if (frp == NULL) {
+ emsg(_(e_noroom));
+ p_ch = old_p_ch;
+ break;
}
-
- msg_row = MAX(msg_row, cmdline_row);
- redraw_cmdline = true;
+ int h = MIN((int)(p_ch - old_p_ch), frp->fr_height - frame_minheight(frp, NULL));
+ frame_add_height(frp, -h);
+ old_p_ch += h;
+ frp = frp->fr_prev;
+ }
+ if (p_ch < old_p_ch && command_frame_height && frp != NULL) {
+ frame_add_height(frp, (int)(old_p_ch - p_ch));
}
- frame_add_height(frp, (int)(old_p_ch - p_ch));
// Recompute window positions.
- if (frp != lastwin->w_frame) {
- win_comp_pos();
+ win_comp_pos();
+ cmdline_row = Rows - (int)p_ch;
+ redraw_cmdline = true;
+
+ // Clear the cmdheight area.
+ if (msg_scrolled == 0 && full_screen) {
+ ScreenGrid *grid = &default_grid;
+ if (!ui_has(kUIMessages)) {
+ msg_grid_validate();
+ grid = &msg_grid_adj;
+ }
+ grid_clear(grid, cmdline_row, Rows, 0, Columns, 0);
+ msg_row = cmdline_row;
}
+
+ // Use the value of p_ch that we remembered. This is needed for when the
+ // GUI starts up, we can't be sure in what order things happen. And when
+ // p_ch was changed in another tab page.
+ curtab->tp_ch_used = p_ch;
+ min_set_ch = p_ch;
}
// Resize frame "frp" to be "n" lines higher (negative for less high).
// Also resize the frames it is contained in.
static void frame_add_height(frame_T *frp, int n)
{
- frame_new_height(frp, frp->fr_height + n, false, false);
+ frame_new_height(frp, frp->fr_height + n, false, false, false);
while (true) {
frp = frp->fr_parent;
if (frp == NULL) {
@@ -6858,6 +6811,7 @@ void last_status(bool morewin)
{
// Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, last_stl_height(morewin) > 0, global_stl_height() > 0);
+ win_float_anchor_laststatus();
}
// Remove status line from window, replacing it with a horizontal separator if needed.
@@ -6910,7 +6864,7 @@ static bool resize_frame_for_status(frame_T *fr)
emsg(_(e_noroom));
return false;
} else if (fp != fr) {
- frame_new_height(fp, fp->fr_height - 1, false, false);
+ frame_new_height(fp, fp->fr_height - 1, false, false, false);
frame_fix_height(wp);
win_comp_pos();
} else {
@@ -6931,7 +6885,7 @@ static bool resize_frame_for_winbar(frame_T *fr)
emsg(_(e_noroom));
return false;
}
- frame_new_height(fp, fp->fr_height - 1, false, false);
+ frame_new_height(fp, fp->fr_height - 1, false, false, false);
win_new_height(wp, wp->w_height + 1);
frame_fix_height(wp);
win_comp_pos();
@@ -7065,8 +7019,24 @@ int last_stl_height(bool morewin)
}
/// Return the minimal number of rows that is needed on the screen to display
-/// the current number of windows.
-int min_rows(void)
+/// the current number of windows for the given tab page.
+int min_rows(tabpage_T *tp) FUNC_ATTR_NONNULL_ALL
+{
+ if (firstwin == NULL) { // not initialized yet
+ return MIN_LINES;
+ }
+
+ int total = frame_minheight(tp->tp_topframe, NULL);
+ total += tabline_height() + global_stl_height();
+ if ((tp == curtab ? p_ch : tp->tp_ch_used) > 0) {
+ total++; // count the room for the command line
+ }
+ return total;
+}
+
+/// Return the minimal number of rows that is needed on the screen to display
+/// the current number of windows for all tab pages.
+int min_rows_for_all_tabpages(void)
{
if (firstwin == NULL) { // not initialized yet
return MIN_LINES;
@@ -7075,12 +7045,12 @@ int min_rows(void)
int total = 0;
FOR_ALL_TABS(tp) {
int n = frame_minheight(tp->tp_topframe, NULL);
+ if ((tp == curtab ? p_ch : tp->tp_ch_used) > 0) {
+ n++; // count the room for the command line
+ }
total = MAX(total, n);
}
total += tabline_height() + global_stl_height();
- if (p_ch > 0) {
- total += 1; // count the room for the command line
- }
return total;
}
@@ -7304,7 +7274,7 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr)
fr->fr_height = sn->fr_height;
fr->fr_width = sn->fr_width;
if (fr->fr_layout == FR_LEAF) {
- frame_new_height(fr, fr->fr_height, false, false);
+ frame_new_height(fr, fr->fr_height, false, false, false);
frame_new_width(fr, fr->fr_width, false, false);
wp = sn->fr_win;
}
diff --git a/src/nvim/window.h b/src/nvim/window.h
index 9618ff1c2a..b5808d3451 100644
--- a/src/nvim/window.h
+++ b/src/nvim/window.h
@@ -35,9 +35,6 @@ enum {
EXTERN int tabpage_move_disallowed INIT( = 0); ///< moving tabpages around disallowed
-/// Set to true if 'cmdheight' was explicitly set to 0.
-EXTERN bool p_ch_was_zero INIT( = false);
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "window.h.generated.h"
#endif
diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c
index b8a51d686d..d11b965dfc 100644
--- a/src/nvim/winfloat.c
+++ b/src/nvim/winfloat.c
@@ -10,7 +10,6 @@
#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
#include "nvim/buffer_defs.h"
-#include "nvim/decoration.h"
#include "nvim/drawscreen.h"
#include "nvim/errors.h"
#include "nvim/globals.h"
@@ -308,6 +307,15 @@ void win_check_anchored_floats(win_T *win)
}
}
+void win_float_anchor_laststatus(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (win->w_config.relative == kFloatRelativeLaststatus) {
+ win->w_pos_changed = true;
+ }
+ }
+}
+
void win_reconfig_floats(void)
{
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {